diff --git a/backend/build.gradle b/backend/build.gradle index 423d9c450..334c907ea 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -90,8 +90,10 @@ bootWar { ) } from(tasks.findByPath(':ui:npm_run_buildProd').outputs) { - // into '/' - into '/public' + //Copying into this particular classpath location due too + //deployment to external Tomcat would not work with /public location + //This way, it works with both embedded and extarnal Tomcat + into 'WEB-INF/classes/resources' } archiveName = "${baseName}-${version}.war" } @@ -107,8 +109,10 @@ bootJar { ) } from(tasks.findByPath(':ui:npm_run_buildProd').outputs) { - // into '/' - into '/public' + //Copying into this particular classpath location due too + //deployment to external Tomcat would not work with /public location + //This way, it works with both embedded and external Tomcat + into 'WEB-INF/classes/resources' } archiveName = "${baseName}-${version}.jar" } @@ -166,7 +170,6 @@ dependencies { //So it works on Java 9 without explicitly requiring to load that module (needed by Hibernate) runtimeOnly 'javax.xml.bind:jaxb-api:2.3.0' - // TODO: these will likely only be runtimeOnly or test scope, unless we want to ship the libraries with the final product compile "com.h2database:h2" runtimeOnly "org.postgresql:postgresql" runtimeOnly 'org.mariadb.jdbc:mariadb-java-client:2.2.0' diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/RootUiViewController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/RootUiViewController.java index b0ef4984c..fe9e4b246 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/RootUiViewController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/RootUiViewController.java @@ -1,7 +1,22 @@ package edu.internet2.tier.shibboleth.admin.ui.controller; +import com.google.common.io.ByteStreams; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; import org.springframework.stereotype.Controller; +import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.util.stream.Collectors; @Controller public class RootUiViewController { @@ -10,4 +25,20 @@ public class RootUiViewController { public String index() { return "redirect:/index.html"; } + + @RequestMapping(value = {"**/index.html", "/dashboard/**", "/metadata/**"}) + public void indexHtml(HttpServletRequest request, HttpServletResponse response) throws IOException, URISyntaxException { + //This method is necessary in order for Angular framework to honor dynamic ServletContext + //under which shib ui application is deployed, both during initial index.html load and subsequest page refreshes + String content = new BufferedReader(new InputStreamReader(request.getServletContext() + .getResourceAsStream("/WEB-INF/classes/resources/index.html"))) + .lines() + .collect(Collectors.joining("\n")); + + content = content.replaceFirst("", ""); + response.setContentType("text/html"); + try (OutputStream writer = response.getOutputStream()) { + writer.write(content.getBytes()); + } + } } diff --git a/ui/proxy.conf.json b/ui/proxy.conf.json index be9b3e364..89fcfdf90 100644 --- a/ui/proxy.conf.json +++ b/ui/proxy.conf.json @@ -1,22 +1,34 @@ { - "/api": { + "/shibui/api": { "target": "http://localhost:8080", "secure": false, - "logLevel": "debug" + "logLevel": "debug", + "pathRewrite": { + "^/shibui": "" + } }, - "/actuator": { + "/shibui/actuator": { "target": "http://localhost:8080", "secure": false, - "logLevel": "debug" + "logLevel": "debug", + "pathRewrite": { + "^/shibui": "" + } }, "/login": { "target": "http://localhost:8080", "secure": false, - "logLevel": "debug" + "logLevel": "debug", + "pathRewrite": { + "^/shibui": "" + } }, "/logout": { "target": "http://localhost:8080", "secure": false, - "logLevel": "debug" + "logLevel": "debug", + "pathRewrite": { + "^/shibui": "" + } } } diff --git a/ui/src/app/admin/service/admin.service.spec.ts b/ui/src/app/admin/service/admin.service.spec.ts index de2a943a1..609c2d8bc 100644 --- a/ui/src/app/admin/service/admin.service.spec.ts +++ b/ui/src/app/admin/service/admin.service.spec.ts @@ -3,6 +3,7 @@ import { AdminService } from './admin.service'; import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'; import { HttpRequest, HttpClientModule } from '@angular/common/http'; import { Admin } from '../model/admin'; +import API_BASE_PATH from '../../app.constant'; let users = [ { @@ -43,7 +44,7 @@ describe('Admin Service', () => { service.query().subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === '/api/admin/users' + return req.url === `${API_BASE_PATH}/admin/users` && req.method === 'GET'; }, `GET admin collection`); } @@ -55,7 +56,7 @@ describe('Admin Service', () => { service.update({...users[0]}).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === '/api/admin/users/abc' + return req.url === `${API_BASE_PATH}/admin/users/abc` && req.method === 'PATCH'; }, `PATCH admin user`); } @@ -67,7 +68,7 @@ describe('Admin Service', () => { service.remove(users[0].username).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === '/api/admin/users/abc' + return req.url === `${API_BASE_PATH}/admin/users/abc` && req.method === 'DELETE'; }, `DELETE admin user`); } diff --git a/ui/src/app/admin/service/admin.service.ts b/ui/src/app/admin/service/admin.service.ts index 7604c7dd5..580616bc3 100644 --- a/ui/src/app/admin/service/admin.service.ts +++ b/ui/src/app/admin/service/admin.service.ts @@ -4,11 +4,13 @@ import { Admin } from '../model/admin'; import { HttpClient } from '@angular/common/http'; import { map, catchError } from 'rxjs/operators'; +import API_BASE_PATH from '../../app.constant'; + @Injectable() export class AdminService { private endpoint = '/admin/users'; - private base = '/api'; + private base = API_BASE_PATH; constructor( private http: HttpClient diff --git a/ui/src/app/app.brand.ts b/ui/src/app/app.brand.ts index bc0237204..eb828fc41 100644 --- a/ui/src/app/app.brand.ts +++ b/ui/src/app/app.brand.ts @@ -6,9 +6,9 @@ export const brand: Brand = { title: 'brand.header.title' }, logo: { - default: '/assets/shibboleth_logowordmark_color.png', - small: '/assets/shibboleth_icon_color_130x130.png', - large: '/assets/shibboleth_logowordmark_color.png', + default: 'assets/shibboleth_logowordmark_color.png', + small: 'assets/shibboleth_icon_color_130x130.png', + large: 'assets/shibboleth_logowordmark_color.png', alt: 'brand.logo-alt', link: { label: 'brand.logo-link-label', // shibboleth diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index 67ae33e22..fdeb258b9 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -84,11 +84,11 @@
In partnership with  - Unicon Logo + Unicon Logo  and  - Internet 2 Logo + Internet 2 Logo
diff --git a/ui/src/app/app.constant.ts b/ui/src/app/app.constant.ts new file mode 100644 index 000000000..206ee74c7 --- /dev/null +++ b/ui/src/app/app.constant.ts @@ -0,0 +1,2 @@ +export const API_BASE_PATH = 'api'; +export default API_BASE_PATH; diff --git a/ui/src/app/app.module.ts b/ui/src/app/app.module.ts index 99011223b..d8a1f0a7c 100644 --- a/ui/src/app/app.module.ts +++ b/ui/src/app/app.module.ts @@ -1,6 +1,6 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; -import { StoreModule, Store } from '@ngrx/store'; +import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import { StoreDevtoolsModule } from '@ngrx/store-devtools'; import { StoreRouterConnectingModule, RouterStateSerializer } from '@ngrx/router-store'; @@ -24,6 +24,8 @@ import { WizardModule } from './wizard/wizard.module'; import { FormModule } from './schema-form/schema-form.module'; import { environment } from '../environments/environment.prod'; import { I18nModule } from './i18n/i18n.module'; +import { ApiPathInterceptor } from './core/service/api-path.interceptor'; +import { APP_BASE_HREF } from '@angular/common'; @NgModule({ declarations: [ @@ -63,7 +65,17 @@ import { I18nModule } from './i18n/i18n.module'; AppRoutingModule ], providers: [ - { provide: RouterStateSerializer, useClass: CustomRouterStateSerializer }, + { + provide: APP_BASE_HREF, + useFactory: () => { + const url = new URL(document.getElementsByTagName('base')[0].href); + return url.pathname; + } + }, + { + provide: RouterStateSerializer, + useClass: CustomRouterStateSerializer + }, { provide: HTTP_INTERCEPTORS, useClass: AuthorizedInterceptor, @@ -73,6 +85,11 @@ import { I18nModule } from './i18n/i18n.module'; provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true + }, + { + provide: HTTP_INTERCEPTORS, + useClass: ApiPathInterceptor, + multi: true } ], bootstrap: [AppComponent] diff --git a/ui/src/app/core/effect/version.effect.ts b/ui/src/app/core/effect/version.effect.ts index e692e6b0d..b588a6011 100644 --- a/ui/src/app/core/effect/version.effect.ts +++ b/ui/src/app/core/effect/version.effect.ts @@ -13,7 +13,7 @@ import { VersionInfo } from '../model/version'; export class VersionEffects { private endpoint = '/info'; - private base = '/actuator'; + private base = 'actuator'; @Effect() loadVersionInfo$ = this.actions$ diff --git a/ui/src/app/core/service/api-path.interceptor.spec.ts b/ui/src/app/core/service/api-path.interceptor.spec.ts new file mode 100644 index 000000000..3f1b8fc66 --- /dev/null +++ b/ui/src/app/core/service/api-path.interceptor.spec.ts @@ -0,0 +1,41 @@ +import { TestBed, async, inject } from '@angular/core/testing'; +import { HTTP_INTERCEPTORS, HttpClientModule, HttpClient, HttpRequest } from '@angular/common/http'; +import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'; + +import { ApiPathInterceptor } from './api-path.interceptor'; +import { APP_BASE_HREF } from '@angular/common'; + +describe('API Path Interceptor Service', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientModule, + HttpClientTestingModule + ], + providers: [ + { + provide: APP_BASE_HREF, + useValue: '/shibui/' + }, + { + provide: HTTP_INTERCEPTORS, + useClass: ApiPathInterceptor, + multi: true + } + ] + }); + }); + + describe('query', () => { + it(`should send an expected query request`, async(inject([HttpClient, HttpTestingController], + (service: HttpClient, backend: HttpTestingController) => { + service.get('foo').subscribe(); + + backend.expectOne((req: HttpRequest) => { + return req.url === '/shibui/foo' + && req.method === 'GET'; + }, `GET collection`); + } + ))); + }); +}); diff --git a/ui/src/app/core/service/api-path.interceptor.ts b/ui/src/app/core/service/api-path.interceptor.ts new file mode 100644 index 000000000..5417e5a7f --- /dev/null +++ b/ui/src/app/core/service/api-path.interceptor.ts @@ -0,0 +1,15 @@ +import { Injectable, Inject } from '@angular/core'; +import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { APP_BASE_HREF } from '@angular/common'; + +@Injectable() +export class ApiPathInterceptor implements HttpInterceptor { + constructor( + @Inject(APP_BASE_HREF) private baseHref: string + ) {} + intercept(req: HttpRequest, next: HttpHandler): Observable> { + const apiReq = req.clone({ url: `${this.baseHref}${req.url}` }); + return next.handle(apiReq); + } +} diff --git a/ui/src/app/core/service/user.service.ts b/ui/src/app/core/service/user.service.ts index af8907160..d5ee80b53 100644 --- a/ui/src/app/core/service/user.service.ts +++ b/ui/src/app/core/service/user.service.ts @@ -2,12 +2,12 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { User } from '../model/user'; import { HttpClient } from '@angular/common/http'; -import { catchError, map } from 'rxjs/operators'; +import { API_BASE_PATH } from '../../app.constant'; @Injectable() export class UserService { - readonly base = `/api`; + readonly base = API_BASE_PATH; constructor( private http: HttpClient diff --git a/ui/src/app/i18n/service/i18n.service.ts b/ui/src/app/i18n/service/i18n.service.ts index 4d405c5b2..26f0ba4ff 100644 --- a/ui/src/app/i18n/service/i18n.service.ts +++ b/ui/src/app/i18n/service/i18n.service.ts @@ -4,12 +4,13 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { NavigatorService } from '../../core/service/navigator.service'; import { getCurrentLanguage, getCurrentLocale } from '../../shared/util'; import { Messages } from '../model/Messages'; +import API_BASE_PATH from '../../app.constant'; @Injectable() export class I18nService { readonly path = '/messages'; - readonly base = '/api'; + readonly base = API_BASE_PATH; constructor( private http: HttpClient, diff --git a/ui/src/app/metadata/configuration/container/configuration.component.ts b/ui/src/app/metadata/configuration/container/configuration.component.ts index c1e2daf35..1704bc6a6 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.ts +++ b/ui/src/app/metadata/configuration/container/configuration.component.ts @@ -25,7 +25,6 @@ export class ConfigurationComponent implements OnDestroy { private store: Store, private routerState: ActivatedRoute ) { - this.routerState.params.pipe( takeUntil(this.ngUnsubscribe), map(({ id, type, version }) => new SetMetadata({ diff --git a/ui/src/app/metadata/configuration/service/history.service.spec.ts b/ui/src/app/metadata/configuration/service/history.service.spec.ts index e4fa72dac..8edb179a3 100644 --- a/ui/src/app/metadata/configuration/service/history.service.spec.ts +++ b/ui/src/app/metadata/configuration/service/history.service.spec.ts @@ -47,7 +47,7 @@ describe(`Attributes Service`, () => { const type = 'resource'; service.getVersion(resourceId, type).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === `/${service.base}/${PATHS[type]}/${resourceId}` + return req.url === `${service.base}/${PATHS[type]}/${resourceId}` && req.method === 'GET'; }, `GET schema by path`); } @@ -59,7 +59,7 @@ describe(`Attributes Service`, () => { const versionId = '1'; service.getVersion(resourceId, type, versionId).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === `/${service.base}/${PATHS[type]}/${resourceId}/${service.path}/${versionId}` + return req.url === `${service.base}/${PATHS[type]}/${resourceId}/${service.path}/${versionId}` && req.method === 'GET'; }, `GET schema by path`); } @@ -74,7 +74,7 @@ describe(`Attributes Service`, () => { const versionId = '1'; service.updateVersion(resourceId, type, {} as Metadata).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === `/${service.base}/${PATHS[type]}/${resourceId}` + return req.url === `${service.base}/${PATHS[type]}/${resourceId}` && req.method === 'PUT'; }, `PUT schema by path`); } diff --git a/ui/src/app/metadata/configuration/service/history.service.ts b/ui/src/app/metadata/configuration/service/history.service.ts index 9d3edc212..9e6a3d3f3 100644 --- a/ui/src/app/metadata/configuration/service/history.service.ts +++ b/ui/src/app/metadata/configuration/service/history.service.ts @@ -7,11 +7,12 @@ import { PATHS } from '../../configuration/configuration.values'; import { MetadataVersion } from '../model/version'; import { map } from 'rxjs/operators'; import { Metadata } from '../../domain/domain.type'; +import API_BASE_PATH from '../../../app.constant'; @Injectable() export class MetadataHistoryService { - readonly base = `api`; + readonly base = API_BASE_PATH; readonly path = `Versions`; constructor( @@ -19,7 +20,7 @@ export class MetadataHistoryService { ) { } query(resourceId: string, type: string): Observable { - return this.http.get(`/${this.base}/${PATHS[type]}/${resourceId}/${this.path}`).pipe( + return this.http.get(`${this.base}/${PATHS[type]}/${resourceId}/${this.path}`).pipe( map(resp => ({ versions: resp })) @@ -34,13 +35,13 @@ export class MetadataHistoryService { getVersion(resourceId: string, type: string, versionId: string = null): Observable { const api = versionId ? - `/${this.base}/${PATHS[type]}/${resourceId}/${this.path}/${versionId}` + `${this.base}/${PATHS[type]}/${resourceId}/${this.path}/${versionId}` : - `/${this.base}/${PATHS[type]}/${resourceId}`; + `${this.base}/${PATHS[type]}/${resourceId}`; return this.http.get(api); } updateVersion(resourceId: string, type: string, model: Metadata): Observable { - return this.http.put(`/${this.base}/${PATHS[type]}/${resourceId}`, model); + return this.http.put(`${this.base}/${PATHS[type]}/${resourceId}`, model); } } diff --git a/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts b/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts index 13136ddfc..5fbfbd48a 100644 --- a/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts +++ b/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts @@ -1,9 +1,10 @@ import { Wizard, WizardStep } from '../../../../wizard/model'; import { MetadataResolver } from '../metadata-resolver'; import { MetadataSourceBase } from './metadata-source-base'; +import API_BASE_PATH from '../../../../app.constant'; export class MetadataSourceEditor extends MetadataSourceBase implements Wizard { - schema = '/api/ui/MetadataSources'; + schema = `${API_BASE_PATH}/ui/MetadataSources`; steps: WizardStep[] = [ { index: 1, diff --git a/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts b/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts index 10b7c4e79..b92ea5410 100644 --- a/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts +++ b/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts @@ -1,9 +1,10 @@ import { Wizard, WizardStep } from '../../../../wizard/model'; import { MetadataResolver } from '../metadata-resolver'; import { MetadataSourceBase } from './metadata-source-base'; +import API_BASE_PATH from '../../../../app.constant'; export class MetadataSourceWizard extends MetadataSourceBase implements Wizard { - schema = '/api/ui/MetadataSources'; + schema = `${API_BASE_PATH}/ui/MetadataSources`; steps: WizardStep[] = [ { index: 1, diff --git a/ui/src/app/metadata/domain/service/attributes.service.ts b/ui/src/app/metadata/domain/service/attributes.service.ts index c6b671e01..f07cd8e83 100644 --- a/ui/src/app/metadata/domain/service/attributes.service.ts +++ b/ui/src/app/metadata/domain/service/attributes.service.ts @@ -3,6 +3,7 @@ import { HttpClient } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError, shareReplay, map } from 'rxjs/operators'; import { ReleaseAttribute } from '../model/properties/release-attribute'; +import API_BASE_PATH from '../../../app.constant'; const CACHE_SIZE = 1; @@ -10,7 +11,7 @@ const CACHE_SIZE = 1; export class AttributesService { readonly endpoint = '/customAttributes'; - readonly base = '/api'; + readonly base = API_BASE_PATH; private cache$: Observable; diff --git a/ui/src/app/metadata/domain/service/entity-id.service.ts b/ui/src/app/metadata/domain/service/entity-id.service.ts index 07009e3d1..58845dd09 100644 --- a/ui/src/app/metadata/domain/service/entity-id.service.ts +++ b/ui/src/app/metadata/domain/service/entity-id.service.ts @@ -6,13 +6,14 @@ import { map, catchError } from 'rxjs/operators'; import { QueryParams } from '../../../core/model/query'; import { MDUI } from '../model'; +import API_BASE_PATH from '../../../app.constant'; @Injectable() export class EntityIdService { readonly searchEndpoint = '/EntityIds/search'; readonly entitiesEndpoint = '/entities'; - readonly base = '/api'; + readonly base = API_BASE_PATH; constructor( private http: HttpClient diff --git a/ui/src/app/metadata/domain/service/filter.service.ts b/ui/src/app/metadata/domain/service/filter.service.ts index bb610bf01..fed08adec 100644 --- a/ui/src/app/metadata/domain/service/filter.service.ts +++ b/ui/src/app/metadata/domain/service/filter.service.ts @@ -3,13 +3,14 @@ import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { MetadataFilter } from '../../domain/model'; +import API_BASE_PATH from '../../../app.constant'; @Injectable() export class MetadataFilterService { readonly endpoint = '/MetadataResolvers'; readonly order = 'FiltersPositionOrder'; - readonly base = '/api'; + readonly base = API_BASE_PATH; readonly path = 'Filters'; constructor( diff --git a/ui/src/app/metadata/domain/service/provider.service.ts b/ui/src/app/metadata/domain/service/provider.service.ts index f17557902..b48e8876f 100644 --- a/ui/src/app/metadata/domain/service/provider.service.ts +++ b/ui/src/app/metadata/domain/service/provider.service.ts @@ -4,6 +4,7 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { MetadataProvider } from '../../domain/model'; +import API_BASE_PATH from '../../../app.constant'; @Injectable() @@ -11,7 +12,7 @@ export class MetadataProviderService { readonly endpoint = '/MetadataResolvers'; readonly order = '/MetadataResolversPositionOrder'; - readonly base = '/api'; + readonly base = API_BASE_PATH; constructor( private http: HttpClient diff --git a/ui/src/app/metadata/domain/service/resolver.service.spec.ts b/ui/src/app/metadata/domain/service/resolver.service.spec.ts index c11bb0dd5..69a00ccc0 100644 --- a/ui/src/app/metadata/domain/service/resolver.service.spec.ts +++ b/ui/src/app/metadata/domain/service/resolver.service.spec.ts @@ -2,6 +2,7 @@ import { TestBed, async, inject } from '@angular/core/testing'; import { HttpClientModule, HttpRequest } from '@angular/common/http'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { ResolverService } from './resolver.service'; +import API_BASE_PATH from '../../../app.constant'; describe(`Resolver Service`, () => { @@ -24,7 +25,7 @@ describe(`Resolver Service`, () => { service.query().subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === '/api/EntityDescriptors' + return req.url === `${API_BASE_PATH}/EntityDescriptors` && req.method === 'GET'; }, `GET EntityDescriptors collection`); } @@ -36,7 +37,7 @@ describe(`Resolver Service`, () => { expect(next).toBeTruthy(); }); - backend.expectOne('/api/EntityDescriptors').flush(['foo'], { status: 200, statusText: 'Ok' }); + backend.expectOne(`${API_BASE_PATH}/EntityDescriptors`).flush(['foo'], { status: 200, statusText: 'Ok' }); } ))); }); @@ -47,7 +48,7 @@ describe(`Resolver Service`, () => { service.queryForAdmin().subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === '/api/EntityDescriptor/disabledNonAdmin' + return req.url === `${API_BASE_PATH}/EntityDescriptor/disabledNonAdmin` && req.method === 'GET'; }, `GET EntityDescriptors collection for an admin`); } @@ -59,7 +60,7 @@ describe(`Resolver Service`, () => { expect(next).toBeTruthy(); }); - backend.expectOne('/api/EntityDescriptor/disabledNonAdmin').flush(['foo'], { status: 200, statusText: 'Ok' }); + backend.expectOne(`${API_BASE_PATH}/EntityDescriptor/disabledNonAdmin`).flush(['foo'], { status: 200, statusText: 'Ok' }); } ))); }); @@ -72,7 +73,7 @@ describe(`Resolver Service`, () => { service.find(id).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === `/api/EntityDescriptor/${id}` + return req.url === `${API_BASE_PATH}/EntityDescriptor/${id}` && req.method === 'GET'; }, `GET EntityDescriptor by id`); } @@ -89,7 +90,7 @@ describe(`Resolver Service`, () => { service.update({id, serviceProviderName, createdBy}).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === `/api/EntityDescriptor/${id}` + return req.url === `${ API_BASE_PATH }/EntityDescriptor/${id}` && req.method === 'PUT'; }, `PUT EntityDescriptor by id`); } @@ -106,7 +107,7 @@ describe(`Resolver Service`, () => { service.save({ id, serviceProviderName, createdBy }).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === `/api/EntityDescriptor` + return req.url === `api/EntityDescriptor` && req.method === 'POST'; }, `POST new EntityDescriptor`); } @@ -123,7 +124,7 @@ describe(`Resolver Service`, () => { service.remove({ id, serviceProviderName, createdBy }).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === `/api/EntityDescriptor/${id}` + return req.url === `api/EntityDescriptor/${id}` && req.method === 'DELETE'; }, `DELETE an EntityDescriptor`); } @@ -138,7 +139,7 @@ describe(`Resolver Service`, () => { service.preview(id).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === `/api/EntityDescriptor/${id}` + return req.url === `api/EntityDescriptor/${id}` && req.method === 'GET' && req.headers.get('Accept') === 'application/xml' && req.responseType === 'text'; @@ -154,7 +155,7 @@ describe(`Resolver Service`, () => { service.upload(name, xml).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === `/api/EntityDescriptor` + return req.url === `api/EntityDescriptor` && req.method === 'POST' && req.headers.get('Content-Type') === 'application/xml'; }, `POST new EntityDescriptor`); @@ -169,7 +170,7 @@ describe(`Resolver Service`, () => { service.createFromUrl(name, url).subscribe(); backend.expectOne((req: HttpRequest) => { - return req.url === `/api/EntityDescriptor` + return req.url === `api/EntityDescriptor` && req.method === 'POST' && req.headers.get('Content-Type') === 'application/x-www-form-urlencoded' && req.body === `metadataUrl=${url}`; diff --git a/ui/src/app/metadata/domain/service/resolver.service.ts b/ui/src/app/metadata/domain/service/resolver.service.ts index cae3a5ed8..4db8ab41f 100644 --- a/ui/src/app/metadata/domain/service/resolver.service.ts +++ b/ui/src/app/metadata/domain/service/resolver.service.ts @@ -3,12 +3,13 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Observable, throwError, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { MetadataResolver } from '../model'; +import API_BASE_PATH from '../../../app.constant'; @Injectable() export class ResolverService { private endpoint = '/EntityDescriptor'; - private base = '/api'; + private base = API_BASE_PATH; constructor( private http: HttpClient diff --git a/ui/src/app/metadata/filter/model/entity-attributes.filter.ts b/ui/src/app/metadata/filter/model/entity-attributes.filter.ts index a4b18b67a..b310e6a85 100644 --- a/ui/src/app/metadata/filter/model/entity-attributes.filter.ts +++ b/ui/src/app/metadata/filter/model/entity-attributes.filter.ts @@ -5,13 +5,14 @@ import { EntityAttributesFilterEntity } from '../../domain/entity'; import { RegexValidator } from '../../../shared/validation/regex.validator'; import { getFilterNames } from '../reducer'; import { memoize } from '../../../shared/memo'; +import API_BASE_PATH from '../../../app.constant'; const checkRegex = memoize(RegexValidator.isValidRegex); export const EntityAttributesFilter: FormDefinition = { label: 'EntityAttributes', type: 'EntityAttributes', - schema: '/api/ui/EntityAttributesFilters', + schema: `${API_BASE_PATH}/ui/EntityAttributesFilters`, getEntity(filter: MetadataFilter): EntityAttributesFilterEntity { return new EntityAttributesFilterEntity(filter); }, diff --git a/ui/src/app/metadata/filter/model/nameid.filter.ts b/ui/src/app/metadata/filter/model/nameid.filter.ts index ee48f4f89..a095513ea 100644 --- a/ui/src/app/metadata/filter/model/nameid.filter.ts +++ b/ui/src/app/metadata/filter/model/nameid.filter.ts @@ -4,13 +4,14 @@ import { NameIDFormatFilterEntity } from '../../domain/entity/filter/nameid-form import { RegexValidator } from '../../../shared/validation/regex.validator'; import { getFilterNames } from '../reducer'; import { memoize } from '../../../shared/memo'; +import API_BASE_PATH from '../../../app.constant'; const checkRegex = memoize(RegexValidator.isValidRegex); export const NameIDFilter: FormDefinition = { label: 'NameIDFormat', type: 'NameIDFormat', - schema: '/api/ui/NameIdFormatFilter', + schema: `${API_BASE_PATH}/ui/NameIdFormatFilter`, getEntity(filter: MetadataFilter): NameIDFormatFilterEntity { return new NameIDFormatFilterEntity(filter); }, diff --git a/ui/src/app/metadata/provider/effect/collection.effect.ts b/ui/src/app/metadata/provider/effect/collection.effect.ts index 70aff3257..1eb9e5093 100644 --- a/ui/src/app/metadata/provider/effect/collection.effect.ts +++ b/ui/src/app/metadata/provider/effect/collection.effect.ts @@ -115,7 +115,6 @@ export class CollectionEffects { map(action => action.payload), withLatestFrom(this.store.select(fromI18n.getMessages)), map(([error, messages]) => { - console.log(error); let message = `${error.errorCode}: ${this.i18nService.translate(error.errorMessage, null, messages)}`; message = error.cause ? `${message} - ${error.cause}` : message; return new AddNotification( diff --git a/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts b/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts index 395bf56e1..ac09ed7ac 100644 --- a/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts +++ b/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts @@ -4,6 +4,7 @@ import { BaseMetadataProviderEditor } from './base.provider.form'; import { metadataFilterProcessor } from './utilities'; import RegexValidator from '../../../shared/validation/regex.validator'; import { memoize } from '../../../shared/memo'; +import API_BASE_PATH from '../../../app.constant'; const checkRegex = memoize(RegexValidator.isValidRegex); @@ -83,7 +84,7 @@ export const DynamicHttpMetadataProviderWizard: Wizard = { ...BaseMetadataProviderEditor, label: 'FilesystemMetadataProvider', type: 'FilesystemMetadataResolver', - schema: '/api/ui/MetadataResolver/FilesystemMetadataResolver', + schema: `${API_BASE_PATH}/ui/MetadataResolver/FilesystemMetadataResolver`, steps: [ { id: 'common', diff --git a/ui/src/app/metadata/provider/model/local-dynamic.provider.form.ts b/ui/src/app/metadata/provider/model/local-dynamic.provider.form.ts index c79bc487d..6fdba5270 100644 --- a/ui/src/app/metadata/provider/model/local-dynamic.provider.form.ts +++ b/ui/src/app/metadata/provider/model/local-dynamic.provider.form.ts @@ -1,13 +1,13 @@ import { Wizard } from '../../../wizard/model'; import { LocalDynamicMetadataProvider } from '../../domain/model/providers/local-dynamic-metadata-provider'; import { BaseMetadataProviderEditor } from './base.provider.form'; -import { MetadataProvider } from '../../domain/model'; +import API_BASE_PATH from '../../../app.constant'; export const LocalDynamicMetadataProviderWizard: Wizard = { ...BaseMetadataProviderEditor, label: 'LocalDynamicMetadataProvider', type: 'LocalDynamicMetadataResolver', - schema: '/api/ui/MetadataResolver/LocalDynamicMetadataResolver', + schema: `${API_BASE_PATH}/ui/MetadataResolver/LocalDynamicMetadataResolver`, steps: [ { id: 'common', diff --git a/ui/src/assets/schema/provider/metadata-provider.schema.json b/ui/src/assets/schema/provider/metadata-provider.schema.json index a0242b633..5849ec8d8 100644 --- a/ui/src/assets/schema/provider/metadata-provider.schema.json +++ b/ui/src/assets/schema/provider/metadata-provider.schema.json @@ -21,7 +21,7 @@ "type": "string", "widget": { "id": "select", - "dataUrl": "/api/ui/MetadataResolver/types" + "dataUrl": "api/ui/MetadataResolver/types" } } }, diff --git a/ui/src/testing/attributes.stub.ts b/ui/src/testing/attributes.stub.ts index 200bf8ade..d2d6f9575 100644 --- a/ui/src/testing/attributes.stub.ts +++ b/ui/src/testing/attributes.stub.ts @@ -6,7 +6,7 @@ import { ReleaseAttribute } from '../app/metadata/domain/model/properties/releas export class MockAttributeService { readonly path = '/customAttributes'; - readonly base = '/api'; + readonly base = 'api'; constructor() { } diff --git a/ui/src/testing/i18n.stub.ts b/ui/src/testing/i18n.stub.ts index 3b38bef8c..4f0f012b3 100644 --- a/ui/src/testing/i18n.stub.ts +++ b/ui/src/testing/i18n.stub.ts @@ -49,7 +49,7 @@ export class MockTranslateComponent { export class MockI18nService { readonly path = '/messages'; - readonly base = '/api'; + readonly base = 'api'; constructor() { } diff --git a/ui/src/testing/mockMetadataWizard.ts b/ui/src/testing/mockMetadataWizard.ts index 6c0135e29..95844f8a0 100644 --- a/ui/src/testing/mockMetadataWizard.ts +++ b/ui/src/testing/mockMetadataWizard.ts @@ -23,7 +23,7 @@ export const MockMetadataWizard: Wizard = { getValidators(): { [key: string]: any } { return {}; }, - schema: '/api/ui/MetadataSources', + schema: 'api/ui/MetadataSources', steps: [ { index: 1,