Skip to content

Commit

Permalink
Fixed minor issues with B2C configuration. Switched publishing functi…
Browse files Browse the repository at this point in the history
…onality to portal revisions API. (#1349)
  • Loading branch information
azaslonov committed Jun 21, 2021
1 parent 2f88b66 commit da9e09b
Show file tree
Hide file tree
Showing 14 changed files with 185 additions and 28 deletions.
6 changes: 3 additions & 3 deletions src/apim.runtime.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import "./polyfills";
import "@paperbits/core/ko/bindingHandlers/bindingHandlers.activate";
import "@paperbits/core/ko/bindingHandlers/bindingHandlers.component";
import "@paperbits/core/ko/bindingHandlers/bindingHandlers.dialog";
import "@paperbits/core/ko/bindingHandlers/bindingHandlers.focus";
import "@paperbits/core/ko/bindingHandlers/bindingHandlers.scrollable";
import { DefaultSettingsProvider } from "@paperbits/common/configuration";
import { IInjector, IInjectorModule } from "@paperbits/common/injection";
import { ConsoleLogger } from "@paperbits/common/logging";
import { DefaultSessionManager } from "@paperbits/common/persistence/defaultSessionManager";
Expand Down Expand Up @@ -53,7 +53,6 @@ import { Signup } from "./components/users/signup/ko/runtime/signup";
import { Subscriptions } from "./components/users/subscriptions/ko/runtime/subscriptions";
import { ValidationSummary } from "./components/users/validation-summary/ko/runtime/validation-summary";
import { UnhandledErrorHandler } from "./errors/unhandledErrorHandler";
import "./polyfills";
import { AadSignOutRouteGuard } from "./routing/aadSignoutRouteGuard";
import { RouteHelper } from "./routing/routeHelper";
import { SignOutRouteGuard } from "./routing/signOutRouteGuard";
Expand All @@ -69,6 +68,7 @@ import { ProvisionService } from "./services/provisioningService";
import { TagService } from "./services/tagService";
import { TenantService } from "./services/tenantService";
import { UsersService } from "./services/usersService";
import { ApimSettingsProvider } from "./configuration/apimSettingsProvider";

export class ApimRuntimeModule implements IInjectorModule {
public register(injector: IInjector): void {
Expand Down Expand Up @@ -122,7 +122,7 @@ export class ApimRuntimeModule implements IInjectorModule {
injector.bindSingleton("backendService", BackendService);
injector.bindSingleton("aadService", AadService);
injector.bindSingleton("mapiClient", MapiClient);
injector.bindSingleton("settingsProvider", DefaultSettingsProvider);
injector.bindSingleton("settingsProvider", ApimSettingsProvider);
injector.bindSingleton("authenticator", DefaultAuthenticator);
injector.bindSingleton("routeHelper", RouteHelper);
injector.bindSingleton("userService", StaticUserService);
Expand Down
10 changes: 8 additions & 2 deletions src/components/app/app.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { EventManager } from "@paperbits/common/events";
import { AccessToken } from "./../../authentication/accessToken";
import template from "./app.html";
import { ViewManager } from "@paperbits/common/ui";
import { Component, OnMounted } from "@paperbits/common/ko/decorators";
import { ISettingsProvider } from "@paperbits/common/configuration";
import { ISiteService } from "@paperbits/common/sites";
import { IAuthenticator } from "../../authentication";
import { Utils } from "../../utils";

const startupError = `Unable to start the portal`;

Expand All @@ -18,7 +18,8 @@ export class App {
private readonly settingsProvider: ISettingsProvider,
private readonly authenticator: IAuthenticator,
private readonly viewManager: ViewManager,
private readonly siteService: ISiteService
private readonly siteService: ISiteService,
private readonly eventManager: EventManager
) { }

@OnMounted()
Expand Down Expand Up @@ -70,6 +71,11 @@ export class App {

this.viewManager.setHost({ name: "page-host" });
this.viewManager.showToolboxes();

setTimeout(() => this.eventManager.dispatchEvent("displayHint", {
key: "a69b",
content: `When you're in the administrative view, you still can navigate any website hyperlink by clicking on it holding Ctrl (Windows) or ⌘ (Mac) key.`
}), 5000);
}
catch (error) {
this.viewManager.addToast(startupError, `Check if the settings specified in the configuration file <i>config.design.json</i> are correct or refer to the <a href="http://aka.ms/apimdocs/portal#faq" target="_blank">frequently asked questions</a>.`);
Expand Down
22 changes: 7 additions & 15 deletions src/components/content/content.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import template from "./content.html";
import * as moment from "moment";
import * as Constants from "../../constants";
import { ViewManager, View } from "@paperbits/common/ui";
import { Component } from "@paperbits/common/ko/decorators";
import { HttpClient } from "@paperbits/common/http";
import { ISettingsProvider } from "@paperbits/common/configuration";
import { Logger } from "@paperbits/common/logging";
import { IAuthenticator } from "../../authentication/IAuthenticator";
import { AppError } from "./../../errors/appError";
import { MapiError } from "../../errors/mapiError";
import { MapiClient } from "../../services";


@Component({
Expand All @@ -16,9 +17,8 @@ import { MapiError } from "../../errors/mapiError";
export class ContentWorkshop {
constructor(
private readonly viewManager: ViewManager,
private readonly httpClient: HttpClient,
private readonly mapiClient: MapiClient,
private readonly authenticator: IAuthenticator,
private readonly settingsProvider: ISettingsProvider,
private readonly logger: Logger
) { }

Expand All @@ -30,20 +30,12 @@ export class ContentWorkshop {
}

try {
const accessToken = await this.authenticator.getAccessTokenAsString();
const revisionName = moment.utc().format(Constants.releaseNameFormat);

const publishRootUrl = await this.settingsProvider.getSetting<string>("backendUrl") || "";

const response = await this.httpClient.send({
url: publishRootUrl + "/publish",
method: "POST",
headers: [{ name: "Authorization", value: accessToken }]
await this.mapiClient.put(`/portalRevisions/${revisionName}`, null, {
properties: { description: "" }
});

if (response.statusCode !== 200) {
throw MapiError.fromResponse(response);
}

this.viewManager.notifySuccess("Operations", `The website is being published...`);
this.viewManager.closeWorkshop("content-workshop");
}
Expand Down
72 changes: 72 additions & 0 deletions src/configuration/apimSettingsProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as Objects from "@paperbits/common/objects";
import { EventManager } from "@paperbits/common/events";
import { HttpClient } from "@paperbits/common/http";
import { ISettingsProvider } from "@paperbits/common/configuration";
import { SessionManager } from "@paperbits/common/persistence/sessionManager";


export class ApimSettingsProvider implements ISettingsProvider {
private configuration: Object;
private initializePromise: Promise<void>;

constructor(
private readonly httpClient: HttpClient,
private readonly eventManager: EventManager,
private readonly sessionManager: SessionManager
) { }

private async ensureInitialized(): Promise<void> {
if (!this.initializePromise) {
this.initializePromise = this.loadSettings();
}
return this.initializePromise;
}

private async loadSettings(): Promise<void> {
const commonConfigurationResponse = await this.httpClient.send<any>({ url: "/config.json" });
const commonConfiguration = commonConfigurationResponse.toObject();

const searializedDesignTimeSettings = await this.sessionManager?.getItem("designTimeSettings");

if (searializedDesignTimeSettings) {
const designTimeSettings = searializedDesignTimeSettings;
Object.assign(commonConfiguration, designTimeSettings);
}
else {
const apimsConfigurationResponse = await this.httpClient.send<any>({ url: "/config-apim.json" });

if (apimsConfigurationResponse.statusCode === 200) {
const apimConfiguration = apimsConfigurationResponse.toObject();
Object.assign(commonConfiguration, apimConfiguration);
}
}

this.configuration = commonConfiguration;
}

public async getSetting<T>(name: string): Promise<T> {
await this.ensureInitialized();
return Objects.getObjectAt(name, this.configuration);
}

public onSettingChange<T>(name: string, eventHandler: (value: T) => void): void {
this.eventManager.addEventListener("onSettingChange", (setting) => {
if (setting.name === name) {
eventHandler(setting.value);
}
});
}

public async setSetting<T>(name: string, value: T): Promise<void> {
await this.ensureInitialized();

Objects.setValue(name, this.configuration, value);
this.eventManager.dispatchEvent("onSettingChange", { name: name, value: value });
}

public async getSettings(): Promise<any> {
await this.ensureInitialized();

return this.configuration;
}
}
4 changes: 3 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,6 @@ export const developerPortalType = "self-hosted-portal";
/**
* Header name to track developer portal type.
*/
export const portalHeaderName = "x-ms-apim-client";
export const portalHeaderName = "x-ms-apim-client";

export const releaseNameFormat = "YYYYMMDDHHmm";
11 changes: 11 additions & 0 deletions src/contracts/oauthSession.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface OAuthSession {
authenticationFlow: string;
authenticationCallback: (accessToken: string) => void;
authenticationErrorCallback: (error: Error) => void;
loginUrl: string;
redirectUri: string;
clientId: string;
issuer: string;
tokenEndpoint: string;
scope: string;
}
15 changes: 15 additions & 0 deletions src/models/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ServiceDescriptionContract, HostnameConfiguration, ServiceSku } from "./../contracts/service";

export class ServiceDescription {
public name: string;
public hostnameConfigurations: HostnameConfiguration[];
public sku: ServiceSku;
public gatewayUrl: string;

constructor(contract: ServiceDescriptionContract) {
this.name = contract.name;
this.sku = contract.sku;
this.gatewayUrl = contract.properties.gatewayUrl;
this.hostnameConfigurations = contract.properties.hostnameConfigurations;
}
}
5 changes: 5 additions & 0 deletions src/modules.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ declare module "*.liquid" {
declare module "*.txt" {
const content: string;
export default content;
}

declare module "*.raw" {
const content: string;
export default content;
}
42 changes: 42 additions & 0 deletions src/persistence/cachedObjectStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { LruCache } from "@paperbits/common/caching";
import { IObjectStorage, Page, Query } from "@paperbits/common/persistence";

export class CachedObjectStorage {
private readonly cache: LruCache<Promise<any>>;

constructor(
private readonly underlyingStorage: IObjectStorage
) {
this.cache = new LruCache(100, () => { return; });
}

public addObject<T>(key: string, dataObject: T): Promise<void> {
throw new Error("Not supported.");
}

public getObject<T>(key: string): Promise<T> {
const cachedItemPromise = this.cache.getItem(key);

if (cachedItemPromise) {
return cachedItemPromise;
}

const fetchPromise = this.underlyingStorage.getObject<T>(key);

this.cache.setItem(key, fetchPromise);

return fetchPromise;
}

public deleteObject(key: string): Promise<void> {
throw new Error("Not supported.");
}

public updateObject<T>(key: string, dataObject: T): Promise<void> {
throw new Error("Not supported.");
}

public async searchObjects<T>(key: string, query?: Query<T>): Promise<Page<T>> {
return await this.underlyingStorage.searchObjects<T>(key, query);
}
}
14 changes: 14 additions & 0 deletions src/persistence/publishingCacheModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IObjectStorage } from "@paperbits/common/persistence";
import { IInjector, IInjectorModule } from "@paperbits/common/injection";
import { CachedObjectStorage } from "./cachedObjectStorage";


export class PublishingCacheModule implements IInjectorModule {
public register(injector: IInjector): void {
const underlyingObjectStorage = injector.resolve<IObjectStorage>("objectStorage");

injector.bindSingletonFactory<IObjectStorage>("objectStorage", (ctx: IInjector) => {
return new CachedObjectStorage(underlyingObjectStorage);
});
}
}
6 changes: 1 addition & 5 deletions src/publishing/aadConfigPublisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,10 @@ import { RuntimeConfigBuilder } from "./runtimeConfigBuilder";
export class AadConfigPublisher implements IPublisher {
constructor(
private readonly runtimeConfigBuilder: RuntimeConfigBuilder,
private readonly identityService: IdentityService,
private readonly settingsProvider: ISettingsProvider
private readonly identityService: IdentityService
) { }

public async publish(): Promise<void> {
const managementApiUrl = await this.settingsProvider.getSetting(SettingNames.managementApiUrl);
this.runtimeConfigBuilder.addSetting(SettingNames.managementApiUrl, managementApiUrl);

const identityProviders = await this.identityService.getIdentityProviders();

const aadIdentityProvider = identityProviders.find(x => x.type === SettingNames.aadClientConfig);
Expand Down
2 changes: 1 addition & 1 deletion src/publishing/runtimeConfigPublisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ export class RuntimeConfigPublisher implements IPublisher {
const configuration = this.runtimeConfigBuilder.build();
const content = Utils.stringToUnit8Array(JSON.stringify(configuration));

await this.outputBlobStorage.uploadBlob("/config.json", content);
await this.outputBlobStorage.uploadBlob("/config-apim.json", content);
}
}
2 changes: 1 addition & 1 deletion src/services/runtimeConfigurator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@ export class RuntimeConfigurator {
designTimeSettings[SettingNames.aadB2CClientConfig] = aadB2CConfig;
}

this.sessionManager.setItem("designTimeSettings", JSON.stringify(designTimeSettings));
this.sessionManager.setItem("designTimeSettings", designTimeSettings);
}
}
2 changes: 2 additions & 0 deletions src/startup.publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ProseMirrorModule } from "@paperbits/prosemirror/prosemirror.module";
import { StaticSettingsProvider } from "./components/staticSettingsProvider";
import { FileSystemBlobStorage } from "./components/filesystemBlobStorage";
import { ApimPublishModule } from "./apim.publish.module";
import { PublishingCacheModule } from "./persistence/publishingCacheModule";

/* Reading settings from configuration file */
const configFile = path.resolve(__dirname, "./config.json");
Expand All @@ -27,6 +28,7 @@ injector.bindModule(new ProseMirrorModule());
injector.bindModule(new ApimPublishModule());
injector.bindInstance("settingsProvider", settingsProvider);
injector.bindInstance("outputBlobStorage", outputBlobStorage);
injector.bindModule(new PublishingCacheModule());
injector.resolve("autostart");

/* Allowing self-signed certificates for HTTP requests */
Expand Down

0 comments on commit da9e09b

Please sign in to comment.