diff --git a/.travis.yml b/.travis.yml index 135010f..94f1375 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,13 +29,20 @@ install: addons: chrome: stable +env: + global: + - API_URL=http://localhost:5000/v1.0 + - BASE_HREF=/ + - HOME_ROUTE=home + - IRB_URL=http://localhost:5001 + - PORT0=4200 + - PRODUCTION=false script: - npm run ci deploy: provider: script script: bash ./deploy.sh - skip_cleanup: true on: all_branches: true condition: $TRAVIS_BRANCH =~ ^(dev|testing|demo|training|staging|master|rrt\/.*)$ diff --git a/Dockerfile b/Dockerfile index 89030b6..2b988f3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,9 @@ RUN npm install && \ npm run build:$build_config ### STAGE 2: Run ### -FROM nginx +FROM nginx:alpine +RUN set -x && apk add --update --no-cache bash libintl gettext curl + COPY --from=builder /crc-bpmn/dist/* /usr/share/nginx/html/ COPY --from=builder /crc-bpmn/nginx.conf /etc/nginx/conf.d/default.conf @@ -23,7 +25,10 @@ COPY ./docker/substitute-env-variables.sh ./entrypoint.sh RUN chmod +x ./entrypoint.sh # Substitute environment variables in nginx configuration and index.html -ENTRYPOINT ["./entrypoint.sh", "/usr/share/nginx/html/index.html,/etc/nginx/conf.d/default.conf", "PRODUCTION,API_URL,IRB_URL,HOME_ROUTE,PORT0"] +ENTRYPOINT ["./entrypoint.sh", \ + "/usr/share/nginx/html/index.html,/etc/nginx/conf.d/default.conf", \ + "PRODUCTION,API_URL,IRB_URL,HOME_ROUTE,BASE_HREF,PORT0", \ + "/usr/share/nginx/html/index.html"] ### STAGE 3: Profit! ### CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 1562cce..e832bc4 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -2,11 +2,9 @@ version: "3.3" services: db: container_name: db - image: postgres - volumes: - - ./pg-init-scripts/initdb.sh:/docker-entrypoint-initdb.d/initdb.sh + image: sartography/cr-connect-db:$E2E_TAG ports: - - "5432" + - "5432:5432" environment: - POSTGRES_USER=crc_user - POSTGRES_PASSWORD=crc_pass @@ -15,29 +13,19 @@ services: test: ["CMD", "pg_isready"] timeout: 20s retries: 10 - - backend: - container_name: backend - depends_on: - - db - image: sartography/cr-connect-workflow:$E2E_TAG - volumes: - - ./flask-config/config.py:/crc-workflow/instance/config.py - environment: - - FLASK_APP=./crc/__init__.py - - RESET_DB=true - - LDAP_URL=ldap - ports: - - "5000:5000" - command: ./wait-for-it.sh db:5432 -t 0 -- ./docker_run.sh - pb: container_name: pb + depends_on: + - db image: sartography/protocol-builder-mock:$E2E_TAG - volumes: - - ./flask-config/config_pb.py:/protocol-builder-mock/instance/config.py environment: - FLASK_APP=/protocol-builder-mock/app.py + - UPGRADE_DB=true + - DB_HOST=db + - DB_PORT=5432 + - DB_PASSWORD=crc_pass + - DB_USER=crc_user + - DB_NAME=pb_test ports: - "5001:5001" command: ./wait-for-it.sh db:5432 -t 0 -- ./docker_run.sh @@ -47,3 +35,25 @@ services: image: tuxmonteiro/ldap-mock ports: - "3890" + + backend: + container_name: backend + depends_on: + - db + - pb + - ldap + image: sartography/cr-connect-workflow:$E2E_TAG + environment: + - FLASK_APP=./crc/__init__.py + - UPGRADE_DB=true + - RESET_DB=true + - LDAP_URL=ldap + - PB_BASE_URL=http://pb:5001/pb/ + - DB_HOST=db + - DB_PORT=5432 + - DB_PASSWORD=crc_pass + - DB_USER=crc_user + - DB_NAME=crc_test + ports: + - "5000:5000" + command: ./wait-for-it.sh pb:5001 -t 0 -- ./docker_run.sh diff --git a/docker/flask-config/config.py b/docker/flask-config/config.py deleted file mode 100644 index 99094c6..0000000 --- a/docker/flask-config/config.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -basedir = os.path.abspath(os.path.dirname(__file__)) - -NAME = "CR Connect Workflow" -DEVELOPMENT = True -SQLALCHEMY_DATABASE_URI = "postgresql://crc_user:crc_pass@db:5432/crc_dev" - -# %s/%i placeholders expected for uva_id and study_id in various calls. -PB_USER_STUDIES_URL = "http://pb:5001/pb/user_studies?uva_id=%s" -PB_INVESTIGATORS_URL = "http://pb:5001/pb/investigators?studyid=%i" -PB_REQUIRED_DOCS_URL = "http://pb:5001/pb/required_docs?studyid=%i" -PB_STUDY_DETAILS_URL = "http://pb:5001/pb/study?studyid=%i" - -print('\n\n*** USING INSTANCE CONFIG ***\n\n') diff --git a/docker/flask-config/config_pb.py b/docker/flask-config/config_pb.py deleted file mode 100644 index 20160c7..0000000 --- a/docker/flask-config/config_pb.py +++ /dev/null @@ -1,11 +0,0 @@ -import os -basedir = os.path.abspath(os.path.dirname(__file__)) - -NAME = "CR Connect Protocol Builder Mock" -CORS_ENABLED = False -DEVELOPMENT = True -TESTING = True -SQLALCHEMY_DATABASE_URI = "postgresql://crc_user:crc_pass@db:5432/pb_test" -SECRET_KEY = 'a really really really really long secret key' - -print('\n\n*** USING INSTANCE CONFIG ***\n\n') diff --git a/docker/pg-init-scripts/initdb.sh b/docker/pg-init-scripts/initdb.sh deleted file mode 100644 index aa665fa..0000000 --- a/docker/pg-init-scripts/initdb.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -set -e -set -u - -function create_user_and_database() { - local database=$1 - echo " Creating user and database '$database'" - psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL - CREATE USER $database; - CREATE DATABASE $database; - GRANT ALL PRIVILEGES ON DATABASE $database TO $database; -EOSQL -} - -if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then - echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" - for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do - create_user_and_database $db - done - echo "Multiple databases created" -fi diff --git a/docker/substitute-env-variables.sh b/docker/substitute-env-variables.sh index 3a191b8..6dd40f5 100755 --- a/docker/substitute-env-variables.sh +++ b/docker/substitute-env-variables.sh @@ -1,5 +1,13 @@ #!/bin/bash +##################################################################### +# Substitutes the given environment variables in the given files. +# Parameters: +# $1: Comma-delimited list of file paths +# $2: Comma-delimited list of environment variables +# $3: File path to index.html (optional) +##################################################################### + echo 'Substituting environment variables...' # The first parameter is a comma-delimited list of paths to files which should be substituted @@ -32,5 +40,17 @@ do echo "$env_var = ${!env_var}" done -# Execute all other commands with parameters -exec "${@:3}" +# The third parameter is the path to the index.html file +# Rewrite base href in index.html. +# Use @ as a sed delimiter because $BASE_HREF will contain a / character +if [[ -z $3 ]]; then + # Execute all other commands with parameters + exit 0 +else + sed -i -e 's@@@' "$3" + + # Execute all other commands with parameters + exec "${@:4}" +fi + + diff --git a/package-lock.json b/package-lock.json index 02361d8..f6a5122 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12291,9 +12291,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sartography-workflow-lib": { - "version": "0.0.175", - "resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.175.tgz", - "integrity": "sha512-hU86cbCNvj2bGOUftIlOBCbWs6Zl+09g2sUAtuhCP5ZIoZ76xCWAPBUggPGltcEHvQU4Tc1bbMwM8EdfxJuqAQ==" + "version": "0.0.180", + "resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.180.tgz", + "integrity": "sha512-d/ePxdkeIX/ZW3Sp5NIkZsitxRxS560PWorOd9RaojyJtMks2EgCwuN0Gs18NkT0Qrvk7fJHKYYV89k0/H0djQ==" }, "sass": { "version": "1.23.3", diff --git a/package.json b/package.json index d005162..5ac3f7f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "e2e-wf:db-setup": "docker exec -it backend pipenv run flask load-example-data", "e2e-wf:pb-setup": "docker exec -it pb pipenv run flask db upgrade", "e2e-wf": "npm run e2e-wf:stop && npm run e2e-wf:clean && npm run e2e-wf:build && npm run e2e-wf:start && npm run e2e-wf:db-upgrade && npm run e2e-wf:db-setup && npm run e2e-wf:pb-setup", - "ci": "npm run lint && npm run test:coverage && npm run e2e:with-wf && sonar-scanner" + "env": "chmod +x ./docker/substitute-env-variables.sh && ./docker/substitute-env-variables.sh src/index.html PRODUCTION,API_URL,IRB_URL,HOME_ROUTE,BASE_HREF,PORT0 src/index.html", + "ci": "npm run lint && npm run test:coverage && npm run env && npm run e2e:with-wf && sonar-scanner" }, "private": true, "dependencies": { @@ -52,7 +53,7 @@ "ngx-file-drop": "^8.0.8", "ngx-markdown": "^9.0.0", "rxjs": "~6.5.4", - "sartography-workflow-lib": "^0.0.175", + "sartography-workflow-lib": "^0.0.180", "tslib": "^1.11.1", "uuid": "^7.0.2", "zone.js": "^0.10.3" diff --git a/src/app/_dialogs/open-file-dialog/open-file-dialog.component.spec.ts b/src/app/_dialogs/open-file-dialog/open-file-dialog.component.spec.ts index f90f557..9ccc7fc 100644 --- a/src/app/_dialogs/open-file-dialog/open-file-dialog.component.spec.ts +++ b/src/app/_dialogs/open-file-dialog/open-file-dialog.component.spec.ts @@ -6,6 +6,8 @@ import {MatFormFieldModule} from '@angular/material/form-field'; import {MatIconModule} from '@angular/material/icon'; import {MatInputModule} from '@angular/material/input'; import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {Router} from '@angular/router'; +import {RouterTestingModule} from '@angular/router/testing'; import {ApiService, MockEnvironment, mockFileMeta0} from 'sartography-workflow-lib'; import {OpenFileDialogData} from '../../_interfaces/dialog-data'; @@ -15,6 +17,7 @@ describe('OpenFileDialogComponent', () => { let httpMock: HttpTestingController; let component: OpenFileDialogComponent; let fixture: ComponentFixture; + const mockRouter = {navigate: jasmine.createSpy('navigate')}; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -28,6 +31,7 @@ describe('OpenFileDialogComponent', () => { MatInputModule, NoopAnimationsModule, ReactiveFormsModule, + RouterTestingModule, ], declarations: [ OpenFileDialogComponent ], providers: [ @@ -48,6 +52,7 @@ describe('OpenFileDialogComponent', () => { } }, {provide: MAT_DIALOG_DATA, useValue: []}, + {provide: Router, useValue: mockRouter}, ] }) .compileComponents(); diff --git a/src/app/_dialogs/workflow-spec-dialog/workflow-spec-dialog.component.spec.ts b/src/app/_dialogs/workflow-spec-dialog/workflow-spec-dialog.component.spec.ts index 7e7bb90..4be8eec 100644 --- a/src/app/_dialogs/workflow-spec-dialog/workflow-spec-dialog.component.spec.ts +++ b/src/app/_dialogs/workflow-spec-dialog/workflow-spec-dialog.component.spec.ts @@ -6,6 +6,8 @@ import {MatFormFieldModule} from '@angular/material/form-field'; import {MatIconModule} from '@angular/material/icon'; import {MatInputModule} from '@angular/material/input'; import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {Router} from '@angular/router'; +import {RouterTestingModule} from '@angular/router/testing'; import {FormlyModule} from '@ngx-formly/core'; import {FormlyMaterialModule} from '@ngx-formly/material'; import {ApiService, MockEnvironment, mockWorkflowSpec0, mockWorkflowSpecCategories} from 'sartography-workflow-lib'; @@ -17,6 +19,7 @@ describe('WorkflowSpecDialogComponent', () => { let httpMock: HttpTestingController; let component: WorkflowSpecDialogComponent; let fixture: ComponentFixture; + const mockRouter = {navigate: jasmine.createSpy('navigate')}; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -32,6 +35,7 @@ describe('WorkflowSpecDialogComponent', () => { MatInputModule, NoopAnimationsModule, ReactiveFormsModule, + RouterTestingModule, ], declarations: [ WorkflowSpecDialogComponent ], providers: [ @@ -52,6 +56,7 @@ describe('WorkflowSpecDialogComponent', () => { } }, {provide: MAT_DIALOG_DATA, useValue: []}, + {provide: Router, useValue: mockRouter}, ] }) .compileComponents(); diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index c39c157..03e7839 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,6 +1,7 @@ -import {NgModule} from '@angular/core'; +import {APP_BASE_HREF} from '@angular/common'; +import {Injectable, NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; -import {SessionRedirectComponent} from 'sartography-workflow-lib'; +import {AppEnvironment, SessionRedirectComponent} from 'sartography-workflow-lib'; import {environment} from '../environments/environment.runtime'; import {HomeComponent} from './home/home.component'; import {ModelerComponent} from './modeler/modeler.component'; @@ -9,6 +10,14 @@ import {ReferenceFilesComponent} from './reference-files/reference-files.compone import {SignInComponent} from './sign-in/sign-in.component'; import {SignOutComponent} from './sign-out/sign-out.component'; +@Injectable() +export class ThisEnvironment implements AppEnvironment { + homeRoute = environment.homeRoute; + production = environment.production; + api = environment.api; + irbUrl = environment.irbUrl; + baseHref = environment.baseHref; +} const routes: Routes = [ { @@ -59,7 +68,11 @@ const routes: Routes = [ scrollOffset: [0, 84], }) ], - exports: [RouterModule] + exports: [RouterModule], + providers: [ + {provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment}, + {provide: APP_BASE_HREF, useValue: environment.baseHref}, + ] }) export class AppRoutingModule { } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index aaeeb30..afa3b86 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,3 +1,4 @@ +import {APP_BASE_HREF} from '@angular/common'; import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http'; import {Injectable, NgModule} from '@angular/core'; import {FlexLayoutModule} from '@angular/flex-layout'; @@ -22,6 +23,7 @@ import {FormlyModule} from '@ngx-formly/core'; import { AppEnvironment, AuthInterceptor, + ErrorInterceptor, SartographyFormsModule, SartographyPipesModule, SartographyWorkflowLibModule @@ -45,12 +47,12 @@ import {FooterComponent} from './footer/footer.component'; import {HomeComponent} from './home/home.component'; import {ModelerComponent} from './modeler/modeler.component'; import {NavbarComponent} from './navbar/navbar.component'; +import {ProtocolBuilderComponent} from './protocol-builder/protocol-builder.component'; +import {ReferenceFilesComponent} from './reference-files/reference-files.component'; import {SignInComponent} from './sign-in/sign-in.component'; import {SignOutComponent} from './sign-out/sign-out.component'; import {WorkflowSpecCardComponent} from './workflow-spec-card/workflow-spec-card.component'; import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list.component'; -import { ProtocolBuilderComponent } from './protocol-builder/protocol-builder.component'; -import { ReferenceFilesComponent } from './reference-files/reference-files.component'; @Injectable() export class ThisEnvironment implements AppEnvironment { @@ -58,6 +60,7 @@ export class ThisEnvironment implements AppEnvironment { production = environment.production; api = environment.api; irbUrl = environment.irbUrl; + baseHref = environment.baseHref; } @NgModule({ @@ -127,11 +130,9 @@ export class ThisEnvironment implements AppEnvironment { providers: [ {provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {appearance: 'outline'}}, {provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment}, - { - provide: HTTP_INTERCEPTORS, - useClass: AuthInterceptor, - multi: true - }, + {provide: APP_BASE_HREF, useValue: environment.baseHref}, + {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true}, + {provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true}, ] }) export class AppModule { diff --git a/src/app/diagram/diagram.component.spec.ts b/src/app/diagram/diagram.component.spec.ts index 3cd9c35..2937bde 100644 --- a/src/app/diagram/diagram.component.spec.ts +++ b/src/app/diagram/diagram.component.spec.ts @@ -2,6 +2,8 @@ import {HttpClientTestingModule, HttpTestingController} from '@angular/common/ht import {DebugNode} from '@angular/core'; import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {MatIconModule} from '@angular/material/icon'; +import {Router} from '@angular/router'; +import {RouterTestingModule} from '@angular/router/testing'; import * as FileSaver from 'file-saver'; import {ApiService, BPMN_DIAGRAM_DEFAULT, FileType, MockEnvironment} from 'sartography-workflow-lib'; import { @@ -16,17 +18,20 @@ describe('DiagramComponent', () => { let httpMock: HttpTestingController; let fixture: ComponentFixture; let component: DebugNode['componentInstance']; + const mockRouter = {navigate: jasmine.createSpy('navigate')}; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ HttpClientTestingModule, MatIconModule, + RouterTestingModule, ], declarations: [DiagramComponent], providers: [ ApiService, - {provide: 'APP_ENVIRONMENT', useClass: MockEnvironment} + {provide: 'APP_ENVIRONMENT', useClass: MockEnvironment}, + {provide: Router, useValue: mockRouter}, ] }); diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts index f047fdf..2a42f0b 100644 --- a/src/app/home/home.component.spec.ts +++ b/src/app/home/home.component.spec.ts @@ -2,6 +2,8 @@ import {HttpClient} from '@angular/common/http'; import {HttpClientTestingModule} from '@angular/common/http/testing'; import {Component} from '@angular/core'; import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {Router} from '@angular/router'; +import {RouterTestingModule} from '@angular/router/testing'; import {ApiService, MockEnvironment} from 'sartography-workflow-lib'; import {HomeComponent} from './home.component'; @@ -24,6 +26,7 @@ class MockWorkflowSpecListComponent { describe('HomeComponent', () => { let component: HomeComponent; let fixture: ComponentFixture; + const mockRouter = {navigate: jasmine.createSpy('navigate')}; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -33,12 +36,14 @@ describe('HomeComponent', () => { MockWorkflowSpecListComponent, ], imports: [ - HttpClientTestingModule + HttpClientTestingModule, + RouterTestingModule, ], providers: [ HttpClient, ApiService, {provide: 'APP_ENVIRONMENT', useClass: MockEnvironment}, + {provide: Router, useValue: mockRouter}, ] }) .compileComponents(); diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index 20afb95..17db5ce 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -1,6 +1,6 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, Inject, OnInit} from '@angular/core'; import {Router} from '@angular/router'; -import {ApiService, isSignedIn, User} from 'sartography-workflow-lib'; +import {ApiService, AppEnvironment, isSignedIn, User, UserParams} from 'sartography-workflow-lib'; interface NavItem { path?: string; @@ -19,11 +19,11 @@ interface NavItem { export class NavbarComponent { navLinks: NavItem[]; user: User; - isSignedIn = isSignedIn; constructor( private router: Router, private api: ApiService, + @Inject('APP_ENVIRONMENT') private environment: AppEnvironment, ) { this._loadUser(); } @@ -39,7 +39,6 @@ export class NavbarComponent { this._loadNavLinks(); }, error => { localStorage.removeItem('token'); - this.api.openUrl('/'); }); } } diff --git a/src/app/reference-files/reference-files.component.spec.ts b/src/app/reference-files/reference-files.component.spec.ts index 8e16284..e65b11d 100644 --- a/src/app/reference-files/reference-files.component.spec.ts +++ b/src/app/reference-files/reference-files.component.spec.ts @@ -6,6 +6,8 @@ import {MatIconModule} from '@angular/material/icon'; import {MatSnackBarModule} from '@angular/material/snack-bar'; import {BrowserDynamicTestingModule} from '@angular/platform-browser-dynamic/testing'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {Router} from '@angular/router'; +import {RouterTestingModule} from '@angular/router/testing'; import {of} from 'rxjs'; import {ApiService, FileMeta, FileType, MockEnvironment, mockFileMetaReference0} from 'sartography-workflow-lib'; import {OpenFileDialogComponent} from '../_dialogs/open-file-dialog/open-file-dialog.component'; @@ -17,6 +19,7 @@ describe('ReferenceFilesComponent', () => { let httpMock: HttpTestingController; let component: ReferenceFilesComponent; let fixture: ComponentFixture; + const mockRouter = {navigate: jasmine.createSpy('navigate')}; // Mock file and response headers const mockDocMeta: FileMeta = createClone()(mockFileMetaReference0); @@ -41,6 +44,7 @@ describe('ReferenceFilesComponent', () => { MatDialogModule, MatIconModule, MatSnackBarModule, + RouterTestingModule, ], declarations: [ OpenFileDialogComponent, @@ -57,6 +61,7 @@ describe('ReferenceFilesComponent', () => { } }, {provide: MAT_DIALOG_DATA, useValue: []}, + {provide: Router, useValue: mockRouter}, ] }).overrideModule(BrowserDynamicTestingModule, { set: { diff --git a/src/app/sign-in/sign-in.component.spec.ts b/src/app/sign-in/sign-in.component.spec.ts index 9baa1a7..2e39e7c 100644 --- a/src/app/sign-in/sign-in.component.spec.ts +++ b/src/app/sign-in/sign-in.component.spec.ts @@ -74,19 +74,19 @@ describe('SignInComponent', () => { }); it('should fake sign in during testing', () => { - const openSessionSpy = spyOn((component as any).api, 'openSession').and.stub(); + const redirectToLoginSpy = spyOn((component as any).api, 'redirectToLogin').and.stub(); (component as any).environment.production = false; component.model = mockUser; component.signIn(); - expect(openSessionSpy).toHaveBeenCalledWith(mockUser); + expect(redirectToLoginSpy).toHaveBeenCalledWith(jasmine.any(String), mockUser); expect(component.error).toBeUndefined(); }); it('should display an error if sign in is called on production', () => { - const openSessionSpy = spyOn((component as any).api, 'openSession').and.stub(); + const redirectToLoginSpy = spyOn((component as any).api, 'redirectToLogin').and.stub(); (component as any).environment.production = true; component.signIn(); - expect(openSessionSpy).not.toHaveBeenCalled(); + expect(redirectToLoginSpy).not.toHaveBeenCalled(); expect(component.error).toBeTruthy(); }); diff --git a/src/app/sign-in/sign-in.component.ts b/src/app/sign-in/sign-in.component.ts index f0e521e..1a59087 100644 --- a/src/app/sign-in/sign-in.component.ts +++ b/src/app/sign-in/sign-in.component.ts @@ -71,8 +71,9 @@ export class SignInComponent implements OnInit { // For testing purposes, create a user to simulate login. if (!this.environment.production) { + localStorage.setItem('prev_url', location.origin); this.model.redirect_url = location.origin + '/session'; - this.api.openSession(this.model); + this.api.redirectToLogin(this.model.redirect_url, this.model); } else { this.error = new Error('This feature does not work in production.'); } diff --git a/src/environments/environment.runtime.ts b/src/environments/environment.runtime.ts index 17a3a9f..d661f74 100644 --- a/src/environments/environment.runtime.ts +++ b/src/environments/environment.runtime.ts @@ -9,4 +9,5 @@ export const environment: AppEnvironment = { production: _has(ENV, 'production', '$PRODUCTION') ? (ENV.production === 'true') : false, api: _has(ENV, 'api', '$API_URL') ? ENV.api : 'http://localhost:5000/v1.0', irbUrl: _has(ENV, 'irbUrl', '$IRB_URL') ? ENV.irbUrl : 'http://localhost:5001', + baseHref: _has(ENV, 'baseHref', '$BASE_HREF') ? ENV.baseHref : '/', }; diff --git a/src/environments/environment.spec.ts b/src/environments/environment.spec.ts index 5d59b5b..b281892 100644 --- a/src/environments/environment.spec.ts +++ b/src/environments/environment.spec.ts @@ -1,3 +1,4 @@ +import {AppEnvironment} from 'sartography-workflow-lib'; import {_has, environment} from './environment.runtime'; declare var ENV; @@ -9,6 +10,7 @@ describe('Environments', () => { expect(environment.api).toEqual('apiRoot'); expect(environment.irbUrl).toEqual('irbUrl'); expect(environment.homeRoute).toEqual('home'); + expect(environment.baseHref).toEqual('/'); }); it('should check if environment variables are defined', () => { @@ -17,31 +19,37 @@ describe('Environments', () => { production: '$PRODUCTION', api: '$API_URL', irbUrl: '$IRB_URL', + baseHref: '$BASE_HREF', }; expect(_has(env, 'homeRoute', '$HOME_ROUTE')).toBeFalse(); expect(_has(env, 'production', '$PRODUCTION')).toBeFalse(); expect(_has(env, 'api', '$API_URL')).toBeFalse(); expect(_has(env, 'irbUrl', '$IRB_URL')).toBeFalse(); + expect(_has(env, 'baseHref', '$BASE_HREF')).toBeFalse(); env.homeRoute = undefined; env.production = undefined; env.api = undefined; env.irbUrl = undefined; + env.baseHref = undefined; expect(_has(env, 'homeRoute', '$HOME_ROUTE')).toBeFalse(); expect(_has(env, 'production', '$PRODUCTION')).toBeFalse(); expect(_has(env, 'api', '$API_URL')).toBeFalse(); expect(_has(env, 'irbUrl', '$IRB_URL')).toBeFalse(); + expect(_has(env, 'baseHref', '$BASE_HREF')).toBeFalse(); env.homeRoute = 'something'; env.production = 'something'; env.api = 'something'; env.irbUrl = 'something'; + env.baseHref = 'something'; expect(_has(env, 'homeRoute', '$HOME_ROUTE')).toBeTrue(); expect(_has(env, 'production', '$PRODUCTION')).toBeTrue(); expect(_has(env, 'api', '$API_URL')).toBeTrue(); expect(_has(env, 'irbUrl', '$IRB_URL')).toBeTrue(); + expect(_has(env, 'baseHref', '$BASE_HREF')).toBeTrue(); }); }); diff --git a/src/index.html b/src/index.html index 93426f9..89824a5 100644 --- a/src/index.html +++ b/src/index.html @@ -10,6 +10,7 @@ production: '$PRODUCTION', api: '$API_URL', irbUrl: '$IRB_URL', + baseHref: '$BASE_HREF', };