Skip to content
This repository has been archived by the owner on Apr 20, 2024. It is now read-only.

Commit

Permalink
feat: Basic music player (#2)
Browse files Browse the repository at this point in the history
* feat: music player widget

* chore: correct naming convetion

* chore: clean up

* feat(ui): music player pause and play

* feat(ui): design system & colorscheme

* chore: clean up

* feat(ui): music player

* feat: setup prisma and database

* add: createdAt and updateAt for collections

* feat(dev): swagger, cors, hemlet, config

* fix(prisma): custom prisma client

* fix: PrismaCustomClient injection

* fix: versioning correction

* chore: lint

* chore: lint

* feat(module/music): CRUD with basic api documentation

* feat: utils for logging

* fix: api URI naming

* fix(db/music): optional attribute

* add(config): default storage directory for music upload destinationn

* add(utils): audio file filteration for upload validation

* add(utils): music file generator

* feat(db): unique filename for uploaded music file

* add(package): mutler and express for file upload

* feat(module/music): music file upload while creating music

* chore: changing prisma output directory

* chore: prisma library import path for new client output directory

Related commits:
- 472612f

* add: necessary packages for file upload

* chore: ignore prisma client generation

* chore: lint & cleanup

* fix(db|tmp): CustomPrismaClient issue

Incorrect temptation to import the default client instead of utilizing the provided custom client.
**This solution is temporary!** Until nestjs-prisma new release.

Related Links to the issue:
- notiz-dev/nestjs-prisma#79

* fix(module/music): invalid request - No file is provided

* fix(module/music): create upload folder if it doesn't exist in the current container system

* fix(module/music): file filteration and standard response

* fix(module/music): parse the complex query request

* refactor(module/music): query input validation and parsing

* fix(module/music): using same file name

- The controller create another filename instead of reusing what mutler provided

* feat(module/music): stream music file

* test(e2e): music creation

* fix(test): api hello world

* fix(test): server config

* chore: lint

* chore: add form data

* chore: clean up

* refactor(module/music): clean response and debug message

* refactor(test):  music creation

- Utils for file stream creation
- Central mock data collection
- Renamed test

* chore: gitignore update

* feat: utils for api request in frontend

* feat: music type for store and api

* feat(music-player): action hook

* feat(music-player): redux store for music player

* feat(music-player): refactor the complicated div design to slider

* feat(music-player): next and previous button

* feat(music-player): redux store instead of state

* feat(music-player): next and previous redux action creator

* refactor(module/music): e2e

* refactor(module/music): e2e

* refactor(api): use supertest for e2e

* lint

* add: docker compose
  • Loading branch information
Krr0ptioN committed Dec 2, 2023
1 parent 49c7108 commit 0d18553
Show file tree
Hide file tree
Showing 97 changed files with 1,825 additions and 47 deletions.
16 changes: 16 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
MONGO_INITDB_ROOT_USERNAME=your_mongo_username
MONGO_INITDB_ROOT_PASSWORD=your_mongo_password
MONGO_INITDB_DATABASE=your_database_name

PORT_API=3000
HOST_API=localhost
HELMET_ENABLED=
SWAGGER_ENABLED=
SWAGGER_TITLE=
SWAGGER_DESC=
API_VERSION=
SWAGGER_PATH=
SWAGGER_USER=
SWAGGER_PASSWORD=
SWAGGER_ENV=
CORS_ENABLED=
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ Thumbs.db
.nx/cache
pnpm-lock.yaml
package-lock.json

data-access/client
.env
Binary file added api-e2e/assets/audio-file.mp3
Binary file not shown.
Binary file added api-e2e/assets/non-audio-file.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 14 additions & 5 deletions api-e2e/project.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
{
"name": "api-e2e",
"$schema": "../node_modules/nx/schemas/project-schema.json",
"implicitDependencies": ["api"],
"implicitDependencies": [
"api"
],
"projectType": "application",
"targets": {
"e2e": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{e2eProjectRoot}"],
"outputs": [
"{workspaceRoot}/coverage/{e2eProjectRoot}"
],
"options": {
"jestConfig": "api-e2e/jest.config.ts",
"passWithNoTests": true
"passWithNoTests": true,
"detectOpenHandle": true
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"],
"outputs": [
"{options.outputFile}"
],
"options": {
"lintFilePatterns": ["api-e2e/**/*.{js,ts}"]
"lintFilePatterns": [
"api-e2e/**/*.{js,ts}"
]
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions api-e2e/src/api/api.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import axios from 'axios';

describe('GET /api', () => {
describe('GET /api/v1', () => {
it('should return a message', async () => {
const res = await axios.get(`/api`);
const res = await axios.get(`/api/v1`);

expect(res.status).toBe(200);
expect(res.data).toEqual({ message: 'Hello API' });
Expand Down
14 changes: 14 additions & 0 deletions api-e2e/src/mock-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export let MockData = {
musicCreation: {
invalidMusicFile: {
filepath: 'api-e2e/assets/non-audio-file.png',
name: 'A music with invalid file format',
id: 'unset',
},
validMusicFile: {
filepath: 'api-e2e/assets/audio-file.mp3',
name: 'A music with valid file format',
id: 'unset',
},
},
};
52 changes: 52 additions & 0 deletions api-e2e/src/music/music.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import axios from 'axios';
import { AppModule } from '@musica/api';
import { MockData } from '../mock-data';
import { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';

import * as request from 'supertest';

describe('Music Module', () => {
let app: INestApplication;

beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();

app = moduleRef.createNestApplication();
await app.init();
});

describe('POST /api/v1/music | Music creation', () => {
it('should throw error if file is not provided', async () => { });
it('should throw error if file is not an audio', async () => { });
it('should create music and upload file', async () => { });
});

describe('GET /api/v1/music/file/:id | Get music file', () => {
it('should find the music and get the file to play', async () => { });
});

describe('GET /api/v1/music | Get music file', () => {
it('should return all music records with default parameters', async () => { });
it('should return music records based on specific query parameters', async () => { });
it('should handle invalid JSON in query parameters gracefully', async () => { });
});

describe('GET /api/v1/music/:id | Get music file', () => {
it('should retrieve a music by ID (findOne)', async () => { });
});

describe('PATCH /api/v1/music/:id | Get music file', () => {
it('should update a music by ID (update)', async () => { });
});

describe('DELETE /api/v1/music/:id | Get music file', () => {
it('should remove a music by ID (remove)', async () => { });
});

afterAll(async () => {
await app.close();
});
});
6 changes: 3 additions & 3 deletions api-e2e/src/support/test-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import axios from 'axios';

module.exports = async function () {
module.exports = async function() {
// Configure axios for tests to use.
const host = process.env.HOST ?? 'localhost';
const port = process.env.PORT ?? '3000';
const host = process.env.HOST_API ?? 'localhost';
const port = process.env.PORT_API ?? '3000';
axios.defaults.baseURL = `http://${host}:${port}`;
};
8 changes: 8 additions & 0 deletions api-e2e/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import path from 'path';
import fs from 'fs';

export const fileStreamByPath = (filePath: string) => {
const cwd = process.cwd();
const absolutePathToFile = path.resolve(cwd, filePath);
return fs.createReadStream(absolutePathToFile);
};
2 changes: 1 addition & 1 deletion api/src/app/app.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { AppService } from './app.service';

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
constructor(private readonly appService: AppService) { }

@Get()
getData() {
Expand Down
16 changes: 14 additions & 2 deletions api/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { Module } from '@nestjs/common';

import { CustomPrismaModule } from 'nestjs-prisma/dist/custom';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MusicModule } from '../music/music.module';
import { PrismaClient } from '@musica/data-access/client';
import { ConfigModule } from '@nestjs/config';

@Module({
imports: [],
imports: [
CustomPrismaModule.forRoot({
name: 'DataAccessService',
client: new PrismaClient(),
isGlobal: true,
}),
MusicModule,
ConfigModule.forRoot({ isGlobal: true, cache: true }),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
export class AppModule { }
6 changes: 6 additions & 0 deletions api/src/configs/admin.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface AdminConfig {
id: number;
username: string;
email: string;
password: string;
}
3 changes: 3 additions & 0 deletions api/src/configs/backend.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface BackendConfig {
port: number;
}
17 changes: 17 additions & 0 deletions api/src/configs/config.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { BackendConfig } from './backend.interface';
import { CorsConfig } from './cors.interface';
import { SwaggerConfig } from './swagger.interface';
import { SecurityConfig } from './security.interface';
import { HelmetConfig } from './helmet.interface';
import { AdminConfig } from './admin.interface';
import { StorageConfig } from './storage.interface';

export interface Config {
backend: BackendConfig;
cors: CorsConfig;
swagger: SwaggerConfig;
security: SecurityConfig;
helmet: HelmetConfig;
admin: AdminConfig;
storage: StorageConfig;
}
40 changes: 40 additions & 0 deletions api/src/configs/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Config } from './config.interface';

const config: Config = {
backend: {
port: 3000,
},
helmet: {
enabled: true,
},
cors: {
enabled: true,
},
swagger: {
enabled: true,
title: 'Musica API ',
description: 'Swagger API Documentation server',
version: '1',
path: 'swg',
user: 'admin',
password: '4#2M0!s0D1N#2398@M1N233l',
env: ['local', 'staging', 'development'],
},
security: {
expiresIn: '5m',
refreshIn: '7d',
bcryptSaltOrRound: 10,
passwordResetTokenExpiresIn: '5m',
},
admin: {
username: 'admin',
email: '[email protected]',
password: '4#2M0!s0D1N#2398@M1N233l',
id: 0,
},
storage: {
musicStorageDest: '/tmp/musica/musics',
},
};

export default config;
3 changes: 3 additions & 0 deletions api/src/configs/cors.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface CorsConfig {
enabled: boolean;
}
3 changes: 3 additions & 0 deletions api/src/configs/helmet.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface HelmetConfig {
enabled: boolean;
}
6 changes: 6 additions & 0 deletions api/src/configs/security.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface SecurityConfig {
expiresIn: string;
refreshIn: string;
bcryptSaltOrRound: string | number;
passwordResetTokenExpiresIn: string;
}
3 changes: 3 additions & 0 deletions api/src/configs/storage.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface StorageConfig {
musicStorageDest: string;
}
10 changes: 10 additions & 0 deletions api/src/configs/swagger.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface SwaggerConfig {
enabled: boolean;
title: string;
description: string;
version: string;
path: string;
password: string;
user: string;
env: string[];
}
Loading

0 comments on commit 0d18553

Please sign in to comment.