refactor request chain of contracts, plans and policies
MODIFY
src/app/components/marketplace-signup-stepper/marketplace-signup-stepper.component.ts
src/app/components/marketplace-signup-stepper/marketplace-signup-stepper.component.ts
@@ -14,6 +14,7 @@ import { SignUpService } from '../../services/sign-up/sign-up.service';
import { ISignUpInfo } from '../../interfaces/ISignUpInfo';
import { BackendService } from '../../services/backend/backend.service';
import { MatStepper } from '@angular/material/stepper';
+import {IContractExt} from "../../interfaces/IContractExt";
@Component({
selector: 'app-marketplace-signup-stepper',
@@ -25,7 +26,7 @@ export class MarketplaceSignupStepperComponent implements OnInit {
agreedTermsAndPrivacy: boolean | undefined;
termsEnabled: boolean;
newContractDetails: ISignUpInfo;
- contract: IContract | undefined;
+ contract: IContractExt | undefined;
policies: IPolicy[] = [];
constructor(
@@ -105,10 +106,12 @@ export class MarketplaceSignupStepperComponent implements OnInit {
.createContract(client.organizationId, client.id, '1.0', contract)
.subscribe(
(contract: IContract) => {
+
this.snackbar.showPrimarySnackBar(
this.translator.instant('WIZARD.SUCCESS')
);
- this.contract = contract;
+ this.contract = contract as IContractExt;
+
stepper.next();
},
(error) => this.snackbar.showErrorSnackBar(error.message, error)

MODIFY
src/app/components/my-apps-summary/my-apps-summary.component.html
src/app/components/my-apps-summary/my-apps-summary.component.html
</div>
<div>
<mat-form-field>
- <input matInput readonly value="{{managedEndpoint}}" />
+ <input matInput readonly value="{{contract!.managedEndpoint}}" />
<button
[cdkCopyToClipboard]="'test'"
mat-icon-button

MODIFY
src/app/components/my-apps-summary/my-apps-summary.component.ts
src/app/components/my-apps-summary/my-apps-summary.component.ts
import { Component, Input, OnInit } from '@angular/core';
import {IContractExt} from '../../interfaces/IContractExt';
-import {IApiVersionEndpointSummary} from "../../interfaces/ICommunication";
-import {BackendService} from "../../services/backend/backend.service";
-import {SnackbarService} from "../../services/snackbar/snackbar.service";
@Component({
selector: 'app-my-apps-summary',
@@ -11,24 +8,7 @@ import {SnackbarService} from "../../services/snackbar/snackbar.service";
})
export class MyAppsSummaryComponent implements OnInit {
@Input() contract?: IContractExt;
- managedEndpoint: string = '';
- constructor(private backendService: BackendService,
- private snackbarService: SnackbarService) {}
+ constructor() {}
- ngOnInit(): void {
- if (this.contract) {
- this.backendService
- .getManagedApiEndpoint(
- this.contract.api.api.organization.id,
- this.contract.api.api.id,
- this.contract.api.version
- )
- .subscribe(
- (endpoint: IApiVersionEndpointSummary) => {
- this.managedEndpoint = endpoint.managedEndpoint;
- },
- (error) => this.snackbarService.showErrorSnackBar(error.message, error)
- );
- }
- }
+ ngOnInit(): void {}
}

MODIFY
src/app/components/my-apps-use-api/my-apps-use-api.component.ts
src/app/components/my-apps-use-api/my-apps-use-api.component.ts
import { Component, Input, OnChanges, OnInit } from '@angular/core';
-import {
- IApiVersionEndpointSummary,
- IContract,
-} from '../../interfaces/ICommunication';
import { BackendService } from '../../services/backend/backend.service';
import { SnackbarService } from '../../services/snackbar/snackbar.service';
import {ISignUpInfo} from "../../interfaces/ISignUpInfo";
import {SignUpService} from "../../services/sign-up/sign-up.service";
+import {IContractExt} from "../../interfaces/IContractExt";
@Component({
selector: 'app-my-apps-use-api',
@@ -14,7 +11,7 @@ import {SignUpService} from "../../services/sign-up/sign-up.service";
styleUrls: ['./my-apps-use-api.component.scss'],
})
export class MyAppsUseApiComponent implements OnInit, OnChanges {
- @Input() contract?: IContract;
+ @Input() contract?: IContractExt;
newContractDetails: ISignUpInfo;
mockText =
@@ -54,18 +51,7 @@ export class MyAppsUseApiComponent implements OnInit, OnChanges {
if (this.contract) {
this.disableButtons = false;
this.apiKey = this.contract!.client.apikey;
- this.backend
- .getManagedApiEndpoint(
- this.contract.api.api.organization.id,
- this.contract.api.api.id,
- this.contract.api.version
- )
- .subscribe(
- (endpoint: IApiVersionEndpointSummary) => {
- this.endpoint = endpoint.managedEndpoint;
- },
- (error) => this.snackbar.showErrorSnackBar(error.message, error)
- );
+ this.endpoint = this.contract.managedEndpoint;
} else {
this.disableButtons = true;
this.apiKey = this.previewText.apiKey;

MODIFY
src/app/components/my-apps/my-apps.component.ts
src/app/components/my-apps/my-apps.component.ts
@@ -14,10 +14,11 @@ import {
IContract,
IContractSummary,
} from '../../interfaces/ICommunication';
-import { flatMap } from 'rxjs/internal/operators';
import { IContractExt } from '../../interfaces/IContractExt';
import {PolicyService} from "../../services/policy/policy.service";
-import {IPolicyExt} from "../../interfaces/IPolicyExt";
+import {map, switchMap} from "rxjs/operators";
+import {forkJoin} from "rxjs";
+import {flatArray} from "../../shared/utility";
@Component({
selector: 'app-my-apps',
@@ -43,45 +44,62 @@ export class MyAppsComponent implements OnInit {
this.setUpHero();
this.fetchContracts();
}
-
+ // Detailed explanation of request chain: https://stackoverflow.com/questions/69421293/how-to-chain-requests-correctly-with-rxjs
private fetchContracts() {
this.backend
.getEditableClients()
.pipe(
- flatMap((clients: IClientSummaryBean[]) => {
- return clients;
- }),
- flatMap((clientSum: IClientSummaryBean) => {
+ switchMap((clientSummaries: IClientSummaryBean[]) => {
+ return forkJoin(clientSummaries.map(clientSummary => {
return this.backend.getClientVersions(
- clientSum.organizationId,
- clientSum.id
+ clientSummary.organizationId,
+ clientSummary.id
);
+ }))
}),
- flatMap((versions: IClientVersionSummaryBean[]) => {
- return versions;
- }),
- flatMap((versionSum: IClientVersionSummaryBean) => {
+ switchMap((nestedClientVersionSummaries: IClientVersionSummaryBean[][])=> {
+ const clientVersionSummaries: IClientVersionSummaryBean[] = flatArray(nestedClientVersionSummaries)
+ return forkJoin(clientVersionSummaries.map(clientVersionSummary => {
return this.backend.getContracts(
- versionSum.organizationId,
- versionSum.id,
- versionSum.version
+ clientVersionSummary.organizationId,
+ clientVersionSummary.id,
+ clientVersionSummary.version
+ );
+ })
);
}),
- flatMap((contracts: IContractSummary[]) => {
- return contracts;
- }),
- flatMap((contractSum: IContractSummary) => {
+ switchMap((nestedContractSummaries: IContractSummary[][]) => {
+ const contractSummaries: IContractSummary[] = flatArray(nestedContractSummaries)
+ return forkJoin(contractSummaries.map(contractSummary => {
return this.backend.getContract(
- contractSum.clientOrganizationId,
- contractSum.clientId,
- contractSum.clientVersion,
- contractSum.contractId
+ contractSummary.clientOrganizationId,
+ contractSummary.clientId,
+ contractSummary.clientVersion,
+ contractSummary.contractId
);
+ }))
+ }),
+ switchMap((contracts: IContract[]) => {
+ return forkJoin(contracts.map(contract => {
+ const orgId = contract.plan.plan.organization.id;
+ return forkJoin([
+ this.policyService.getPlanPolicies(orgId, contract.plan.plan.id, contract.plan.version),
+ this.policyService.getApiPolicies(orgId, contract.api.api.id, contract.api.version),
+ this.backend.getManagedApiEndpoint(orgId, contract.api.api.id, contract.api.version)
+ ]).pipe(
+ map(([planPolicies, apiPolicies,endpoint]) => {
+ return {
+ ...contract,
+ policies: planPolicies.concat(apiPolicies),
+ section: 'summary',
+ managedEndpoint: endpoint.managedEndpoint
+ } as IContractExt;
})
)
- .subscribe((contracts: IContract[]) => {
- const extended = contracts as IContractExt[];
- this.extractContracts(extended);
+ }))
+ })
+ ).subscribe((test) => {
+ this.extractContracts(test)
});
}
@@ -100,8 +118,6 @@ export class MyAppsComponent implements OnInit {
clientNameVersionMapped
);
- this.extendContract(contract);
-
if (foundContracts) {
const contractIfPartOfArray = foundContracts.find((c: IContractExt) => {
return c.id === contract.id;
@@ -117,25 +133,10 @@ export class MyAppsComponent implements OnInit {
);
} else {
this.clientContractsMap.set(clientNameVersionMapped, [contract]);
- console.log('test')
}
});
}
-
-
- /**
- * Extends the ContractBean object with additional values
- * @param contract
- * @private
- */
- private extendContract(contract: IContract): IContractExt {
- const contractExt = contract as IContractExt;
- contractExt.section = 'summary';
- contractExt.policies = this.getPolicies(contractExt);
- return contractExt;
- }
-
private setUpHero() {
this.heroService.setUpHero({
title: this.translator.instant('APPS.TITLE'),
@@ -143,21 +144,6 @@ export class MyAppsComponent implements OnInit {
});
}
- private getPolicies(extendedContract: IContractExt): IPolicyExt[] {
- const policies: IPolicyExt[] = [];
- this.policyService.getPlanPolicies(extendedContract.plan.plan.organization.id,
- extendedContract.plan.plan.id,
- extendedContract.plan.version).subscribe((extendedPolicy: IPolicyExt) => {
- policies.push(extendedPolicy);
- });
- this.policyService.getApiPolicies(extendedContract.api.api.organization.id,
- extendedContract.api.api.id,
- extendedContract.api.version).subscribe((extendedPolicy: IPolicyExt) => {
- policies.push(extendedPolicy);
- })
- return policies;
- }
-
setSection(api: any, sectionName: ISection) {
api.section = api.section === sectionName ? api.section : sectionName;
}

MODIFY
src/app/components/plan-card-list/plan-card-list.component.ts
src/app/components/plan-card-list/plan-card-list.component.ts
@@ -4,8 +4,10 @@ import { ActivatedRoute, Router } from "@angular/router";
import { SignUpService } from "../../services/sign-up/sign-up.service";
import {IApiPlanSummary, IApiVersion} from "../../interfaces/ICommunication";
import {IPolicyExt} from "../../interfaces/IPolicyExt";
-import {flatMap} from "rxjs/internal/operators";
import {PolicyService} from "../../services/policy/policy.service";
+import {switchMap} from "rxjs/operators";
+import {forkJoin} from "rxjs";
+import {flatArray} from "../../shared/utility";
@Component({
selector: 'app-plan-card-list',
@@ -52,28 +54,30 @@ export class PlanCardListComponent implements OnInit {
private fetchPlansAndPolicies(): void {
this.planService.getPlans(this.orgId, this.apiId, this.apiVersion.version).pipe(
- flatMap((apiPlanSummaries: IApiPlanSummary[]) => {
+ switchMap((apiPlanSummaries: IApiPlanSummary[]) => {
this.plans = apiPlanSummaries;
- return apiPlanSummaries
- }),
- flatMap((apiPlanSummary: IApiPlanSummary) => {
+ return forkJoin(apiPlanSummaries.map(apiPlanSummary => {
return this.policyService.getPlanPolicies(this.orgId, apiPlanSummary.planId, apiPlanSummary.version)
+ }))
})
- ).subscribe((extendedPolicy: IPolicyExt) => {
- this.extractPolicy(extendedPolicy);
+ ).subscribe((nestedExtendedPolicies: IPolicyExt[][]) => {
+ const extendedPolicies: IPolicyExt[] = flatArray(nestedExtendedPolicies);
+ this.extractPolicies(extendedPolicies);
});
- this.policyService.getApiPolicies(this.orgId, this.apiId, this.apiVersion.version).subscribe((extendedApiPolicy: IPolicyExt) => {
- this.apiPolicies.push(extendedApiPolicy);
+ this.policyService.getApiPolicies(this.orgId, this.apiId, this.apiVersion.version).subscribe((extendedApiPolicies: IPolicyExt[]) => {
+ this.apiPolicies = this.apiPolicies.concat(extendedApiPolicies);
})
}
- private extractPolicy(policyExt: IPolicyExt) {
- const planIdVersionMapped = policyExt.planId + ':' + policyExt.planVersion;
+ private extractPolicies(extendedPolicies: IPolicyExt[]) {
+ extendedPolicies.forEach(extendedPolicy => {
+ const planIdVersionMapped = extendedPolicy.planId + ':' + extendedPolicy.planVersion;
const foundPolicies = this.planPoliciesMap.get(planIdVersionMapped);
if (foundPolicies) {
- this.planPoliciesMap.set(planIdVersionMapped, foundPolicies.concat(policyExt))
+ this.planPoliciesMap.set(planIdVersionMapped, foundPolicies.concat(extendedPolicy))
} else {
- this.planPoliciesMap.set(planIdVersionMapped, [policyExt]);
+ this.planPoliciesMap.set(planIdVersionMapped, [extendedPolicy]);
}
+ })
}
}

MODIFY
src/app/components/policy-card/policy-card.component.ts
src/app/components/policy-card/policy-card.component.ts
import { Component, Input, OnInit } from '@angular/core';
import {IPolicyExt} from "../../interfaces/IPolicyExt";
+import {formatBytes} from "../../shared/utility";
export interface PolicyHeaders {
headerLimit: string;
@@ -60,7 +61,7 @@ export class PolicyCardComponent implements OnInit {
this.checkHeaders(config, this.transferQuotaPolicyHeaders);
this.icon = 'import_export';
this.policyIdentifier = 'QUOTA';
- limit = this.formatBytes(config.limit);
+ limit = formatBytes(config.limit);
break;
}
}
@@ -76,24 +77,11 @@ export class PolicyCardComponent implements OnInit {
defaultHeaders.headerLimit = config.headerLimit;
}
if (config.hasOwnProperty('headerRemaining')) {
- defaultHeaders.headerLimit = config.headerRemaining;
+ defaultHeaders.headerRemaining = config.headerRemaining;
}
if (config.hasOwnProperty('headerReset')) {
- defaultHeaders.headerLimit = config.headerReset;
+ defaultHeaders.headerReset = config.headerReset;
}
this.headers = defaultHeaders;
}
-
- private formatBytes(bytes: number, decimals = 0): string {
- // Thankfully taken from https://stackoverflow.com/a/18650828
- if (bytes === 0) return '0 Bytes';
-
- const k = 1024;
- const dm = decimals < 0 ? 0 : decimals;
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
-
- const i = Math.floor(Math.log(bytes) / Math.log(k));
-
- return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
- }
}

MODIFY
src/app/interfaces/IContractExt.ts
src/app/interfaces/IContractExt.ts
@@ -7,6 +7,7 @@ import {IPolicyExt} from "./IPolicyExt";
* Further it extends the contract object with policies
*/
export interface IContractExt extends IContract {
+ managedEndpoint: string;
section: 'summary' | 'use-api' | 'policies' | 'manage-api';
policies: IPolicyExt[];
}

MODIFY
src/app/services/backend/backend.service.ts
src/app/services/backend/backend.service.ts
@@ -240,11 +240,9 @@ export class BackendService {
clientId: string,
versionName: string,
contractId: number
- ): Observable<Array<IContract>> {
+ ): Observable<IContract> {
const path = `/organizations/${organizationId}/clients/${clientId}/versions/${versionName}/contracts/${contractId}`;
- return this.http.get(this.generateUrl(path)) as Observable<
- Array<IContract>
- >;
+ return this.http.get<IContract>(this.generateUrl(path)) ;
}
public getManagedApiEndpoint(

MODIFY
src/app/services/policy/policy.service.ts
src/app/services/policy/policy.service.ts
import { Injectable } from '@angular/core';
-import {flatMap} from "rxjs/internal/operators";
import {IPolicy} from "../../interfaces/ICommunication";
import {IPolicySummaryExt} from "../../interfaces/IPolicySummaryExt";
import {IPolicyExt} from "../../interfaces/IPolicyExt";
-import {map, tap} from "rxjs/operators";
+import {map, mergeMap, tap} from "rxjs/operators";
import {BackendService} from "../backend/backend.service";
-import {Observable} from "rxjs";
+import {forkJoin, Observable, of} from "rxjs";
+import {formatBytes} from "../../shared/utility";
@Injectable({
providedIn: 'root'
@@ -14,24 +14,30 @@ export class PolicyService {
constructor(private backendService: BackendService) {}
- public getPlanPolicies(orgId: string, planId: string, planVersion: string): Observable<IPolicyExt> {
+ public getPlanPolicies(orgId: string, planId: string, planVersion: string): Observable<IPolicyExt[]> {
return this.getExtendedPolicySummaries(orgId, planId, planVersion).pipe(
- flatMap((policySummaries: IPolicySummaryExt[]) => {
- return policySummaries
- }),
- flatMap((policySummary: IPolicySummaryExt) => {
- return this.getExtendedPlanPolicy(orgId, policySummary);
+ mergeMap((extendedPolicySummaries: IPolicySummaryExt[]) => {
+ if (extendedPolicySummaries.length > 0){
+ return forkJoin(extendedPolicySummaries.map(policySummary => {
+ return this.getExtendedPlanPolicy(orgId, policySummary)
+ }))
+ } else {
+ return of([] as IPolicyExt[])
+ }
})
)
};
- public getApiPolicies(orgId: string, apiId: string, apiVersion: string): Observable<IPolicyExt> {
+ public getApiPolicies(orgId: string, apiId: string, apiVersion: string): Observable<IPolicyExt[]> {
return this.backendService.getApiPolicySummaries(orgId, apiId, apiVersion).pipe(
- flatMap((apiPolicySummaries: IPolicySummaryExt[]) => {
- return apiPolicySummaries;
- }),
- flatMap((apiPolicySummary: IPolicySummaryExt) => {
- return this.getExtendedApiPolicy(orgId, apiId, apiVersion, apiPolicySummary.id.toString());
+ mergeMap((extendedPolicySummaries: IPolicySummaryExt[]) => {
+ if (extendedPolicySummaries.length > 0) {
+ return forkJoin(extendedPolicySummaries.map(extendedPolicySummary => {
+ return this.getExtendedApiPolicy(orgId, apiId, apiVersion, extendedPolicySummary.id.toString())
+ }))
+ } else {
+ return of([] as IPolicyExt[])
+ }
})
)
}
@@ -67,7 +73,7 @@ export class PolicyService {
}
case 'TransferQuotaPolicy': {
extendedPolicy.shortName = 'Quota'
- extendedPolicy.shortDescription = `${this.formatBytes(policyConfig.limit)} per ${policyConfig.period}`
+ extendedPolicy.shortDescription = `${formatBytes(policyConfig.limit)} per ${policyConfig.period}`
break;
}
}
@@ -95,17 +101,4 @@ export class PolicyService {
})
)
}
-
- private formatBytes(bytes: number, decimals = 0): string {
- // Thankfully taken from https://stackoverflow.com/a/18650828
- if (bytes === 0) return '0 Bytes';
-
- const k = 1024;
- const dm = decimals < 0 ? 0 : decimals;
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
-
- const i = Math.floor(Math.log(bytes) / Math.log(k));
-
- return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
- }
}

ADD
src/app/shared/utility.ts
+export function flatArray(array: any[][]): any[] {
+ let flattenedArray: any[] = []
+ flattenedArray = flattenedArray.concat(...array)
+ return flattenedArray;
+}
+
+export function formatBytes(bytes: number, decimals = 0): string {
+ // Thankfully taken from https://stackoverflow.com/a/18650828
+ if (bytes === 0) return '0 Bytes';
+
+ const k = 1024;
+ const dm = decimals < 0 ? 0 : decimals;
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
+}

refactor request chain of contracts, plans and policies