Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • web-et-numerique/factory/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_server
1 result
Show changes
Commits on Source (34)
Showing
with 3322 additions and 3245 deletions
# Env
TAG=<version number>
NODE_ENV=<dev or production>
APP_HOST=<localhost or host>
# Configuration
SERVICE_API_BIND_PORT=<service port>
JWT_SECRET=<the secret used to sign jwt token>
......
......@@ -4,12 +4,13 @@ module.exports = {
project: true,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin', 'jest'],
plugins: ['@typescript-eslint/eslint-plugin', 'jest', 'jsdoc'],
extends: [
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
'plugin:jest/recommended',
'plugin:jsdoc/recommended',
],
root: true,
env: {
......@@ -23,5 +24,17 @@ module.exports = {
'@typescript-eslint/no-explicit-any': 'warn',
'jest/no-conditional-expect': 'off',
// JSDOC extends overrides: disable some of extends rules
// JSdoc is not always needed
'jsdoc/require-jsdoc': 0,
// Prefer declaring types with typescript
'jsdoc/require-returns': 0,
'jsdoc/require-returns-type': 0,
'jsdoc/require-param': 0,
'jsdoc/require-param-type': 0,
// Allow some params to be described and some to be omitted
'jsdoc/check-param-names': 0,
},
};
......@@ -44,15 +44,25 @@
"aidants",
"aptic",
"cnfs",
"compodoc",
"courriel",
"cpam",
"espace",
"friday",
"grandlyon",
"healthcheck",
"luxon",
"monday",
"nestjs",
"orientator",
"permanences",
"photonban",
"saturday",
"spyer",
"Unvalidated"
"sunday",
"thursday",
"tuesday",
"Unvalidated",
"wednesday"
]
}
# Res'In Serveur
L'appplication est basée sur le framework [Nest](https://nestjs.com). Celle-ci utilise une BDD mongodb.
## Installation
### Prérequis
Windows: avoir Docker Desktop installé
Linux: avoir docker et docker-compose installé
Créer un ficher .env sur le modèle du ficher `template.env` et remplir les différentes variables.
### Installation
Suivre le [wiki d'installation disponible sur le client](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/-/wikis/Installation-projet-RESIN)
## Running the app
### Base de donnée
```bash
$ docker-compose up -d database-ram
$ docker-compose up -d mongo-express
```
### Application
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
### Liens
- [Swagger](http://localhost:3000/api/doc)
- [Mongo Express](http://localhost:8081)
## Documentation
A documentation is generated with compodoc in addition of the existing documentation on the wiki.
```sh
npm run doc:serve
```
You can now visualize it at : `localhost:8080`
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## [Documentation](https://resin-dev.grandlyon.com/doc/)
## Support
......
......@@ -30,6 +30,7 @@ services:
MC_API_KEY: ${MC_API_KEY}
MC_SERVER: ${MC_SERVER}
MC_LIST_ID: ${MC_LIST_ID}
APP_HOST: ${APP_HOST}
restart: unless-stopped
networks:
- backend
......@@ -54,7 +55,7 @@ services:
- db-ram:/bitnami
mongo-express:
image: mongo-express
image: mongo-express:0.54.0
restart: unless-stopped
container_name: resin-mongo-express
depends_on:
......@@ -69,6 +70,7 @@ services:
ME_CONFIG_BASICAUTH_USERNAME: ${ME_CONFIG_BASICAUTH_USERNAME}
ME_CONFIG_BASICAUTH_PASSWORD: ${ME_CONFIG_BASICAUTH_PASSWORD}
ME_CONFIG_MONGODB_SERVER: database-ram
ME_CONFIG_MONGODB_URL: mongodb://database-ram:27017
ghost:
platform: linux/x86_64
......
This diff is collapsed.
......@@ -2,13 +2,12 @@
"name": "ram_server",
"private": true,
"version": "3.0.1",
"description": "Nest TypeScript starter repository",
"description": "Res'in API",
"license": "MIT",
"scripts": {
"build": "nest build",
"dev": "nodemon",
"doc:serve": "./node_modules/.bin/compodoc compodoc -s",
"doc": "./node_modules/.bin/compodoc compodoc -p tsconfig.json -s -n 'Resin api documentation'",
"doc": "npx compodoc -p tsconfig.doc.json -s -n 'Resin api documentation'",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"init-db": "node ./scripts/init-db.js",
"lint": "eslint src test -c .eslintrc.js --fix",
......@@ -67,7 +66,7 @@
"swagger-ui-express": "^4.6.0"
},
"devDependencies": {
"@compodoc/compodoc": "^1.1.16",
"@compodoc/compodoc": "^1.1.24",
"@golevelup/ts-jest": "^0.3.2",
"@nestjs/cli": "^9.1.3",
"@nestjs/schematics": "^9.0.3",
......@@ -81,6 +80,7 @@
"@typescript-eslint/parser": "^5.37",
"eslint": "^8.0.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-jsdoc": "^48.2.4",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.1.2",
"jest-junit": "^14.0.0",
......
......@@ -15,7 +15,7 @@ axios
headers: { Authorization: `Bearer ${res.data.accessToken}` },
};
axios
.get('http://localhost:3000/api/structures/ctm/update', config)
.post('http://localhost:3000/api/structures/ctm/update', {}, config)
.then((res) => {
console.log(`CTM territories | statusCode: ${res.status}`);
})
......
......@@ -61,6 +61,17 @@ docker cp ./mongo_clean_users.js $TARGET_CONTAINER_NAME:/dump/temp/
docker exec $TARGET_CONTAINER_NAME bash -c "mongo --authenticationDatabase admin --username root --password \$MONGODB_ROOT_PASSWORD $DB_NAME < /dump/temp/mongo_clean_users.js"
rm mongo_clean_users.js
# For local db, change user email
if [ "$TARGET_CONTAINER_NAME" = "resin-db" ]; then
cat > mongo_rename_user.js <<EOL
db.users.updateMany({email: "inclusionnumerique@grandlyon.com"}, [{ \$set: { email: "admin@admin.com" } }])
db.users.updateMany({role: 1}, [{ \$set: { password: { \$literal: "\$2a\$12\$vLQjJ9zAWyUwiXLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu" } } }])
EOL
docker cp ./mongo_rename_user.js $TARGET_CONTAINER_NAME:/dump/temp/
docker exec $TARGET_CONTAINER_NAME bash -c "mongo --authenticationDatabase admin --username root --password \$MONGODB_ROOT_PASSWORD $DB_NAME < /dump/temp/mongo_rename_user.js"
rm mongo_rename_user.js
fi
# .migrate file copy
cd $CWD
if [ "$DB_NAME" = "ram" ]; then
......@@ -69,12 +80,13 @@ if [ "$DB_NAME" = "ram" ]; then
fi
docker exec $TARGET_CONTAINER_NAME rm -rf /dump/temp/
echo ""
echo "*************************************************************************** Summary *********************************************************************"
echo "Db $TARGET_CONTAINER_NAME/$DB_NAME backed up in $BACKUP_DIR (with .migrate file)"
echo "Latest dump $SRC_URL stored in $DUMP_DIR and restored in $TARGET_CONTAINER_NAME/$DB_NAME (and prod emails cleaned up)"
if [ "$DB_NAME" = "ram" ]; then echo "The local .migrate file has been copied from $SRC_URL for db $DB_NAME and migration:up runned"; fi
if [ "$DB_NAME" = "ram" ]; then echo "The local .migrate file has been copied from $SRC_URL for db $DB_NAME"; fi
if [ "$DB_NAME" = "ram" ]; then echo "Don't forget to reset elasticsearch indexes!!!"; fi
if [ "$DB_NAME" = "ram" ] && [ "$TARGET_CONTAINER_NAME" = "resin-db" ]; then echo "migration:up runned"; fi
echo "*********************************************************************************************************************************************************"
exit 0
import { HttpModule } from '@nestjs/axios';
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { Types } from 'mongoose';
import { structuresDocumentDataMock } from '../../test/mock/data/structures.mock.data';
import { mockJwtAuthGuard } from '../../test/mock/guards/jwt-auth.mock.guard';
......@@ -88,7 +88,7 @@ describe('AdminController', () => {
updatedAt: new Date('2021-03-02T10:07:48.000Z'),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
const module = await Test.createTestingModule({
imports: [ConfigurationModule, HttpModule, SearchModule],
providers: [
{
......@@ -169,8 +169,10 @@ describe('AdminController', () => {
});
it('should get structure does not exist', async () => {
mockStructureService.findOne.mockResolvedValue(null);
try {
await adminController.validatePendingStructure(pendingStructureTest);
expect(true).toBe(false);
} catch (e) {
expect(e.message).toBe('Structure does not exist');
expect(e.status).toBe(404);
......@@ -186,8 +188,10 @@ describe('AdminController', () => {
});
it('should get structure does not exist', async () => {
mockStructureService.findOne.mockResolvedValue(null);
try {
await adminController.refusePendingStructure(pendingStructureTest);
expect(true).toBe(false);
} catch (e) {
expect(e.message).toBe('Structure does not exist');
expect(e.status).toBe(404);
......@@ -204,6 +208,7 @@ describe('AdminController', () => {
it('should return nonexisting user', async () => {
try {
await adminController.deleteUser({ id: 'userDoesNotExist' });
expect(true).toBe(false);
} catch (e) {
expect(e.message).toBe('Invalid user id');
expect(e.status).toBe(400);
......
import { Test, TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { AdminService } from './admin.service';
describe('AdminService', () => {
let adminService: AdminService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
const module = await Test.createTestingModule({
providers: [AdminService],
}).compile();
......
import { Test, TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { version } from '../package.json';
import { AppController } from './app.controller';
describe('AppController', () => {
let app: TestingModule;
let appController: AppController;
beforeAll(async () => {
app = await Test.createTestingModule({
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [AppController],
}).compile();
appController = module.get<AppController>(AppController);
});
describe('healthcheck', () => {
it('should return "Hello World!"', async () => {
const appController = app.get<AppController>(AppController);
const result = { status: 'API Online', uptime: 1, version: '' };
jest
.spyOn(appController, 'healthcheck')
.mockImplementation(async (): Promise<{ status; uptime; version }> => result);
const healthcheck = await appController.healthcheck();
expect(healthcheck.status).toBe(result.status);
expect(healthcheck.uptime).toBe(result.uptime);
it('should return healthcheck', async () => {
const healthcheck = appController.healthcheck();
expect(healthcheck.status).toBe('API Online');
expect(healthcheck.uptime).not.toBeNull();
expect(healthcheck.version).toBe(version);
});
});
});
......@@ -2,15 +2,10 @@ import { Controller, Get } from '@nestjs/common';
import { version } from '../package.json';
@Controller()
export class AppController {
private start: number;
private version: string = version;
constructor() {
this.start = Date.now();
}
private start = Date.now();
@Get('healthcheck')
async healthcheck() {
healthcheck() {
const now = Date.now();
return {
status: 'API Online',
......
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { appointmentMockData, singleAppointmentMock } from '../../test/mock/data/appointment.mock.data';
import { AppointmentController } from './appointment.controller';
import { Appointment } from './appointment.schema';
......@@ -14,7 +14,7 @@ describe('UserRegistryController', () => {
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
const module = await Test.createTestingModule({
imports: [],
providers: [
{
......
import { HttpModule } from '@nestjs/axios';
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import * as ejs from 'ejs';
import { appointmentMockData } from '../../test/mock/data/appointment.mock.data';
import { StructuresServiceMock } from '../../test/mock/services/structures.mock.service';
......@@ -38,7 +38,7 @@ describe('userRegistryService', () => {
const ejsSpy = jest.spyOn(ejs, 'renderFile');
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
const module = await Test.createTestingModule({
imports: [MailerModule, HttpModule],
providers: [
AppointmentService,
......
......@@ -25,10 +25,6 @@ export class AppointmentService {
private readonly mailerService: MailerService
) {}
/**
* @param newAppointment
* @returns {Promise<AppointmentDocument>}
*/
public async create(newAppointment: AppointmentDto): Promise<IAppointment> {
this.appointment = newAppointment;
......@@ -55,9 +51,6 @@ export class AppointmentService {
}
}
/**
* @returns {Promise<AppointmentDocument[]>}
*/
public async findAll(): Promise<IAppointment[]> {
return this.AppointmentModel.find().exec();
}
......
......@@ -2,7 +2,7 @@ import { HttpService } from '@nestjs/axios';
import { JwtModule } from '@nestjs/jwt';
import { getModelToken } from '@nestjs/mongoose';
import { PassportModule } from '@nestjs/passport';
import { Test, TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { AuthServiceMock } from '../../test/mock/services/auth.mock.service';
import { StructuresServiceMock } from '../../test/mock/services/structures.mock.service';
import { CategoriesService } from '../categories/services/categories.service';
......@@ -35,7 +35,7 @@ describe('AuthController', () => {
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
const module = await Test.createTestingModule({
imports: [
PassportModule,
MailerModule,
......@@ -111,15 +111,4 @@ describe('AuthController', () => {
'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
});
});
it('should not login invalid user', async () => {
const loginCredentials: LoginDto = { email: 'jacques.dupont@mii.com', password: process.env.USER_PWD };
try {
await authController.login(loginCredentials);
} catch (e) {
expect(e.response).toBe('Invalid credentials');
expect(e.message).toBe('Invalid credentials');
expect(e.status).toBe(401);
}
});
});
import { JwtService } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { Test, TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { JwtServiceMock } from '../../test/mock/services/jwt.mock.service';
import { UsersServiceMock } from '../../test/mock/services/user.mock.service';
import { ConfigurationModule } from '../configuration/configuration.module';
......@@ -13,7 +13,7 @@ describe('AuthService', () => {
let authService: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
const module = await Test.createTestingModule({
imports: [PassportModule, MailerModule, ConfigurationModule],
providers: [
AuthService,
......@@ -62,7 +62,6 @@ describe('AuthService', () => {
_createToken = jest.spyOn(AuthService.prototype as any, '_createToken');
});
it('should login user pauline.dupont@mii.com', async () => {
// Token creation mock
const token = {
accessToken:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4',
......@@ -82,7 +81,6 @@ describe('AuthService', () => {
});
it('should login not login user jacques.dupont@mii.com because email is not verified', async () => {
// Token creation mock
const token = {
accessToken:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4',
......@@ -93,6 +91,7 @@ describe('AuthService', () => {
const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
try {
await authService.login(loginDto);
expect(true).toBe(false);
} catch (e) {
expect(e.response).toBe('Unverified user');
expect(e.message).toBe('Unverified user');
......@@ -101,7 +100,6 @@ describe('AuthService', () => {
});
it('should login not login user toto@mii.com because email does not exist', async () => {
// Token creation mock
const token = {
accessToken:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4',
......@@ -112,6 +110,7 @@ describe('AuthService', () => {
const loginDto: LoginDto = { email: 'toto@mii.com', password: 'test1A!!' }; //NOSONAR
try {
await authService.login(loginDto);
expect(true).toBe(false);
} catch (e) {
expect(e.response).toBe('Invalid credentials');
expect(e.message).toBe('Invalid credentials');
......@@ -120,7 +119,6 @@ describe('AuthService', () => {
});
it('should login not login user toto@mii.com because password is invalid', async () => {
// Token creation mock
const token = {
accessToken:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4',
......@@ -131,6 +129,7 @@ describe('AuthService', () => {
const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: '1' }; //NOSONAR
try {
await authService.login(loginDto);
expect(true).toBe(false);
} catch (e) {
expect(e.response).toBe('Invalid credentials');
expect(e.message).toBe('Invalid credentials');
......
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { mockJwtAuthGuard } from '../../../test/mock/guards/jwt-auth.mock.guard';
import { mockRoleGuard } from '../../../test/mock/guards/role.mock.guard';
import { CategoriesServiceMock } from '../../../test/mock/services/categories.mock.service';
......@@ -13,7 +13,7 @@ describe('CategoriesAccompagnementController', () => {
let categoriesController: CategoriesController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
const module = await Test.createTestingModule({
imports: [],
providers: [
{
......
import { HttpModule } from '@nestjs/axios';
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { CreateCategories } from '../dto/create-categories.dto';
import { CategoriesService } from './categories.service';
......@@ -16,7 +16,7 @@ describe('CategoriesService', () => {
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
const module = await Test.createTestingModule({
imports: [HttpModule],
providers: [
CategoriesService,
......