Skip to content

Commit

Permalink
Merge pull request #14 from kuukuuuruyuk/develop
Browse files Browse the repository at this point in the history
Develop modif besar, banyak fix
  • Loading branch information
kuukuuuruyuk authored Oct 1, 2022
2 parents 2631340 + e0bf799 commit 62efc13
Show file tree
Hide file tree
Showing 55 changed files with 1,034 additions and 444 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ RABBITMQ_SERVER=
UPLOADS_DIRECTORY=

MAIL_HOST=
MAil_PORT=
MAIL_PORT=
MAIL_ADDRESS=
MAIL_PASSWORD=

Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,3 @@ node_modules/
package-lock.json

/.vscode

consumer/
57 changes: 57 additions & 0 deletions consumer/consume.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
require('dotenv').config();

const {Pool} = require('pg');
const amqp = require('amqplib');
const redis = require('redis');
const nodemailer = require('nodemailer');
// Service
const {PlayistsService} = require('./src/playists-service');
const {MailSender} = require('./src/mail-sender');
const {Listener} = require('./src/listener');
const {CacheControl} = require('./src/cache/cache-control');
const {InvariantError} = require('./src/exception/invariant-error');

const initConsumer = async () => {
const dbPool = new Pool();
const redisConfig = () => {
const client = redis.createClient({
socket: {
host: process.env.REDIS_SERVER_HOST,
},
});

client.on('error', (error) => {
throw new InvariantError(error);
});

client.connect();

return client;
};
const cacheControl = new CacheControl({redis: redisConfig()});
const playistsService = new PlayistsService(dbPool, {cacheControl});
const mailSender = new MailSender({
transporter: nodemailer.createTransport({
host: process.env.MAIL_HOST,
port: process.env.MAIL_PORT,
auth: {
user: process.env.MAIL_ADDRESS,
pass: process.env.MAIL_PASSWORD,
},
}),
});
const listener = new Listener({playistsService, mailSender});

const initRabbitMQ = async () => {
const connection = await amqp.connect(process.env.RABBITMQ_SERVER);
const channel = await connection.createChannel();

await channel.assertQueue('export:playlists', {durable: true});

channel.consume('export:playlists', listener.eventListener, {noAck: true});
};

initRabbitMQ();
};

initConsumer();
48 changes: 48 additions & 0 deletions consumer/src/cache/cache-control.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Redis cache control
*/
class CacheControl {
/**
* Cache redis control
*
* @param {any} service Cache service
*/
constructor(service) {
this._client = service.redis;
}

/**
* Set redis cache
*
* @param {string} key Key cache
* @param {string} value Value cache
* @param {number} expInSec Expire
*/
async set(key, value, expInSec = 900) {
await this._client.set(key, value, {
EX: expInSec,
});
}

/**
* Get redis cache by key
*
* @param {string} key Cache key
*/
async get(key) {
const result = await this._client.get(key);
if (!result) throw new Error('Cache not found');
}

/**
* Hapus cache data
*
* @param {string} key Cache redis key
* @return {any} Cache data
*/
del(key) {
return this._client.del(key);
}
}

module.exports = {CacheControl};
18 changes: 18 additions & 0 deletions consumer/src/exception/client-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Client exception
*/
class ClientError extends Error {
/**
* Client error
*
* @param {string} message Error message
* @param {number} statusCode Error code
*/
constructor(message, statusCode = 400) {
super(message);
this.name = 'Client Error';
this.statusCode = statusCode;
}
}

module.exports = {ClientError};
18 changes: 18 additions & 0 deletions consumer/src/exception/invariant-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const {ClientError} = require('./client-error');

/**
* Invariant exception
*/
class InvariantError extends ClientError {
/**
* Invariant error
*
* @param {string} message Error message
*/
constructor(message) {
super(message);
this.name = 'Invariant Error';
}
}

module.exports = {InvariantError};
18 changes: 18 additions & 0 deletions consumer/src/exception/not-found-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const {ClientError} = require('./client-error');

/**
* Not found exception
*/
class NotFoundError extends ClientError {
/**
* Not founde error
*
* @param {string} message Error message
*/
constructor(message) {
super(message, 404);
this.name = 'Not Found Error';
}
}

module.exports = {NotFoundError};
40 changes: 40 additions & 0 deletions consumer/src/listener.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Listener
*/
class Listener {
/**
* Listener
*
* @param {any} service Playlist servis
*/
constructor(service) {
this._playistsService = service.playistsService;
this._mailSender = service.mailSender;

this.eventListener = this.eventListener.bind(this);
}

/**
* Evant listener
*
* @param {string} message Message content
*/
async eventListener(message) {
try {
const {playlistId, targetEmail} = JSON.parse(message.content.toString());
const songs =
await this._playistsService.getSongsFromPlaylistId(playlistId);
const playlist = await this._playistsService.getPlaylistById(playlistId);
await this._mailSender.sendEmail(targetEmail, JSON.stringify({
playlist: {
...playlist,
songs,
},
}));
} catch (error) {
console.error(error);
}
}
}

module.exports = {Listener};
39 changes: 39 additions & 0 deletions consumer/src/mail-sender.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Mail sender
*/
class MailSender {
/**
* Mail sender
*
* @param {any} service Service mail sender
*/
constructor(service) {
this._transporter = service.transporter;
}

/**
* send email
*
* @param {string} targetEmail Email target
* @param {any} content Emai content
* @return {any} Info sender
*/
async sendEmail(targetEmail, content) {
const message = {
from: 'Openmusic API',
to: targetEmail,
subject: 'Ekspor Playlists',
text: 'Export Playlists Data',
attachments: [
{
filename: 'playlists.json',
content,
},
],
};

return await this._transporter.sendMail(message);
}
}

module.exports = {MailSender};
63 changes: 63 additions & 0 deletions consumer/src/playists-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const {NotFoundError} = require('./exception/not-found-error');

/**
* Playlist service
*/
class PlayistsService {
/**
* Playlist Service
*
* @param {any} db Database pool
* @param {any} service Playlist service
*/
constructor(db, service) {
this._pool = db;
this._cacheControl = service.cacheControl;
}

/**
* Get playlist data
*
* @param {string} playlistId Playlist id
* @return {any} Playlist model
*/
async getPlaylistById(playlistId) {
const sql = 'SELECT id, name FROM playlists WHERE id=$1';
const result = await this._pool.query(sql, [playlistId]);

if (!result.rowCount) {
throw new NotFoundError('Failed, playlist ID not found!');
}

return result.rows[0];
}

/**
* Find all playlist
*
* @param {string} playlistId Playlist id
* @return {any} Playlist model
*/
async getSongsFromPlaylistId(playlistId) {
try {
const result = await this._cacheControl.get(`playlist:${playlistId}`);

return JSON.parse(result);
} catch {
const sql = [
'SELECT songs.id, songs.title, songs.performer',
'FROM songs',
'LEFT JOIN playlist_songs ON songs.id = playlist_songs.song_id',
'WHERE playlist_songs.playlist_id=$1',
].join(' ');
const result = await this._pool.query(sql, [playlistId]);
const parseToJSON = JSON.stringify(result.rows);

await this._cacheControl.set(`playlist:${playlistId}`, parseToJSON);

return result.rows;
}
}
}

module.exports = {PlayistsService};
1 change: 0 additions & 1 deletion migrations/1663392988516_authentications.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ exports.up = (pgm) => {
},
refresh_token: {
type: 'text',
notNull: false,
},
user_id: {
type: 'text',
Expand Down
4 changes: 2 additions & 2 deletions migrations/1664006908353_add-cover-to-albums.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-disable camelcase */

exports.up = (pgm) => {
pgm.addColumn('albums', {
pgm.addColumns('albums', {
cover: {type: 'text'},
});
};

exports.down = (pgm) => {
pgm.dropColumn('albums', 'cover');
pgm.dropColumns('albums', 'cover');
};
18 changes: 18 additions & 0 deletions migrations/1664446606892_add-created-at-updated-at-to-songs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* eslint-disable camelcase */

exports.up = (pgm) => {
pgm.addColumns('songs', {
created_at: {
type: 'timestamp',
notNull: true,
default: pgm.func('current_timestamp'),
},
updated_at: {
type: 'timestamp',
},
});
};

exports.down = (pgm) => {
pgm.dropColumns('songs', ['created_at', 'updated_at']);
};
18 changes: 18 additions & 0 deletions migrations/1664447037930_add-created-at-updated-at-to-albums.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* eslint-disable camelcase */

exports.up = (pgm) => {
pgm.addColumns('albums', {
created_at: {
type: 'timestamp',
notNull: true,
default: pgm.func('current_timestamp'),
},
updated_at: {
type: 'timestamp',
},
});
};

exports.down = (pgm) => {
pgm.dropColumns('songs', ['created_at', 'updated_at']);
};
Loading

0 comments on commit 62efc13

Please sign in to comment.