Skip to content

Commit

Permalink
Merge pull request #30 from Mostaqem/dev
Browse files Browse the repository at this point in the history
Improve Reciters Search  #28
  • Loading branch information
the-sabra authored Jan 18, 2025
2 parents 811244f + f448e71 commit ed16f9b
Show file tree
Hide file tree
Showing 7 changed files with 1,337 additions and 1,138 deletions.
46 changes: 23 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,49 +23,49 @@
"typeorm:migrate": "npm run typeorm -- -d ./typeorm.config.ts migration:run"
},
"dependencies": {
"@nestjs/cache-manager": "^2.2.2",
"@nestjs/cli": "^10.4.4",
"@nestjs/common": "^10.4.0",
"@nestjs/config": "^3.2.3",
"@nestjs/core": "^10.4.0",
"@nestjs/mapped-types": "^2.0.5",
"@nestjs/platform-express": "^10.4.0",
"@nestjs/cache-manager": "^2.3.0",
"@nestjs/cli": "^10.4.9",
"@nestjs/common": "^10.4.15",
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.4.15",
"@nestjs/mapped-types": "^2.1.0",
"@nestjs/platform-express": "^10.4.15",
"@nestjs/typeorm": "^10.0.2",
"axios": "^1.7.3",
"axios": "^1.7.9",
"cache-manager": "^5.7.6",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"cookie-parser": "^1.4.6",
"dotenv": "^16.4.5",
"helmet": "^7.1.0",
"mysql2": "^3.11.0",
"cookie-parser": "^1.4.7",
"dotenv": "^16.4.7",
"helmet": "^7.2.0",
"mysql2": "^3.12.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"typeorm": "^0.3.20",
"unsplash-js": "^7.0.19"
},
"devDependencies": {
"@nestjs/schematics": "^10.1.3",
"@nestjs/testing": "^10.4.0",
"@types/cookie-parser": "^1.4.7",
"@nestjs/schematics": "^10.2.3",
"@nestjs/testing": "^10.4.15",
"@types/cookie-parser": "^1.4.8",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/node": "^20.14.15",
"@types/jest": "^29.5.14",
"@types/node": "^20.17.14",
"@types/supertest": "^6.0.2",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-prettier": "^5.2.2",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"prettier": "^3.4.2",
"source-map-support": "^0.5.21",
"supertest": "^6.3.4",
"ts-jest": "^29.2.4",
"ts-loader": "^9.5.1",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.2",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.5.4"
"typescript": "^5.7.3"
},
"jest": {
"moduleNameMapper": {
Expand Down
2,268 changes: 1,242 additions & 1,026 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

28 changes: 11 additions & 17 deletions src/audio/audio.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,26 +72,20 @@ describe('AudioController', () => {
surah_id: 1,
reciter_id: 1,
};
const expectedResult: Partial<TilawaSurah> & { reciter_id: number } = {
surah_id: 1,
const expectedResult = {
tilawa_id: 1,
url: 'https://example.com/audio.mp3',
tilawa: {
id: 1,
name: 'Test Tilawa',
name_english: 'Test Tilawa',
reciter_id: 1,
reciter: {},
tilawaSurah: [],
} as any,
surah: {} as any,
reciter_id: 1,
url: 'https://example',
surah: {
name_arabic: 'الفاتحة',
name_complex: 'Al-Fatiha',
},
reciter: {
name_arabic: 'عبدالرحمن السديس',
name_english: 'Abdur-Rahman as-Sudais',
},
};

jest.spyOn(service, 'getAudio').mockResolvedValue({
...expectedResult,
reciter_id: 1,
} as TilawaSurah & { reciter_id: number });
jest.spyOn(service, 'getAudio').mockResolvedValue(expectedResult as any);

const result = await controller.get(filterAudioDto);

Expand Down
75 changes: 18 additions & 57 deletions src/audio/audio.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ import { AudioService } from './audio.service';
import { TilawaSurah } from 'src/surah/entities/tilawa-surah.entity';
import { CreateAudioDto } from './dto/create-audio.dto';
import { FilterAudioDto } from './dto/filter-audio.dto';
import { NotFoundException } from '@nestjs/common';
import { ReciterService } from 'src/reciter/reciter.service';
import { FilterAudioLrcDto } from './dto/filter-lrc.dto';

describe('AudioService', () => {
let service: AudioService;
let tilawaSurahRepo: Repository<TilawaSurah>;
let reciterService: ReciterService;
// let reciterService: ReciterService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
Expand All @@ -35,7 +34,7 @@ describe('AudioService', () => {
tilawaSurahRepo = module.get<Repository<TilawaSurah>>(
getRepositoryToken(TilawaSurah),
);
reciterService = module.get<ReciterService>(ReciterService);
// reciterService = module.get<ReciterService>(ReciterService);
});

it('should be defined', () => {
Expand Down Expand Up @@ -84,68 +83,30 @@ describe('AudioService', () => {
const filterAudioDto: FilterAudioDto = {
surah_id: 1,
reciter_id: 1,
};
const expectedResult: Partial<TilawaSurah> & { reciter_id: number } = {
surah_id: 1,
tilawa_id: 1,
url: 'https://example.com/audio.mp3',
tilawa: {
id: 1,
name: 'Test Tilawa',
name_english: 'Test Tilawa',
reciter_id: 1,
reciter: {},
tilawaSurah: [],
} as any,
surah: {} as any,
reciter_id: 1,
};

jest.spyOn(reciterService, 'getReciterTilawa').mockResolvedValue([
const expectedResult = [
{
id: 1,
name: 'حفص عن عاصم - مرتل',
name_english: 'Hafs an Asim - Murattal',
reciter_id: 1,
reciter: {} as any,
tilawaSurah: [] as any[],
tilawa_id: 1,
url: 'https://example.com/audio.mp3',
surah: {
name_arabic: 'الفاتحة',
name_complex: 'Al-Fatihah',
},
reciter: {
name_arabic: 'عبد الباسط عبد الصمد',
name_english: 'Abdul Basit Abdul Samad',
},
},
]);
];

jest
.spyOn(tilawaSurahRepo, 'findOne')
.mockResolvedValue(expectedResult as TilawaSurah);
.spyOn(tilawaSurahRepo, 'find')
.mockResolvedValue(expectedResult as any);

const result = await service.getAudio(filterAudioDto);

expect(result).toEqual(expectedResult);
expect(tilawaSurahRepo.findOne).toHaveBeenCalledWith({
select: ['url', 'surah_id', 'tilawa_id'],
where: { surah_id: filterAudioDto.surah_id, tilawa_id: 1 },
});
});

it('should throw NotFoundException when audio is not found', async () => {
const filterAudioDto: FilterAudioDto = {
surah_id: 1,
reciter_id: 1,
};

jest.spyOn(reciterService, 'getReciterTilawa').mockResolvedValue([
{
id: 1,
name: 'حفص عن عاصم - مرتل',
name_english: 'Hafs an Asim - Murattal',
reciter_id: 1,
reciter: {} as any,
tilawaSurah: [] as any[],
},
]);

jest.spyOn(tilawaSurahRepo, 'findOne').mockResolvedValue(null);

await expect(service.getAudio(filterAudioDto)).rejects.toThrow(
NotFoundException,
);
expect(result).toEqual(expectedResult[0]);
});
});
describe('getAudioLrc', () => {
Expand Down
30 changes: 15 additions & 15 deletions src/audio/audio.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
ConflictException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { ConflictException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { TilawaSurah } from 'src/surah/entities/tilawa-surah.entity';
import { Repository } from 'typeorm';
Expand All @@ -25,9 +21,7 @@ export class AudioService {
return this.tilawaSurahRepo.save(audio);
}

async getAudio(
paginatedFilter: FilterAudioDto,
): Promise<TilawaSurah & { reciter_id: number }> {
async getAudio(paginatedFilter: FilterAudioDto) {
const { surah_id, reciter_id } = paginatedFilter;
let tilawa_id = paginatedFilter.tilawa_id;

Expand All @@ -39,15 +33,21 @@ export class AudioService {
tilawa_id = tilawa[0].id;
}

const audio = await this.tilawaSurahRepo.findOne({
select: ['url', 'surah_id', 'tilawa_id'],
where: { surah_id, tilawa_id },
const audio = await this.tilawaSurahRepo.find({
select: ['tilawa_id', 'url'],
where: {
surah_id,
tilawa_id,
},
relations: {
surah: true,
tilawa: {
reciter: true,
},
},
});

if (!audio) {
throw new NotFoundException('Audio not found');
}
return { ...audio, reciter_id };
return audio[0];
}

getAudioLrc(filterAudioLrcDto: FilterAudioLrcDto) {
Expand Down
5 changes: 5 additions & 0 deletions src/reciter/reciter.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export class ReciterController {
return this.reciterService.findAll(lang, reciterFilterDto);
}

@Get('/search')
searchReciter(@Query('name') name: string) {
return this.reciterService.searchReciter(name);
}

@Post('/:id/tilawa')
addReciterTilawa(
@Param('id') id: number,
Expand Down
23 changes: 23 additions & 0 deletions src/reciter/reciter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,27 @@ export class ReciterService {
});
return this.tilawaRepository.save(tilawa);
}

async searchReciter(name: string) {
const reciter = await this.reciterRepository.find();

// escape regex special characters in the search query

const escapedName = name
? name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
: undefined;

const filteredReciter = name
? reciter.filter(
(reciter) =>
reciter.name_arabic.match(new RegExp(escapedName, 'i')) ||
reciter.name_english.match(new RegExp(escapedName, 'i')),
)
: reciter;

return {
reciters: filteredReciter,
total: filteredReciter.length,
};
}
}

0 comments on commit ed16f9b

Please sign in to comment.