Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/backend e2e tests #3380

Merged
merged 2 commits into from
Oct 23, 2024
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
17 changes: 15 additions & 2 deletions backend/.env.default
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
#Identifiant du Google spreadsheet utilisé pour le calcul de la trajectoire.
TRAJECTOIRE_SNBC_SHEET_ID=

# Identifiant du Xlsx original utilisé pour le calcul de la trajectoire (stocké sur le drive)
TRAJECTOIRE_SNBC_XLSX_ID=

# Identifiant du dossier Google Drive dans lequel les spreadsheets des trajectoires calculées doivent être sauvés (un fichier par EPCI).
# Il existe un dossier par environnement (dev, preprod, prod).
TRAJECTOIRE_SNBC_RESULT_FOLDER_ID=

# Contenu au format json du fichier de clé de compte de service permettant l'utilisation des api Google Drive et Google Spreadsheet
GCLOUD_SERVICE_ACCOUNT_KEY=
SUPABASE_DATABASE_URL=postgresql://postgres:postgres@localhost:54322/postgres

# Clé de signature des tokens jwt. Utilisé pour vérifier la signature des tokens.
SUPABASE_JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long

# Supabase database connection credentials
SUPABASE_DATABASE_URL=postgresql://postgres:postgres@localhost:54322/postgres
SUPABASE_URL=http://127.0.0.1:54321
SUPABASE_SERVICE_ROLE_KEY=
SUPABASE_SERVICE_ROLE_KEY=
SUPABASE_ANON_KEY=
44 changes: 35 additions & 9 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@ Backend de territoires en transitions basé sur [Nest](https://github.com/nestjs

## Configuration

Les variables d'environnement suivantes doivent être définies dans un fichier .env (voir le fichier [.env.default](.env.default)):
Les variables d'environnement du fichier [.env.default](.env.default) doivent être définies dans un fichier `.env` à la racine.

- **TRAJECTOIRE_SNBC_SHEET_ID**: Identifiant du Google spreadsheet utilisé pour le calcul de la trajectoire.
- **TRAJECTOIRE_SNBC_XLSX_ID**: Identifiant du Xlsx original utilisé pour le calcul de la trajectoire (stocké sur le drive)
- **TRAJECTOIRE_SNBC_RESULT_FOLDER_ID**: Identifiant du dossier Google Drive dans lequel les spreadsheets des trajectoires calculées doivent être sauvés (un fichier par EPCI). A noter qu'il existe **un dossier par environnement** (dev, preprod, prod).
- **GCLOUD_SERVICE_ACCOUNT_KEY**: contenu au format json du fichier de clé de compte de service permettant l'utilisation des api Google Drive et Google Spreadsheet.
- **SUPABASE_JWT_SECRET**: clé de signature des tokens jwt. Utilisé pour vérifier la signature des tokens.
Les variables d'environnement suivantes sont définies:

Ces variables d'environnement sont définies:
- Dans les [variables d'environnement de Github](https://github.com/incubateur-ademe/territoires-en-transitions/settings/environments/1431973268/edit) utilisées pour configurer le [déploiement Koyeb](https://app.koyeb.com/services/c7001069-ca11-4fd7-86c6-7feb45b9b68d/settings) pour :

- pour **TRAJECTOIRE_SNBC_SHEET_ID**, **TRAJECTOIRE_SNBC_XLSX_ID** et **TRAJECTOIRE_SNBC_RESULT_FOLDER_ID** dans les [variables d'environnement de Github](https://github.com/incubateur-ademe/territoires-en-transitions/settings/environments/1431973268/edit) utilisées pour configurer le [déploiement Koyeb](https://app.koyeb.com/services/c7001069-ca11-4fd7-86c6-7feb45b9b68d/settings). Les identifiants peuvent également être récupérés à partir du drive de `territoiresentransitions`.
- pour **GCLOUD_SERVICE_ACCOUNT_KEY**, **SUPABASE_JWT_SECRET** et **SUPABASE_SERVICE_ROLE_KEY** dans le [gestionnaire de secret de Koyeb](https://app.koyeb.com/secrets)
- `TRAJECTOIRE_SNBC_SHEET_ID`
- `TRAJECTOIRE_SNBC_XLSX_ID`
- `TRAJECTOIRE_SNBC_RESULT_FOLDER_ID`

Les identifiants peuvent également être récupérés à partir du drive de `territoiresentransitions`.

- Dans le [gestionnaire de secret de Koyeb](https://app.koyeb.com/secrets) pour :

- `GCLOUD_SERVICE_ACCOUNT_KEY`
- `SUPABASE_JWT_SECRET`
- `SUPABASE_SERVICE_ROLE_KEY`
- `SUPABASE_ANON_KEY`

## Scripts disponibles

Expand All @@ -37,3 +43,23 @@ $ pnpm test:backend
# Ou directement avec Nx
$ nx test @tet/backend
```

### Tests

Pour lancer tous les tests :

```
nx test @tet/backend
```

Pour lancer uniquement les tests unitaires (dossier `src`) :

```
nx test @tet/backend src
```

Pour lancer uniquement les tests end-to-end (dossier `test`) :

```
nx test @tet/backend test
```
13 changes: 7 additions & 6 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,30 @@ import { ConfigModule } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';
import { CollectivitesModule } from './collectivites/collectivites.module';
import { CommonModule } from './common/common.module';
import { validateBackendConfiguration } from './common/services/backend-configuration.service';
import configuration from './config/configuration';
import { FichesActionModule } from './fiches/fiches-action.module';
import { IndicateursModule } from './indicateurs/indicateurs.module';
import { SheetModule } from './spreadsheets/sheet.module';
import { TrpcRouter } from './trpc.router';
import { TrpcModule } from './trpc/trpc.module';
import { ConfigurationModule } from './config/configuration.module';
import { TrpcRouter } from './trpc.router';

@Module({
imports: [
ConfigModule.forRoot({
ignoreEnvFile: process.env.NODE_ENV === 'production', // In production, environment variables are set by the deployment
validate: validateBackendConfiguration,
// validate: validateBackendConfiguration,
load: [configuration],
}),
TrpcModule,
ConfigurationModule,
CommonModule,
TrpcModule,
SheetModule,
CollectivitesModule,
IndicateursModule,
AuthModule,
FichesActionModule,
],
controllers: [],
exports: [TrpcRouter],
providers: [TrpcRouter],
})
export class AppModule {}
2 changes: 2 additions & 0 deletions backend/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { JwtModule } from '@nestjs/jwt';
import { CommonModule } from '../common/common.module';
import { AuthGuard } from './guards/auth.guard';
import { AuthService } from './services/auth.service';
import { ConfigurationModule } from '../config/configuration.module';

@Module({
imports: [
JwtModule.register({
global: true,
secret: process.env.SUPABASE_JWT_SECRET,
}),
ConfigurationModule,
CommonModule,
],
providers: [
Expand Down
12 changes: 5 additions & 7 deletions backend/src/auth/guards/auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';
import BackendConfigurationService from '../../common/services/backend-configuration.service';
import BackendConfigurationService from '../../config/configuration.service';
import { getErrorMessage } from '../../common/services/errors.helper';
import { PublicEndpoint } from '../decorators/public-endpoint.decorator';
import { SupabaseJwtPayload } from '../models/auth.models';
Expand All @@ -19,15 +19,15 @@ export class AuthGuard implements CanActivate {
constructor(
private jwtService: JwtService,
private reflector: Reflector,
private backendConfigurationService: BackendConfigurationService,
private configService: BackendConfigurationService
) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();

const publicEndpoint = this.reflector.get(
PublicEndpoint,
context.getHandler(),
context.getHandler()
);

if (publicEndpoint) {
Expand All @@ -43,10 +43,8 @@ export class AuthGuard implements CanActivate {
const payload: SupabaseJwtPayload = await this.jwtService.verifyAsync(
token,
{
secret:
this.backendConfigurationService.getBackendConfiguration()
.SUPABASE_JWT_SECRET,
},
secret: this.configService.get('SUPABASE_JWT_SECRET'),
}
);
// 💡 We're assigning the payload to the request object here
// so that we can access it in our route handlers
Expand Down
9 changes: 4 additions & 5 deletions backend/src/common/common.module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ConfigurationModule } from '../config/configuration.module';
import { VersionController } from './controllers/version.controller';
import BackendConfigurationService from './services/backend-configuration.service';
import DatabaseService from './services/database.service';

@Module({
imports: [ConfigModule],
providers: [ConfigModule, BackendConfigurationService, DatabaseService],
exports: [DatabaseService, BackendConfigurationService],
imports: [ConfigurationModule],
providers: [DatabaseService],
exports: [DatabaseService],
controllers: [VersionController],
})
export class CommonModule {}
27 changes: 0 additions & 27 deletions backend/src/common/services/backend-configuration.service.ts

This file was deleted.

22 changes: 8 additions & 14 deletions backend/src/common/services/database.service.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import { Injectable, Logger, OnApplicationShutdown } from '@nestjs/common';
import { drizzle, PostgresJsDatabase } from 'drizzle-orm/postgres-js';
import { default as postgres } from 'postgres';
import BackendConfigurationService from './backend-configuration.service';
import ConfigurationService from '../../config/configuration.service';

@Injectable()
export default class DatabaseService implements OnApplicationShutdown {
private readonly logger = new Logger(DatabaseService.name);
public readonly db: PostgresJsDatabase;
private readonly client: postgres.Sql;

constructor(
private readonly backendConfigurationService: BackendConfigurationService
) {
constructor(private readonly configService: ConfigurationService) {
this.logger.log(`Initializing database service`);
this.client = postgres(
backendConfigurationService.getBackendConfiguration()
.SUPABASE_DATABASE_URL,
{
prepare: false,
connection: {
application_name: `Backend ${process.env.APPLICATION_VERSION}`,
},
}
);
this.client = postgres(this.configService.get('SUPABASE_DATABASE_URL'), {
prepare: false,
connection: {
application_name: `Backend ${process.env.APPLICATION_VERSION}`,
},
});

this.db = drizzle(this.client);
}
Expand Down
15 changes: 6 additions & 9 deletions backend/src/common/services/supabase.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable, Logger } from '@nestjs/common';
import { createClient } from '@supabase/supabase-js';
import BackendConfigurationService from './backend-configuration.service';
import ConfigurationService from '../../config/configuration.service';
import { Database, DBClient } from '@tet/api';

@Injectable()
Expand All @@ -9,15 +9,12 @@ export default class SupabaseService {

public readonly client: DBClient;

constructor(backendConfigurationService: BackendConfigurationService) {
const backendConfiguration =
backendConfigurationService.getBackendConfiguration();
this.logger.log(
`Initializing supabase service with url: ${backendConfiguration.SUPABASE_URL}`
);
constructor(configService: ConfigurationService) {
const supabaseUrl = configService.get('SUPABASE_URL');
this.logger.log(`Initializing supabase service with url: ${supabaseUrl}`);
this.client = createClient<Database>(
backendConfiguration.SUPABASE_URL,
backendConfiguration.SUPABASE_SERVICE_ROLE_KEY
supabaseUrl,
configService.get('SUPABASE_SERVICE_ROLE_KEY')
);
}
}
10 changes: 10 additions & 0 deletions backend/src/config/configuration.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import ConfigurationService from './configuration.service';

@Module({
imports: [ConfigModule.forRoot()],
providers: [ConfigurationService],
exports: [ConfigurationService],
})
export class ConfigurationModule {}
21 changes: 21 additions & 0 deletions backend/src/config/configuration.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { BackendConfigurationType } from './configuration.model';

@Injectable()
export default class ConfigurationService {
private readonly logger = new Logger(ConfigurationService.name);

constructor(
private readonly configService: ConfigService<
BackendConfigurationType,
true
>
) {
this.logger.log(`Initializing configuration service`);
}

get(key: keyof BackendConfigurationType) {
return this.configService.get(key);
}
}
5 changes: 5 additions & 0 deletions backend/src/config/configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { backendConfigurationSchema } from './configuration.model';

export default () => ({
...backendConfigurationSchema.parse(process.env),
});
16 changes: 8 additions & 8 deletions backend/src/fiches/controllers/fiches-action.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createZodDto } from '@anatine/zod-nestjs';
import { Controller, Get, Param, Query } from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { TokenInfo } from '../../auth/decorators/token-info.decorators';
import { SupabaseJwtPayload } from '../../auth/models/auth.models';
import type { SupabaseJwtPayload } from '../../auth/models/auth.models';
import { getFichesActionSyntheseSchema } from '../models/get-fiches-action-synthese.response';
import { getFichesActionFilterRequestSchema } from '../models/get-fiches-actions-filter.request';
import FichesActionSyntheseService from '../services/fiches-action-synthese.service';
Expand All @@ -11,17 +11,17 @@ import FichesActionSyntheseService from '../services/fiches-action-synthese.serv
* Création des classes de réponse à partir du schema pour générer automatiquement la documentation OpenAPI
*/
export class GetFichesActionSyntheseResponseClass extends createZodDto(
getFichesActionSyntheseSchema,
getFichesActionSyntheseSchema
) {}
export class GetFichesActionFilterRequestClass extends createZodDto(
getFichesActionFilterRequestSchema,
getFichesActionFilterRequestSchema
) {}

@ApiTags('Fiches action')
@Controller('collectivites/:collectivite_id/fiches-action')
export class FichesActionController {
constructor(
private readonly fichesActionSyntheseService: FichesActionSyntheseService,
private readonly fichesActionSyntheseService: FichesActionSyntheseService
) {}

@Get('synthese')
Expand All @@ -33,12 +33,12 @@ export class FichesActionController {
async getFichesActionSynthese(
@Param('collectivite_id') collectiviteId: number,
@Query() request: GetFichesActionFilterRequestClass,
@TokenInfo() tokenInfo: SupabaseJwtPayload,
@TokenInfo() tokenInfo: SupabaseJwtPayload
) {
return this.fichesActionSyntheseService.getFichesActionSynthese(
collectiviteId,
request,
tokenInfo,
tokenInfo
);
}

Expand All @@ -50,12 +50,12 @@ export class FichesActionController {
async getFichesAction(
@Param('collectivite_id') collectiviteId: number,
@Query() request: GetFichesActionFilterRequestClass,
@TokenInfo() tokenInfo: SupabaseJwtPayload,
@TokenInfo() tokenInfo: SupabaseJwtPayload
) {
return this.fichesActionSyntheseService.getFichesAction(
collectiviteId,
request,
tokenInfo,
tokenInfo
);
}
}
Loading
Loading