Skip to content

Commit dcc0750

Browse files
authored
Use an in-memory repository to save shortened url (#5)
1 parent dadfc56 commit dcc0750

File tree

8 files changed

+109
-16
lines changed

8 files changed

+109
-16
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ jobs:
3737

3838
- name: Create .env file
3939
run: |
40-
echo "MACHINE_ID=1" > .env
41-
echo "PORT=3000" >> .env
42-
echo "SHORTENED_BASE_URL=http://localhost:3000" >> .env
40+
cp .env-dev .env
4341
working-directory: api # Ensure it is created inside `api/`
4442

4543
- name: Run Tests (API)

api/.env-dev

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CACHE_PERSISTENCE= memory
2+
MACHINE_ID=1
3+
PORT = 3000
4+
# Perhpas we should only use a domain name here instead of a full URL (with the port as it is error prone)
5+
SHORTENED_BASE_URL = http://localhost:3000

api/eslint.config.mjs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import js from '@eslint/js';
2+
import tseslint from '@typescript-eslint/eslint-plugin';
3+
import tsParser from '@typescript-eslint/parser';
4+
import prettier from 'eslint-plugin-prettier'; // ✅ Explicitly import the plugin
5+
import prettierConfig from 'eslint-config-prettier';
6+
7+
export default [
8+
js.configs.recommended,
9+
{
10+
files: ['src/**/*.ts', 'test/**/*.ts'],
11+
languageOptions: {
12+
parser: tsParser,
13+
parserOptions: {
14+
project: './tsconfig.json',
15+
tsconfigRootDir: import.meta.dirname,
16+
sourceType: 'module',
17+
},
18+
},
19+
plugins: {
20+
'@typescript-eslint': tseslint,
21+
prettier, // ✅ Register Prettier plugin
22+
},
23+
rules: {
24+
...tseslint.configs.recommended.rules,
25+
...prettierConfig.rules, // ✅ Apply Prettier rules
26+
'prettier/prettier': 'error', // ✅ Ensure Prettier rules are enforced
27+
'@typescript-eslint/interface-name-prefix': 'off',
28+
'@typescript-eslint/explicit-function-return-type': 'off',
29+
'@typescript-eslint/explicit-module-boundary-types': 'off',
30+
'@typescript-eslint/no-explicit-any': 'off',
31+
},
32+
ignores: ['**/*.config.js', '**/node_modules/**'],
33+
},
34+
];
Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
import { Module } from '@nestjs/common';
2-
import { ConfigModule } from '@nestjs/config';
3-
import { InMemoryUrlRepository } from '../infrastructure/repository/in-memory-url.repository';
1+
import { Module, DynamicModule } from '@nestjs/common';
2+
import { ConfigModule, ConfigService } from '@nestjs/config';
3+
import { ShortenUrlRepository } from '../shorten-url/shorten-url.repository';
4+
import { RepositoryProvider } from './repository/repository.provider';
45

56
@Module({
6-
imports: [ConfigModule.forRoot()], // Load environment variables
7-
controllers: [],
8-
providers: [InMemoryUrlRepository],
7+
imports: [ConfigModule], // ✅ Ensure ConfigModule is loaded
98
})
10-
export class InfrastructureModule {}
9+
export class InfrastructureModule {
10+
static register(): DynamicModule {
11+
return {
12+
module: InfrastructureModule,
13+
providers: [RepositoryProvider],
14+
exports: [ShortenUrlRepository], // ✅ Export repository so other modules can use it
15+
};
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ShortenUrlRepository } from '../../shorten-url/shorten-url.repository';
2+
import { Injectable } from '@nestjs/common';
3+
4+
@Injectable()
5+
export class RedisUrlRepository implements ShortenUrlRepository {
6+
private urls: Map<string, string> = new Map();
7+
8+
create(url: string, shortenedUrl: string): Promise<void> {
9+
this.urls.set(url, shortenedUrl);
10+
return Promise.resolve(undefined);
11+
}
12+
13+
findURL(url: string): Promise<string | null> {
14+
const shortenedUrl = this.urls.get(url);
15+
return Promise.resolve(shortenedUrl);
16+
}
17+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ConfigService } from '@nestjs/config';
2+
import { Provider } from '@nestjs/common';
3+
import { InMemoryUrlRepository } from './in-memory-url.repository';
4+
import { ShortenUrlRepository } from '../../shorten-url/shorten-url.repository';
5+
6+
/**
7+
* Factory provider to select the appropriate repository implementation based on configuration.
8+
*/
9+
export const RepositoryProvider: Provider = {
10+
// TODO: Use a constant for the key
11+
//provide: 'ShortenUrlRepository',
12+
provide: ShortenUrlRepository,
13+
useFactory: (configService: ConfigService) => {
14+
const persistenceType = configService.get<string>(
15+
'CACHE_PERSISTENCE',
16+
'memory',
17+
);
18+
19+
if (persistenceType === 'redis') {
20+
throw new Error('Redis persistence is not yet implemented');
21+
}
22+
23+
return new InMemoryUrlRepository();
24+
},
25+
inject: [ConfigService], // ✅ Ensure ConfigService is available
26+
};

api/src/shorten-url/shorten-url.module.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ import { ConfigModule } from '@nestjs/config';
33
import { ShortenUrlController } from './shorten-url.controller';
44
import { ShortenUrlUsecase } from './shorten-url.usecase';
55
import { ShortenUrlIdGeneratorService } from './shorten-url.id-generator.service';
6-
import { InMemoryUrlRepository } from '../infrastructure/repository/in-memory-url.repository';
76
import { InfrastructureModule } from '../infrastructure/infrastructure.module';
7+
import { ShortenUrlRepository } from './shorten-url.repository';
88

99
@Module({
10-
imports: [ConfigModule.forRoot(), InfrastructureModule], // Load environment variables
10+
imports: [
11+
ConfigModule, // ✅ Ensure ConfigModule is available
12+
InfrastructureModule.register(), // ✅ Register InfrastructureModule dynamically
13+
],
1114
controllers: [ShortenUrlController],
1215
providers: [
13-
{ provide: 'ShortenUrlRepository', useClass: InMemoryUrlRepository },
16+
{
17+
provide: 'ShortenUrlRepository',
18+
useExisting: ShortenUrlRepository, // ✅ Use injected provider
19+
},
1420
ShortenUrlIdGeneratorService,
1521
ShortenUrlUsecase,
1622
],
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
export interface ShortenUrlRepository {
2-
findURL(url: string): Promise<string | null>;
1+
export abstract class ShortenUrlRepository {
2+
abstract findURL(url: string): Promise<string | null>;
33

4-
create(url: string, shortenedUrl: string): Promise<void>;
4+
abstract create(url: string, shortenedUrl: string): Promise<void>;
55
}

0 commit comments

Comments
 (0)