feat: create default organization in wizard
MODIFY
src/app/components/marketplace-api-details/marketplace-api-details.component.ts
src/app/components/marketplace-api-details/marketplace-api-details.component.ts
@@ -36,7 +36,7 @@ export class MarketplaceApiDetailsComponent implements OnInit {
}
getApiVersions(): void {
- this.spinnerService.startWaiting()
+ this.spinnerService.startWaiting();
const orgId = this.route.snapshot.paramMap.get('orgId')!;
const apiId = this.route.snapshot.paramMap.get('apiId')!;
this.apiService
@@ -66,7 +66,7 @@ export class MarketplaceApiDetailsComponent implements OnInit {
)
.subscribe((apiVersions) => {
this.spinnerService.stopWaiting();
- this.apis = apiVersions
+ this.apis = apiVersions;
});
}
}

MODIFY
src/app/components/marketplace-client-app/marketplace-client-app.component.ts
src/app/components/marketplace-client-app/marketplace-client-app.component.ts
@@ -2,15 +2,20 @@ import { Component, EventEmitter, Output, OnInit } from '@angular/core';
import {BackendService} from '../../services/backend/backend.service';
import {MatTableDataSource} from '@angular/material/table';
import {
+ IClient,
IClientSummary,
- IOrganizationSummary,
+ IOrganizationSummary
} from '../../interfaces/ICommunication';
import {SnackbarService} from '../../services/snackbar/snackbar.service';
+import {HttpErrorResponse} from '@angular/common/http';
+import {catchError, switchMap} from 'rxjs/operators';
+import {forkJoin, of} from 'rxjs';
+import {TranslateService} from '@ngx-translate/core';
@Component({
selector: 'app-marketplace-client-app',
templateUrl: './marketplace-client-app.component.html',
- styleUrls: ['./marketplace-client-app.component.scss'],
+ styleUrls: ['./marketplace-client-app.component.scss']
})
export class MarketplaceClientAppComponent implements OnInit {
displayedColumns: string[] = ['name'];
@@ -24,11 +29,12 @@ export class MarketplaceClientAppComponent implements OnInit {
constructor(
private backend: BackendService,
- private snackbar: SnackbarService
+ private snackbar: SnackbarService,
+ private translator: TranslateService
) {}
ngOnInit(): void {
- this.loadClients();
+ this.createOrgAndLoadClients();
}
public selectClient(client: IClientSummary): void {
@@ -41,50 +47,89 @@ export class MarketplaceClientAppComponent implements OnInit {
}
/**
- * Add a new client
+ * Add a new client and refresh table after that
*/
public addClient(): void {
const orgId: string =
this.organizations.length > 1
? this.organizationId
: this.organizations[0].id;
- this.backend.createClient(orgId, this.clientName).subscribe(
- () => this.loadClients(),
- (error) => this.snackbar.showErrorSnackBar(error.message, error)
+ this.backend.createClient(orgId, this.clientName)
+ .pipe(
+ switchMap((client: IClient) => {
+ this.snackbar.showPrimarySnackBar(
+ this.translator.instant('COMMON.SUCCESS')
+ );
+ return this.loadClients();
+ })
+ )
+ .subscribe(
+ (results: [IOrganizationSummary[], IClientSummary[]]) => {
+ this.createTableView(results);
+ },
+ (error) => {
+ this.snackbar.showErrorSnackBar(error);
+ }
);
}
+ public applyFilter(event: Event): void {
+ this.clickedRows.clear();
+ // https://material.angular.io/components/table/examples
+ const filterValue = (event.target as HTMLInputElement).value;
+ this.dataSource.filter = filterValue.trim().toLowerCase();
+ }
+
/**
- * Refresh list of clients
+ * Try to create a user organization every time.
+ * Refactor this later to accept org-names
* @private
*/
- private loadClients(): void {
- this.backend.getClientOrgs().subscribe(
- (orgs: IOrganizationSummary[]) => {
- this.organizations = orgs;
- if (orgs.length > 1) {
- this.displayedColumns = ['org-name', 'name'];
+ private createOrgAndLoadClients() {
+ this.backend
+ .createOrganization()
+ .pipe(
+ catchError((err: HttpErrorResponse) => {
+ if (err.status !== 409) {
+ console.error(err);
}
+ return of({});
+ }),
+ switchMap(() => {
+ return this.loadClients();
+ })
+ )
+ .subscribe(
+ (results: [IOrganizationSummary[], IClientSummary[]]) => {
+ this.createTableView(results);
},
- (error) => this.snackbar.showErrorSnackBar(error.message, error)
+ (error) => {
+ console.warn(error);
+ this.snackbar.showErrorSnackBar(
+ this.translator.instant('COMMON.ERROR')
+ );
+ }
);
+ }
+
+ private loadClients() {
+ return forkJoin([
+ this.backend.getClientOrgs(),
+ this.backend.getEditableClients()
+ ]);
+ }
- this.backend.getEditableClients().subscribe(
- (clients: IClientSummary[]) => {
- this.dataSource = new MatTableDataSource(clients);
+ private createTableView(results: [IOrganizationSummary[], IClientSummary[]]) {
+ const organizations = results[0];
+ const clients = results[1];
+ this.organizations = results[0];
+ if (organizations.length > 1) {
+ this.displayedColumns = ['org-name', 'name'];
+ }
+ this.dataSource = new MatTableDataSource(results[1]);
if (clients.length === 1) {
// if we only have one client we can select it automatically
this.selectClient(clients[0]);
}
- },
- (error) => this.snackbar.showErrorSnackBar(error.message)
- );
- }
-
- public applyFilter(event: Event): void {
- this.clickedRows.clear();
- // https://material.angular.io/components/table/examples
- const filterValue = (event.target as HTMLInputElement).value;
- this.dataSource.filter = filterValue.trim().toLowerCase();
}
}

MODIFY
src/app/components/marketplace-signup-stepper/marketplace-signup-stepper.component.ts
src/app/components/marketplace-signup-stepper/marketplace-signup-stepper.component.ts
@@ -14,8 +14,8 @@ import { ISignUpInfo } from '../../interfaces/ISignUpInfo';
import { BackendService } from '../../services/backend/backend.service';
import { MatStepper } from '@angular/material/stepper';
import { IContractExt } from '../../interfaces/IContractExt';
-import {map, switchMap} from "rxjs/operators";
-import {TocService} from "../../services/toc.service";
+import { map, switchMap } from 'rxjs/operators';
+import { TocService } from '../../services/toc.service';
@Component({
selector: 'app-marketplace-signup-stepper',

MODIFY
src/app/services/backend/backend.service.ts
src/app/services/backend/backend.service.ts
@@ -2,36 +2,34 @@ import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ConfigService } from '../config/config.service';
-import { KeycloakService } from 'keycloak-angular';
import {
IApi, IApiPlanSummary,
IApiVersion,
IApiVersionEndpointSummary,
- IApiVersionSummary, IClientSummary, IClientVersionSummary,
+ IApiVersionSummary, IClient, IClientSummary, IClientVersionSummary,
IContract,
IContractSummary,
- INewContract,
+ INewContract, INewOrganization,
IOrganization,
IOrganizationSummary, IPolicy, IPolicySummary,
ISearchCriteria,
ISearchResultsApiSummary,
} from '../../interfaces/ICommunication';
-import {IPolicySummaryExt} from "../../interfaces/IPolicySummaryExt";
+import { IPolicySummaryExt } from '../../interfaces/IPolicySummaryExt';
+import {KeycloakHelperService} from '../keycloak-helper/keycloak-helper.service';
@Injectable({
providedIn: 'root',
})
export class BackendService {
private readonly endpoint: string;
- userName: string;
constructor(
private http: HttpClient,
private configService: ConfigService,
- private keycloak: KeycloakService
+ private keycloakHelper: KeycloakHelperService
) {
this.endpoint = configService.getEndpoint();
- this.userName = this.keycloak.getKeycloakInstance().profile?.username!;
}
private httpOptions = {
@@ -92,7 +90,7 @@ export class BackendService {
}
public getClientOrgs(): Observable<Array<IOrganizationSummary>> {
- const username = this.keycloak.getKeycloakInstance().profile?.username;
+ const username = this.keycloakHelper.getUsername();
const path = `users/${username}/clientorgs`;
return this.http.get(this.generateUrl(path)) as Observable<
Array<IOrganizationSummary>
@@ -102,13 +100,13 @@ export class BackendService {
public createClient(
orgId: string,
clientName: string
- ): Observable<IOrganization> {
+ ): Observable<IClient> {
const path = `organizations/${orgId}/clients`;
return this.http.post(this.generateUrl(path), {
name: clientName,
initialVersion: '1.0',
description: '',
- }) as Observable<IOrganization>;
+ }) as Observable<IClient>;
}
public createContract(
@@ -124,8 +122,18 @@ export class BackendService {
) as Observable<IContract>;
}
+ public createOrganization(): Observable<IOrganization> {
+ const org: INewOrganization = {
+ name: this.keycloakHelper.getUsername(),
+ description: ''
+ };
+ const path = '/devportal/organizations';
+ return this.http.post<IOrganization>(this.generateUrl(path), org);
+ }
+
public getEditableClients(): Observable<IClientSummary[]> {
- const path = `users/${this.userName}/editable-clients`;
+ const username = this.keycloakHelper.getUsername();
+ const path = `users/${username}/editable-clients`;
return this.http.get<IClientSummary[]>(this.generateUrl(path));
}
@@ -152,7 +160,7 @@ export class BackendService {
versionName: string,
contractId: number
): Observable<IContract> {
- const path = `/organizations/${organizationId}/clients/${clientId}/versions/${versionName}/contracts/${contractId}`;
+ const path = `organizations/${organizationId}/clients/${clientId}/versions/${versionName}/contracts/${contractId}`;
return this.http.get<IContract>(this.generateUrl(path)) ;
}
@@ -161,7 +169,7 @@ export class BackendService {
apiId: string,
versionName: string
): Observable<IApiVersionEndpointSummary> {
- const path = `/organizations/${organizationId}/apis/${apiId}/versions/${versionName}/endpoint`;
+ const path = `organizations/${organizationId}/apis/${apiId}/versions/${versionName}/endpoint`;
return this.http.get<IApiVersionEndpointSummary>(
this.generateUrl(path)
);
@@ -170,14 +178,14 @@ export class BackendService {
public getApiVersionPlans(organizationId: string,
apiId: string,
apiVersion: string): Observable<IApiPlanSummary[]> {
- const path = `/organizations/${organizationId}/apis/${apiId}/versions/${apiVersion}/plans`
+ const path = `organizations/${organizationId}/apis/${apiId}/versions/${apiVersion}/plans`
return this.http.get<IApiPlanSummary[]>(this.generateUrl(path));
}
public getPlanPolicySummaries(organizationId: string,
planId: string,
planVersion: string): Observable<IPolicySummaryExt[]> {
- const path = `/organizations/${organizationId}/plans/${planId}/versions/${planVersion}/policies`
+ const path = `organizations/${organizationId}/plans/${planId}/versions/${planVersion}/policies`
return this.http.get<IPolicySummaryExt[]>(this.generateUrl(path))
}
@@ -185,14 +193,14 @@ export class BackendService {
planId: string,
planVersion: string,
policyId: string): Observable<IPolicy> {
- const path = `/organizations/${organizationId}/plans/${planId}/versions/${planVersion}/policies/${policyId}`
+ const path = `organizations/${organizationId}/plans/${planId}/versions/${planVersion}/policies/${policyId}`
return this.http.get<IPolicy>(this.generateUrl(path))
}
public getApiPolicySummaries(organizationId: string,
apiId: string,
apiVersion: string): Observable<IPolicySummaryExt[]> {
- const path = `/organizations/${organizationId}/apis/${apiId}/versions/${apiVersion}/policies`
+ const path = `organizations/${organizationId}/apis/${apiId}/versions/${apiVersion}/policies`
return this.http.get<IPolicySummary[]>(this.generateUrl(path)) as Observable<IPolicySummaryExt[]>
}
@@ -200,7 +208,7 @@ export class BackendService {
apiId: string,
apiVersion: string,
policyId: string): Observable<IPolicy> {
- const path = `/organizations/${organizationId}/apis/${apiId}/versions/${apiVersion}/policies/${policyId}`
+ const path = `organizations/${organizationId}/apis/${apiId}/versions/${apiVersion}/policies/${policyId}`
return this.http.get<IPolicy>(this.generateUrl(path))
}

MODIFY
src/app/services/keycloak-helper/keycloak-helper.service.ts
src/app/services/keycloak-helper/keycloak-helper.service.ts
@@ -2,7 +2,6 @@ import { Injectable } from '@angular/core';
import { KeycloakService } from 'keycloak-angular';
import { KeycloakInstance } from 'keycloak-js';
import { ConfigService } from '../config/config.service';
-import {Router} from '@angular/router';
@Injectable({
providedIn: 'root',
@@ -11,10 +10,12 @@ import {Router} from '@angular/router';
export class KeycloakHelperService {
private isLoggedIn = false;
private executed = false;
+ private username?: string;
constructor(
private readonly keycloak: KeycloakService,
- private configService: ConfigService) { }
+ private configService: ConfigService
+ ) {}
/**
* Init keycloak setting, this is called via APP Initializer
@@ -102,4 +103,11 @@ export class KeycloakHelperService {
window.sessionStorage.removeItem('KEYCLOAK_SESSION_STORAGE_TOKEN');
window.sessionStorage.removeItem('KEYCLOAK_SESSION_STORAGE_REFRESH_TOKEN');
}
+
+ public getUsername() {
+ if (!this.username) {
+ this.username = this.keycloak.getUsername();
+ }
+ return this.username;
+ }
}

MODIFY
src/assets/i18n/en.json
src/assets/i18n/en.json
"QUERY_PARAMETER": "As HTTP Query Parameter",
"HTTP_HEADER": "As HTTP Request Header",
"API_DOCS": "API Documentation",
- "ERROR": "Data could not be loaded, please try again later."
+ "ERROR": "Data could not be loaded, please try again later.",
+ "SUCCESS": "Successfully created!"
},
"HOME": {
"TITLE": "KoolBank Api Developer Portal",

feat: create default organization in wizard