Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed minor issues with B2C configuration. Switched publishing functionality to portal revisions API. #1349

Merged
merged 1 commit into from
Jun 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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