Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ jobs:

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

- name: Run Tests (API)
Expand Down
5 changes: 5 additions & 0 deletions api/.env-dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CACHE_PERSISTENCE= memory
MACHINE_ID=1
PORT = 3000
# Perhpas we should only use a domain name here instead of a full URL (with the port as it is error prone)
SHORTENED_BASE_URL = http://localhost:3000
34 changes: 34 additions & 0 deletions api/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import js from '@eslint/js';
import tseslint from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import prettier from 'eslint-plugin-prettier'; // ✅ Explicitly import the plugin
import prettierConfig from 'eslint-config-prettier';

export default [
js.configs.recommended,
{
files: ['src/**/*.ts', 'test/**/*.ts'],
languageOptions: {
parser: tsParser,
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: import.meta.dirname,
sourceType: 'module',
},
},
plugins: {
'@typescript-eslint': tseslint,
prettier, // ✅ Register Prettier plugin
},
rules: {
...tseslint.configs.recommended.rules,
...prettierConfig.rules, // ✅ Apply Prettier rules
'prettier/prettier': 'error', // ✅ Ensure Prettier rules are enforced
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
ignores: ['**/*.config.js', '**/node_modules/**'],
},
];
21 changes: 14 additions & 7 deletions api/src/infrastructure/infrastructure.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { InMemoryUrlRepository } from '../infrastructure/repository/in-memory-url.repository';
import { Module, DynamicModule } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { ShortenUrlRepository } from '../shorten-url/shorten-url.repository';
import { RepositoryProvider } from './repository/repository.provider';

@Module({
imports: [ConfigModule.forRoot()], // Load environment variables
controllers: [],
providers: [InMemoryUrlRepository],
imports: [ConfigModule], // ✅ Ensure ConfigModule is loaded
})
export class InfrastructureModule {}
export class InfrastructureModule {
static register(): DynamicModule {
return {
module: InfrastructureModule,
providers: [RepositoryProvider],
exports: [ShortenUrlRepository], // ✅ Export repository so other modules can use it
};
}
}
17 changes: 17 additions & 0 deletions api/src/infrastructure/repository/redis-url.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ShortenUrlRepository } from '../../shorten-url/shorten-url.repository';
import { Injectable } from '@nestjs/common';

@Injectable()
export class RedisUrlRepository implements ShortenUrlRepository {
private urls: Map<string, string> = new Map();

create(url: string, shortenedUrl: string): Promise<void> {
this.urls.set(url, shortenedUrl);
return Promise.resolve(undefined);
}

findURL(url: string): Promise<string | null> {
const shortenedUrl = this.urls.get(url);
return Promise.resolve(shortenedUrl);
}
}
26 changes: 26 additions & 0 deletions api/src/infrastructure/repository/repository.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ConfigService } from '@nestjs/config';
import { Provider } from '@nestjs/common';
import { InMemoryUrlRepository } from './in-memory-url.repository';
import { ShortenUrlRepository } from '../../shorten-url/shorten-url.repository';

/**
* Factory provider to select the appropriate repository implementation based on configuration.
*/
export const RepositoryProvider: Provider = {
// TODO: Use a constant for the key
//provide: 'ShortenUrlRepository',
provide: ShortenUrlRepository,
useFactory: (configService: ConfigService) => {
const persistenceType = configService.get<string>(
'CACHE_PERSISTENCE',
'memory',
);

if (persistenceType === 'redis') {
throw new Error('Redis persistence is not yet implemented');
}

return new InMemoryUrlRepository();
},
inject: [ConfigService], // ✅ Ensure ConfigService is available
};
12 changes: 9 additions & 3 deletions api/src/shorten-url/shorten-url.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ import { ConfigModule } from '@nestjs/config';
import { ShortenUrlController } from './shorten-url.controller';
import { ShortenUrlUsecase } from './shorten-url.usecase';
import { ShortenUrlIdGeneratorService } from './shorten-url.id-generator.service';
import { InMemoryUrlRepository } from '../infrastructure/repository/in-memory-url.repository';
import { InfrastructureModule } from '../infrastructure/infrastructure.module';
import { ShortenUrlRepository } from './shorten-url.repository';

@Module({
imports: [ConfigModule.forRoot(), InfrastructureModule], // Load environment variables
imports: [
ConfigModule, // ✅ Ensure ConfigModule is available
InfrastructureModule.register(), // ✅ Register InfrastructureModule dynamically
],
controllers: [ShortenUrlController],
providers: [
{ provide: 'ShortenUrlRepository', useClass: InMemoryUrlRepository },
{
provide: 'ShortenUrlRepository',
useExisting: ShortenUrlRepository, // ✅ Use injected provider
},
ShortenUrlIdGeneratorService,
ShortenUrlUsecase,
],
Expand Down
6 changes: 3 additions & 3 deletions api/src/shorten-url/shorten-url.repository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export interface ShortenUrlRepository {
findURL(url: string): Promise<string | null>;
export abstract class ShortenUrlRepository {
abstract findURL(url: string): Promise<string | null>;

create(url: string, shortenedUrl: string): Promise<void>;
abstract create(url: string, shortenedUrl: string): Promise<void>;
}
Loading