diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2202721d3b14ca7dbd6941a88c61879adc223e31..71334d68dcb25f1b96dba1727078fc83ce41b5b3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,39 +4,61 @@ stages: build_development: stage: build + tags: + - build only: - - master + - development script: - - export NODE_ENV=DEV - - docker-compose --project-name admin-gui-dev build --build-arg env=dev admin-gui + - export TAG=dev + - export APP_PORT=8090 + - export CONFIG_FILE_PATH=./config/config.json + - docker-compose build --build-arg conf=dev admin-gui + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker-compose push + +build_release: + stage: build + tags: + - build + only: + - tags + except: + - /^(?!master).+@/ + script: + - export TAG=$(echo $CI_COMMIT_TAG | sed 's/v//g') + - export APP_PORT=8090 + - export CONFIG_FILE_PATH=./config/config.json + - docker-compose build --build-arg conf=prod admin-gui + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker-compose push deploy_development: stage: deploy only: - - master + - development script: - - export NODE_ENV=DEV - - docker-compose --project-name admin-gui-dev up -d + - export TAG=dev + - export APP_PORT=8090 + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker-compose pull + - export CONFIG_FILE_PATH=./config/config-dev.json + - docker-compose --project-name admin-gui-${TAG} up -d --force-recreate environment: name: development - # url: - -build_staging: - stage: build - only: - - staging - script: - - export NODE_ENV=REC - - sed -i 's/DEV_/REC_/g' docker-compose.yml - - docker-compose --project-name admin-gui-rec build --build-arg env=rec admin-gui deploy_staging: stage: deploy only: - - staging + - development + when: manual script: - - export NODE_ENV=REC - - sed -i 's/DEV_/REC_/g' docker-compose.yml - - docker-compose --project-name admin-gui-rec up -d + - export TAG=staging + - export APP_PORT=8190 + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - TAG=dev docker-compose pull + - docker tag $CI_REGISTRY/refonte-data/admin-gui:dev $CI_REGISTRY/refonte-data/admin-gui:${TAG} + - docker push $CI_REGISTRY/refonte-data/admin-gui:${TAG} + - export CONFIG_FILE_PATH=./config/config-rec.json + - docker-compose --project-name admin-gui-${TAG} up -d --force-recreate environment: name: staging diff --git a/Dockerfile b/Dockerfile index d6a76d8b0347bbfcc5967b78862e957134f81ba2..a3851616acdbb50ef12377d640803b75ad6f7f12 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,10 +9,10 @@ RUN npm install # Copy the project COPY . /app -ARG env +ARG conf # Building the Angular app /dist -RUN npm run build:${env} +RUN npm run build:${conf} # Stage 1, based on Nginx, to have only the compiled app FROM nginx diff --git a/angular.json b/angular.json index ff8bfd73e820eb8abd3e52cd5ad74bb2a1f19714..c6a5c193bcfa82f49de76c2851067e8b09f3180b 100644 --- a/angular.json +++ b/angular.json @@ -23,33 +23,28 @@ "src/assets" ], "styles": [ - "src/styles.scss", + "src/styles.scss" ], "scripts": [] }, "configurations": { "development": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.dev.ts" - } - ], - "optimization": true, + "optimization": false, "outputHashing": "all", - "sourceMap": false, - "extractCss": true, + "sourceMap": true, + "extractCss": false, "namedChunks": false, "aot": true, "extractLicenses": true, "vendorChunk": false, - "buildOptimizer": true + "buildOptimizer": false, + "fileReplacements": [] }, "recette": { "fileReplacements": [ { "replace": "src/environments/environment.ts", - "with": "src/environments/environment.rec.ts" + "with": "src/environments/environment.prod.ts" } ], "optimization": true, diff --git a/config/config-dev.json b/config/config-dev.json new file mode 100644 index 0000000000000000000000000000000000000000..3320942c82b8a0ff6ae8e347c87e619f1ab91685 --- /dev/null +++ b/config/config-dev.json @@ -0,0 +1,8 @@ +{ + "organizations": { + "url": "https://kong-dev.alpha.grandlyon.com/organizations/" + }, + "resources": { + "url": "https://kong-dev.alpha.grandlyon.com/resources/resources/" + } +} \ No newline at end of file diff --git a/config/config-rec.json b/config/config-rec.json new file mode 100644 index 0000000000000000000000000000000000000000..4955a9a3ca1fb9c3cc45c9fc867b4ff120df5e06 --- /dev/null +++ b/config/config-rec.json @@ -0,0 +1,8 @@ +{ + "organizations": { + "url": "https://kong-rec.alpha.grandlyon.com/organizations/" + }, + "resources": { + "url": "https://kong-rec.alpha.grandlyon.com/resources/resources/" + } +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index adbbe04701c61ab386006ed26eb73e019c90e0e8..1efb1d4d92a653ccefbca2a592d86f481a1b99aa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,13 @@ -version: "2" +version: "3.1" services: admin-gui: - container_name: admin-gui-${NODE_ENV} - build: - context: ./ + build: . + image: registry.alpha.grandlyon.com/refonte-data/admin-gui:${TAG} volumes: - ./nginx.conf.template:/etc/nginx/conf.d/default.conf + - ${CONFIG_FILE_PATH}:/usr/share/nginx/html/assets/config/config.json ports: - - ${DEV_APP_PORT}:80 + - ${APP_PORT}:80 restart: unless-stopped \ No newline at end of file diff --git a/package.json b/package.json index c08ce546adb66c4d1f61d1894a417a9240c94ee6..44e295b09d709f0807cae53bd0e4fbed2c2ed20d 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "ng": "ng", "start": "ng serve", "build": "ng build", - "build:dev": "ng build --prod --configuration=development", - "build:rec": "ng build --prod --configuration=recette", + "build:dev": "node --max_old_space_size=4096 ./node_modules/@angular/cli/bin/ng build --configuration=development", + "build:prod": "node --max_old_space_size=4096 ./node_modules/@angular/cli/bin/ng build --configuration=production", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" diff --git a/src/app/app.module.ts b/src/app/app.module.ts index dae310cdf7818a05bfd15bbada78b3fcfc82a10e..c5e0ccd4a906c771333afc79d90f75d50cdf6476 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,35 +1,27 @@ import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AppComponent } from './app.component'; -import { OrganizationsComponent } from './components/organizations/list/organizations.component'; -import { OrganizationService } from './services/organization.service'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { RouterModule } from '@angular/router'; -import { MenuComponent } from './components/menu/menu.component'; import { AppRoutingModule } from './app.routing.module'; -import { WelcomeComponent } from './components/welcome/welcome.component'; -import { OrganizationDetailComponent } from './components/organizations/detail/organization-detail.component'; -import { OrganizationFormComponent } from './components/organizations/edit/organization-form.component'; -import { PaginatorComponent } from './components/paginator/paginator.component'; -import { ResourcesComponent } from './components/resources/list/resources.component'; -import { ResourceService } from './services/resource.service'; -import { ResourceFormComponent } from './components/resources/edit/resource-form.component'; -import { ResourceDetailComponent } from './components/resources/detail/resource-detail.component'; +import { AppServices, AppConfigService } from './services'; +import { AppComponents } from './components'; + +export function initAppConfig(appConfigService: AppConfigService) { + return (): Promise<any> => { + return new Promise((resolve, reject) => { + appConfigService.load(); + resolve(); + }); + }; +} @NgModule({ declarations: [ AppComponent, - OrganizationsComponent, - PaginatorComponent, - OrganizationDetailComponent, - OrganizationFormComponent, - MenuComponent, - WelcomeComponent, - ResourcesComponent, - ResourceFormComponent, - ResourceDetailComponent, + ...AppComponents, ], imports: [ AppRoutingModule, @@ -41,8 +33,13 @@ import { ResourceDetailComponent } from './components/resources/detail/resource- ReactiveFormsModule, ], providers: [ - OrganizationService, - ResourceService, + ...AppServices, + { + provide: APP_INITIALIZER, + useFactory: initAppConfig, + deps: [AppConfigService], + multi: true, + }, ], bootstrap: [AppComponent] }) diff --git a/src/app/components/index.ts b/src/app/components/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..c1207017b83f5bfc5cfb9855c460632a50d73a78 --- /dev/null +++ b/src/app/components/index.ts @@ -0,0 +1,34 @@ +import { MenuComponent } from './menu/menu.component'; +import { OrganizationDetailComponent } from './organizations/detail/organization-detail.component'; +import { OrganizationFormComponent } from './organizations/edit/organization-form.component'; +import { OrganizationsComponent } from './organizations/list/organizations.component'; +import { PaginatorComponent } from './paginator/paginator.component'; +import { ResourceDetailComponent } from './resources/detail/resource-detail.component'; +import { ResourceFormComponent } from './resources/edit/resource-form.component'; +import { ResourcesComponent } from './resources/list/resources.component'; +import { WelcomeComponent } from './welcome/welcome.component'; + +export { + MenuComponent, + OrganizationDetailComponent, + OrganizationFormComponent, + OrganizationsComponent, + PaginatorComponent, + ResourceDetailComponent, + ResourceFormComponent, + ResourcesComponent, + WelcomeComponent, +}; + +// tslint:disable-next-line:variable-name +export const AppComponents = [ + MenuComponent, + OrganizationDetailComponent, + OrganizationFormComponent, + OrganizationsComponent, + PaginatorComponent, + ResourceDetailComponent, + ResourceFormComponent, + ResourcesComponent, + WelcomeComponent, +]; diff --git a/src/app/components/organizations/list/organizations.component.ts b/src/app/components/organizations/list/organizations.component.ts index a0299af9f17fdcfac3917c6d8d0358d475be4ce7..17d063f7da2902ff8f6a68cef367260d148850ca 100644 --- a/src/app/components/organizations/list/organizations.component.ts +++ b/src/app/components/organizations/list/organizations.component.ts @@ -56,6 +56,7 @@ export class OrganizationsComponent implements OnInit { private search() { this.organizationsService.getOrganizations() .subscribe((items: OrganizationRO) => { + console.log(items); this.organizations = items.organizations; this.totalElement = items.totalCount; diff --git a/src/app/services/app-config.service.ts b/src/app/services/app-config.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..edabf90b5de6509faaa179008aaa0cac92a70606 --- /dev/null +++ b/src/app/services/app-config.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; + +export class AppConfig { + organizations: { + url: string; + }; + resources: { + url: string; + }; +} + +export let APP_CONFIG: AppConfig; + +@Injectable() +export class AppConfigService { + + constructor() { } + + public load() { + return new Promise((resolve, reject) => { + const conf = new AppConfig(); + APP_CONFIG = Object.assign(conf, window['adminGuiEnvConfig']); + resolve(); + }); + } +} diff --git a/src/app/services/index.ts b/src/app/services/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..24455cc0269d221c37faf7bb2c66792a0ec257b8 --- /dev/null +++ b/src/app/services/index.ts @@ -0,0 +1,16 @@ +import { AppConfigService } from './app-config.service'; +import { OrganizationService } from './organization.service'; +import { ResourceService } from './resource.service'; + +export { + AppConfigService, + OrganizationService, + ResourceService, +}; + +// tslint:disable-next-line:variable-name +export const AppServices = [ + AppConfigService, + OrganizationService, + ResourceService, +]; diff --git a/src/app/services/organization.service.ts b/src/app/services/organization.service.ts index 234379783b06433e37e3bd337fa5dad443684c92..c5360560f0a3ef99269166b974109ef92beef31d 100644 --- a/src/app/services/organization.service.ts +++ b/src/app/services/organization.service.ts @@ -3,7 +3,8 @@ import { Observable , Subject } from 'rxjs'; import { map } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; import { Organization, IOrganization, OrganizationRO } from '../models/organization.model'; -import { environment } from 'src/environments/environment'; +import { APP_CONFIG } from './app-config.service'; + @Injectable() export class OrganizationService { @@ -30,7 +31,7 @@ export class OrganizationService { query += '&offset=' + (this.pageNumber ? (this.pageNumber - 1) * this.limit : 0); query += '&sort_by=' + this.sortOptions.value + '.' + this.sortOptions.order; - return this._httpClient.get<IOrganization[]>(environment.organizations.url + query, { observe: 'response' }).pipe( + return this._httpClient.get<IOrganization[]>(APP_CONFIG.organizations.url + query, { observe: 'response' }).pipe( map((response) => { const totalCount = response.headers.get('Content-Range'); const organizations = []; @@ -42,7 +43,7 @@ export class OrganizationService { } findById(id): Observable<Organization> { - return this._httpClient.get<IOrganization>(environment.organizations.url + id).pipe( + return this._httpClient.get<IOrganization>(APP_CONFIG.organizations.url + id).pipe( map((response) => { return new Organization(response); } @@ -50,12 +51,12 @@ export class OrganizationService { } delete(id) { - return this._httpClient.delete(environment.organizations.url + id); + return this._httpClient.delete(APP_CONFIG.organizations.url + id); } replaceOrCreate(data: Organization): Observable<Organization> { if (data.id) { - return this._httpClient.put<IOrganization>(environment.organizations.url + data.id, data).pipe( + return this._httpClient.put<IOrganization>(APP_CONFIG.organizations.url + data.id, data).pipe( map((response) => { return new Organization(response); } @@ -65,7 +66,7 @@ export class OrganizationService { data.links.forEach((link) => { delete link.id; }); - return this._httpClient.post<IOrganization>(environment.organizations.url, data).pipe( + return this._httpClient.post<IOrganization>(APP_CONFIG.organizations.url, data).pipe( map((response) => { return new Organization(response); } diff --git a/src/app/services/resource.service.ts b/src/app/services/resource.service.ts index ed890a249120f268778d35cf3854d06fa6ce3ef5..d1b0d38d309beb7f3763e593c08a352f987e4737 100644 --- a/src/app/services/resource.service.ts +++ b/src/app/services/resource.service.ts @@ -3,7 +3,7 @@ import { Observable , Subject } from 'rxjs'; import { map } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; import { Resource, IResource, ResourceRO } from '../models/resource.model'; -import { environment } from 'src/environments/environment'; +import { APP_CONFIG } from './app-config.service'; @Injectable() export class ResourceService { @@ -30,7 +30,7 @@ export class ResourceService { query += '&offset=' + (this.pageNumber ? (this.pageNumber - 1) * this.limit : 0); query += '&sort_by=' + this.sortOptions.value + '.' + this.sortOptions.order; - return this._httpClient.get<IResource[]>(environment.resources.url + query, { observe: 'response' }).pipe( + return this._httpClient.get<IResource[]>(APP_CONFIG.resources.url + query, { observe: 'response' }).pipe( map((response) => { const totalCount = response.headers.get('Content-Range'); const resources = []; @@ -42,7 +42,7 @@ export class ResourceService { } findById(id): Observable<Resource> { - return this._httpClient.get<IResource>(environment.resources.url + id).pipe( + return this._httpClient.get<IResource>(APP_CONFIG.resources.url + id).pipe( map((response) => { return new Resource(response); } @@ -50,18 +50,18 @@ export class ResourceService { } delete(id) { - return this._httpClient.delete(environment.resources.url + id); + return this._httpClient.delete(APP_CONFIG.resources.url + id); } replaceOrCreate(data): Observable<Resource> { if (data.id) { - return this._httpClient.put<IResource>(environment.resources.url + data.id, data).pipe( + return this._httpClient.put<IResource>(APP_CONFIG.resources.url + data.id, data).pipe( map((response) => { return new Resource(response); } )); } - return this._httpClient.post<IResource>(environment.resources.url, data).pipe( + return this._httpClient.post<IResource>(APP_CONFIG.resources.url, data).pipe( map((response) => { return new Resource(response); } diff --git a/src/assets/config/config.json b/src/assets/config/config.json new file mode 100644 index 0000000000000000000000000000000000000000..3320942c82b8a0ff6ae8e347c87e619f1ab91685 --- /dev/null +++ b/src/assets/config/config.json @@ -0,0 +1,8 @@ +{ + "organizations": { + "url": "https://kong-dev.alpha.grandlyon.com/organizations/" + }, + "resources": { + "url": "https://kong-dev.alpha.grandlyon.com/resources/resources/" + } +} \ No newline at end of file diff --git a/src/assets/swagger/organizations.json b/src/assets/swagger/organizations.json deleted file mode 100644 index 073cfc414e3faf6db1356fadeee198d1532882ed..0000000000000000000000000000000000000000 --- a/src/assets/swagger/organizations.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "description": "The actors API description", - "version": "1.0", - "title": "Actors example" - }, - "basePath": "/localhost:3001", - "tags": [], - "schemes": ["http"], - "paths": { - "/organizations": { - "get": { - "summary": "Get all organizations", - "responses": { - "200": { - "description": "" - } - }, - "tags": ["organizations"], - "produces": ["application/json"], - "consumes": ["application/json"] - }, - "post": { - "summary": "Create one organization", - "parameters": [{ - "name": "Organization", - "required": true, - "in": "body", - "schema": { - "$ref": "#/definitions/Organization" - } - }], - "responses": { - "201": { - "description": "The record has been successfully created." - } - }, - "tags": ["organizations"], - "produces": ["application/json"], - "consumes": ["application/json"] - } - } - }, - "definitions": { - "Organization": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "website": { - "type": "string" - }, - "logo": { - "type": "string" - } - }, - "required": ["id", "name", "description"] - } - } -} \ No newline at end of file diff --git a/src/assets/swagger/resources.json b/src/assets/swagger/resources.json deleted file mode 100644 index 073cfc414e3faf6db1356fadeee198d1532882ed..0000000000000000000000000000000000000000 --- a/src/assets/swagger/resources.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "description": "The actors API description", - "version": "1.0", - "title": "Actors example" - }, - "basePath": "/localhost:3001", - "tags": [], - "schemes": ["http"], - "paths": { - "/organizations": { - "get": { - "summary": "Get all organizations", - "responses": { - "200": { - "description": "" - } - }, - "tags": ["organizations"], - "produces": ["application/json"], - "consumes": ["application/json"] - }, - "post": { - "summary": "Create one organization", - "parameters": [{ - "name": "Organization", - "required": true, - "in": "body", - "schema": { - "$ref": "#/definitions/Organization" - } - }], - "responses": { - "201": { - "description": "The record has been successfully created." - } - }, - "tags": ["organizations"], - "produces": ["application/json"], - "consumes": ["application/json"] - } - } - }, - "definitions": { - "Organization": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "website": { - "type": "string" - }, - "logo": { - "type": "string" - } - }, - "required": ["id", "name", "description"] - } - } -} \ No newline at end of file diff --git a/src/environments/environment.dev.ts b/src/environments/environment.dev.ts deleted file mode 100644 index c6dcbcb734ab73e6013fb599f1408b534ed0d451..0000000000000000000000000000000000000000 --- a/src/environments/environment.dev.ts +++ /dev/null @@ -1,13 +0,0 @@ - -const kongBaseUrl = 'https://kong-dev.alpha.grandlyon.com'; - -export const environment = { - production: true, - - organizations: { - url: kongBaseUrl + '/organizations/', - }, - resources: { - url: kongBaseUrl + '/resources/', - }, -}; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc6808639ae1a66de9930fba6fa3b85c1c22d0e4 --- /dev/null +++ b/src/environments/environment.prod.ts @@ -0,0 +1,6 @@ + +const kongBaseUrl = 'https://kong-dev.alpha.grandlyon.com'; + +export const environment = { + production: true, +}; diff --git a/src/environments/environment.rec.ts b/src/environments/environment.rec.ts deleted file mode 100644 index 90736b86bd04d1a7ba7e74f1b8bc5a61784d1e0b..0000000000000000000000000000000000000000 --- a/src/environments/environment.rec.ts +++ /dev/null @@ -1,13 +0,0 @@ - -const kongBaseUrl = 'https://kong-rec.alpha.grandlyon.com'; - -export const environment = { - production: true, - - organizations: { - url: kongBaseUrl + '/organizations/', - }, - resources: { - url: kongBaseUrl + '/resources/', - }, -}; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index ad234623026cbff20d4b5a05ae315c25d9cd1b12..0fe39f17326a0e65121c41612229dc0e808aed3d 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -4,12 +4,5 @@ export const environment = { production: false, - - organizations: { - url: 'http://localhost:3000/organizations/', - }, - resources: { - url: 'http://localhost:3003/resources/', - }, }; diff --git a/src/main.ts b/src/main.ts index 91ec6da5f07886cf1d97b5d4212f4ddaa3eb2c9c..475ba5ac26b6e19fe1ccf890824b78fccb147c93 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,5 +8,17 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.log(err)); +fetch('./assets/config/config.json') + .then(response => response.json()) + .then((config) => { + if (!config) { + return; + } + + // Store the response so that your ConfigService can read it. + window['adminGuiEnvConfig'] = config; + + platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.log(err)); + + });