Commit 8c839b30 authored by ncastejon's avatar ncastejon
Browse files

Merge branch 'development' of...

Merge branch 'development' of https://gitlab.alpha.grandlyon.com/refonte-data/service-organizations into development
parents c0b6a1c5 7004498d
Pipeline #2361 passed with stages
in 7 minutes and 6 seconds
......@@ -17,6 +17,7 @@
coverage
node_modules
npm-debug.log
.vscode
# Don't commit *.env except the template
*.env
......
stages:
- build
- deploy
- db_migration
variables:
GROUP_HEADER: x-consumer-groups
ADMIN_GROUP_NAME: admin
ACCESS_TOKEN_COOKIE_KEY: access_token
build_development:
stage: build
......
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a>
</p>
[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
[travis-url]: https://travis-ci.org/nestjs/nest
[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
[linux-url]: https://travis-ci.org/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="blank">Node.js</a> framework for building efficient and scalable server-side applications, heavily inspired by <a href="https://angular.io" target="blank">Angular</a>.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/dm/@nestjs/core.svg" alt="NPM Downloads" /></a>
<a href="https://travis-ci.org/nestjs/nest"><img src="https://api.travis-ci.org/nestjs/nest.svg?branch=master" alt="Travis" /></a>
<a href="https://travis-ci.org/nestjs/nest"><img src="https://img.shields.io/travis/nestjs/nest/master.svg?label=linux" alt="Linux" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#5" alt="Coverage" /></a>
<a href="https://gitter.im/nestjs/nestjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge"><img src="https://badges.gitter.im/nestjs/nestjs.svg" alt="Gitter" /></a>
<a href="https://opencollective.com/nest#backer"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec"><img src="https://img.shields.io/badge/Donate-PayPal-dc3d53.svg"/></a>
<a href="https://twitter.com/nestframework"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
......@@ -35,16 +5,19 @@ $ npm install
```
## Environment variables
To avoid to hard code some variables inside the .ts files (credentials, endpoints), we use environment variables.
Here is the flow:
- set the NODE_ENV variable inside the gitlab-ci.yml
- this will define the env_file used by docker-compose.yml (eg. configuration/dev.env or configuration/production.env)
- also this variable (NODE_ENV) is used by the Nestjs application to define the .env file (see config.module.ts)
For development purpose, the files dev.env and production.env are added to the Git project.
But this will have to be removed when the project goes to production.
In order to run the code, some environment variables are needed. They are specified in the `template.env` file at the root of the project.
For a local deployment:
1. `cp template.env .env`
2. Edit .env according to the chosen configuration
The values will be read from the file by default, but you can override any of those by exporting manually the variable before launching the service.
## Running the app without docker
## Running the app
You will need to provide a healthy connection to a database in order for the service to start.
```bash
# development
......@@ -53,15 +26,24 @@ $ npm run start
# watch mode
$ npm run start:dev
# incremental rebuild (webpack)
$ npm run webpack
$ npm run start:hmr
# production mode
$ npm run start:prod
```
## Test
## Running the app with docker
```bash
# build
$ docker-compose build
# deploy
$ docker-compose up [-d]
# build and deploy
$ docker-compose up --build [-d]
```
<!-- ## Test
```bash
# unit tests
......@@ -72,18 +54,6 @@ $ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
``` -->
## License
Nest is [MIT licensed](LICENSE).
......@@ -8,29 +8,27 @@ services:
ports:
- ${ORGANIZATIONS_SERVICE_BIND_PORT}:3000
environment:
NODE_ENV: ${NODE_ENV}
POSTGRES_USER: user
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: postgres
POSTGRES_HOST: database-organizations
POSTGRES_PORT: 5432
GROUP_HEADER: ${GROUP_HEADER}
ADMIN_GROUP_NAME: ${ADMIN_GROUP_NAME}
ACCESS_TOKEN_COOKIE_KEY: ${ACCESS_TOKEN_COOKIE_KEY}
restart: unless-stopped
depends_on:
- database-organizations
database-organizations:
image: postgres:11
image: bitnami/postgresql:11
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: postgres
POSTGRESQL_USERNAME: user
POSTGRESQL_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRESQL_DATABASE: postgres
restart: unless-stopped
ports:
ports:
- ${POSTGRES_PORT}:5432
volumes:
- portail-data:/var/lib/postgresql/data
volumes:
portail-data:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
favicon.ico

4.19 KB | W: | H:

favicon.ico

31.3 KB | W: | H:

favicon.ico
favicon.ico
favicon.ico
favicon.ico
  • 2-up
  • Swipe
  • Onion skin
......@@ -10,28 +10,20 @@ if (process.env.PRODBUILD) {
root = 'dist';
}
// Only for development purpose, set environment variable based on the env file provided
if (process.env.NODE_ENV === 'LOCAL') {
const envConfig = dotenv.parse(fs.readFileSync(`./${process.env.NODE_ENV}.env`));
for (const k in envConfig) {
if (envConfig.hasOwnProperty(k)) {
process.env[k] = envConfig[k];
}
}
}
const ormConfig = {
type: 'postgres',
host: process.env.POSTGRES_HOST,
port: process.env.POSTGRES_PORT,
username: 'user',
username: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: 'postgres',
database: process.env.POSTGRES_DB,
entities: [`${root}/**/**.entity{.ts,.js}`],
synchronize: false,
migrations: [`${root}/migrations/*.ts`],
cli: { migrationsDir: `${root}/migrations` },
logging: ['error'],
logging: [
'error',
],
};
module.exports = ormConfig;
{
"name": "service-organizations",
"version": "1.2.0",
"version": "2.0.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -2435,6 +2435,11 @@
"ieee754": "^1.1.4"
}
},
"buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
......@@ -3075,6 +3080,15 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"cookie-parser": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz",
"integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==",
"requires": {
"cookie": "0.3.1",
"cookie-signature": "1.0.6"
}
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
......@@ -3584,6 +3598,14 @@
"safer-buffer": "^2.1.0"
}
},
"ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
......@@ -6959,6 +6981,30 @@
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true
},
"jsonwebtoken": {
"version": "8.5.1",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
"integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
"requires": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^5.6.0"
},
"dependencies": {
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
......@@ -6971,6 +7017,25 @@
"verror": "1.10.0"
}
},
"jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"requires": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"kareem": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.0.tgz",
......@@ -7115,6 +7180,41 @@
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
"dev": true
},
"lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
},
"lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
},
"lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
},
"lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
},
"lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
},
"lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
},
"lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
},
"lodash.sortby": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
......
{
"name": "service-organizations",
"version": "1.2.0",
"version": "2.0.2",
"description": "description",
"author": "",
"license": "MIT",
......@@ -18,10 +18,10 @@
"test:cov": "jest --coverage",
"test:e2e": "jest --config ./test/jest-e2e.json",
"webpack": "webpack --config webpack.config.js",
"typeorm:generate": "cross-env MIGRATION=1 ts-node -r tsconfig-paths/register ./node_modules/.bin/typeorm migration:generate -n",
"typeorm:migrate": "cross-env MIGRATION=1 ts-node -r tsconfig-paths/register ./node_modules/.bin/typeorm migration:run",
"typeorm:revert": "cross-env MIGRATION=1 ts-node -r tsconfig-paths/register ./node_modules/.bin/typeorm migration:revert",
"typeorm:migrate-ci": "ts-node -r tsconfig-paths/register ./node_modules/.bin/typeorm migration:run"
"typeorm:generate": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate -n",
"typeorm:migrate": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:run",
"typeorm:revert": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:revert",
"typeorm:migrate-ci": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:run"
},
"dependencies": {
"@godaddy/terminus": "^4.1.0",
......@@ -34,15 +34,18 @@
"amqplib": "^0.5.2",
"class-transformer": "^0.1.9",
"class-validator": "^0.9.1",
"cookie-parser": "^1.4.4",
"cross-env": "^5.2.0",
"dotenv": "^6.1.0",
"jsonwebtoken": "^8.5.1",
"pg": "^7.4.3",
"postgresql": "0.0.1",
"reflect-metadata": "^0.1.12",
"rxjs": "^6.2.2",
"serve-favicon": "^2.5.0",
"typeorm": "^0.2.7",
"typescript": "^3.0.1"
"typescript": "^3.0.1",
"uuid": "^3.3.2"
},
"devDependencies": {
"@nestjs/testing": "^5.4.0",
......
import { Logger } from '@nestjs/common';
export class AppLogger extends Logger {
log(message: string, context?: string) {
// add your tailored logic here
super.log(`[log] ${message}`, context);
}
warn(message: string, context?: string) {
// add your tailored logic here
super.warn(`[warn] ${message}`, context);
}
error(message: string, trace?: string, context?: string) {
// add your tailored logic here
super.error(`[error] ${message}`, trace, context);
}
}
\ No newline at end of file
import { Module } from '@nestjs/common';
import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { OrganizationsModule } from './organizations/organizations.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { LinksModule } from './links/links.module';
......@@ -6,6 +6,8 @@ import { HealthModule } from './health/health.module';
import { ConfigModule } from './configuration/config.module';
import { APP_GUARD } from '@nestjs/core';
import { GroupsGuard } from './guards/groups.guards';
import { AppLogger } from './app-logger';
import { VerifyXsrfTokenAndDecodeJWTPayloadMiddleware } from './middlewares/decode-jwt-payload.middleware';
@Module({
imports: [
......@@ -16,10 +18,24 @@ import { GroupsGuard } from './guards/groups.guards';
HealthModule,
],
providers: [
AppLogger,
{
provide: APP_GUARD,
useClass: GroupsGuard,
},
],
})
export class AppModule { }
export class AppModule {
configure(consumer: MiddlewareConsumer) {
// Applying the middleware that takes the Authorization header jwt payload and put it in the request headers
consumer
.apply(VerifyXsrfTokenAndDecodeJWTPayloadMiddleware).forRoutes(
{ path: 'links', method: RequestMethod.POST },
{ path: 'links/:id', method: RequestMethod.PUT },
{ path: 'links/:id', method: RequestMethod.DELETE },
{ path: 'organizations', method: RequestMethod.POST },
{ path: 'organizations/:id', method: RequestMethod.PUT },
{ path: 'organizations/:id', method: RequestMethod.DELETE },
);
}
}
import * as dotenv from 'dotenv';
import * as fs from 'fs';
import { config } from './config';
export class ConfigService {
private _config = config;
constructor() {
// Only for development purpose, set environment variable based on the env file provided
if (process.env.NODE_ENV === 'LOCAL') {
const envConfig = dotenv.parse(fs.readFileSync(`./src/configuration/${process.env.NODE_ENV}.env`));
for (const k in envConfig) {
if (envConfig.hasOwnProperty(k)) {
process.env[k] = envConfig[k];
}
}
}
// Initializing conf with values from var env
dotenv.config();
this._config.groupNames.admin = process.env.ADMIN_GROUP_NAME;
this._config.groupHeader = process.env.GROUP_HEADER;
this._config.accessTokenCookieKey = process.env.ACCESS_TOKEN_COOKIE_KEY;
}
get config() {
return this._config;
}
}
\ No newline at end of file
}
......@@ -3,4 +3,5 @@ export const config = {
admin: '',
},
groupHeader: '',
accessTokenCookieKey: '',
};
\ No newline at end of file
GROUP_HEADER=
ADMIN_GROUP_NAME=
\ No newline at end of file
......@@ -4,10 +4,18 @@ import { ConfigService } from '../configuration/config.service';
@Injectable()
export class GroupsGuard implements CanActivate {
constructor(private readonly reflector: Reflector, private configService: ConfigService) { }
private logger: Logger;
constructor(
private readonly reflector: Reflector,
private configService: ConfigService,
) {
this.logger = new Logger(GroupsGuard.name);
}
canActivate(context: ExecutionContext): boolean {
Logger.log('[+] GroupMiddleware');
this.logger.log('Entering function', `${GroupsGuard.name} - ${this.canActivate.name}`);
// We get the groups specified on the endpoint with the @Groups annotation
// We need then to get the real group names associated to those keys from the config file
let groups = this.reflector.get<string[]>('groups', context.getHandler());
......@@ -31,13 +39,13 @@ export class GroupsGuard implements CanActivate {
const groupHeader = request.headers[this.configService.config.groupHeader];
if (!groupHeader) {
Logger.log(' [-] no group header');
this.logger.log('No group header found in the request');
return false;
}
const consumerGroups = groupHeader.split(',').map(e => e.trim());
Logger.log(` [-] consumer groups: ${consumerGroups}`);
this.logger.log(`Consumer groups: ${consumerGroups}`);
const hasGroup = () => consumerGroups.some((group) => groups.includes(group));
......
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
import { Entity, PrimaryColumn, Column, ManyToOne, JoinColumn, BeforeInsert } from 'typeorm';
import { IsString, IsOptional } from 'class-validator';
import { Organization } from '../organizations/organization.entity';
import { ApiModelProperty } from '@nestjs/swagger';
import * as uuidv4 from 'uuid/v4';
@Entity('link')
export class LinkEntity {
@PrimaryGeneratedColumn()
@PrimaryColumn('text')
@IsString()
@ApiModelProperty()
id: number;
id: string;
@IsString()
@IsOptional()
......@@ -30,7 +32,6 @@ export class LinkEntity {
@JoinColumn({ name: 'organizationId' })
@ApiModelProperty()
organization: Organization;
}
export interface LinksRO {
......
import { Controller, Get, Body, Post, Param, Delete, Put, Query, Response, Request, Req, Res } from '@nestjs/common';
import { Controller, Get, Body, Post, Param, Delete, Put, Query, Response } from '@nestjs/common';
import { LinksService } from './links.service';
import { LinkEntity } from './link.entity';
import { ApiUseTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
......@@ -35,11 +35,7 @@ export class LinksController {
@Post()
@Groups('admin')
async create(@Body() linkDTo: Link) {
return this.linksService.create(linkDTo).then((link) => {
return link;
}).catch((err) => {
return err;
});
return this.linksService.create(linkDTo);
}
@ApiOperation({ title: 'Update one link' })
......
import { Injectable, Logger } from '@nestjs/common';
import { Injectable, Logger, InternalServerErrorException, NotFoundException } from '@nestjs/common';
import { LinkEntity, LinksRO } from './link.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Link } from './link.dto';
import { LinkRepository } from './link.repository';
import * as uuidv4 from 'uuid/v4';
@Injectable()