Skip to content

Commit

Permalink
custom widgets df testing fixes (#1917)
Browse files Browse the repository at this point in the history
* displayName required

* overrides handling - no longer valid override cleanup

* customWidgetList refresh configs in constructor

* listenForSecretsRequests managementApiUrl + ensureUrlArmified
  • Loading branch information
JMach1 authored Aug 22, 2022
1 parent d2d7830 commit f7b3ef0
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/components/custom-widget-list/createWidget.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
data-bind="textInput: displayName, validationElement: displayName, attr: {disabled: !!$component.config}"
maxlength="2000" spellcheck="false" aria-required="true"
/>
<!-- ko if: displayName() && !$component.config -->
<!-- ko if: !$component.config -->
<button class="btn btn-danger input-group-btn" data-bind="validationMessageToggle: displayName"
type="button">!</button>
<!-- /ko -->
Expand Down
2 changes: 2 additions & 0 deletions src/components/custom-widget-list/createWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export class CreateWidget {
errorElementClass: "is-invalid",
decorateInputElement: true
});

this.displayName.extend(<any>{ required: { message: `Name is required.` } });
}

@Param()
Expand Down
13 changes: 10 additions & 3 deletions src/components/custom-widget-list/customWidgetList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Component } from "@paperbits/common/ko/decorators";
import { MapiBlobStorage } from "../../persistence";
import { TCustomWidgetConfig } from "../custom-widget";
import template from "./customWidgetList.html";
import { listConfigBlobs } from "./loadCustomWidgetConfigs";


@Component({
Expand All @@ -21,9 +22,15 @@ export class ContentWorkshop {
customWidgetConfigsPromise: Promise<TCustomWidgetConfig[]>,
) {
this.customWidgetConfigs = ko.observable();
customWidgetConfigsPromise.then(configs =>
this.customWidgetConfigs(configs.sort(ContentWorkshop.sortByName))
);
const refreshConfigs = listConfigBlobs(blobStorage); // in case some configs on the blob storage got deleted/updated/added
Promise.all([refreshConfigs, customWidgetConfigsPromise]).then(([configBlobs, configsAll]) => {
const configs: Record<string, TCustomWidgetConfig> = {};
configBlobs.forEach(config => configs[config.name] = config);
configsAll.forEach(config => {
if (config.override) configs[config.name] = config
});
this.customWidgetConfigs(Object.values(configs).sort(ContentWorkshop.sortByName))
});
}

private static sortByName(a: TCustomWidgetConfig, b: TCustomWidgetConfig): number {
Expand Down
50 changes: 30 additions & 20 deletions src/components/custom-widget-list/loadCustomWidgetConfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,34 @@ import * as Constants from "../../constants";
import { MapiBlobStorage } from "../../persistence";
import { TCustomWidgetConfig } from "../custom-widget";

export async function listConfigBlobs(blobStorage: MapiBlobStorage): Promise<TCustomWidgetConfig[]> {
const configsNames = await blobStorage.listBlobs(`${BLOB_ROOT}/${BLOB_CONFIGS_FOLDER}/`);
const configsUint8s = await Promise.all(configsNames.map(blobName => blobStorage.downloadBlob(blobName)));
return configsUint8s.map(uint8 => JSON.parse(new TextDecoder().decode(uint8)));
}

function showToast(viewManager: ViewManager, widgetSource: TCustomWidgetConfig): void {
const sessionStorageKey = Constants.overrideToastSessionKeyPrefix + widgetSource.name
if (window.sessionStorage.getItem(sessionStorageKey)) return

let message = `Custom widget "${widgetSource.displayName}" URL is overridden`;
if (typeof widgetSource.override === "string") message += ` with ${widgetSource.override}`;
const toast = viewManager.addToast(widgetSource.displayName, message, [{
title: "Got it",
action: async () => {
window.sessionStorage.setItem(sessionStorageKey, "true");
viewManager.removeToast(toast);
}
}]);
}

export async function loadCustomWidgetConfigs(
blobStorage: MapiBlobStorage,
viewManager: ViewManager,
): Promise<TCustomWidgetConfig[]> {
const sourcesSession = Object.keys(window.sessionStorage)
const sourcesSessionKeys = Object.keys(window.sessionStorage)
.filter((key: string) => key.startsWith(Constants.overrideConfigSessionKeyPrefix))
.map(key => window.sessionStorage.getItem(key));
const sourcesSession = sourcesSessionKeys.map(key => window.sessionStorage.getItem(key));
const sourcesSearchParams = new URLSearchParams(window.location.search)
.getAll(OVERRIDE_PORT_KEY)
.map(port => new URL("http://localhost:" + (isNaN(parseInt(port)) ? OVERRIDE_DEFAULT_PORT : port)).href);
Expand All @@ -28,33 +49,22 @@ export async function loadCustomWidgetConfigs(
}
});

const configsNames = await blobStorage.listBlobs(`${BLOB_ROOT}/${BLOB_CONFIGS_FOLDER}/`);
const configsUint8s = await Promise.all(configsNames.map(blobName => blobStorage.downloadBlob(blobName)));
const configs: TCustomWidgetConfig[] = configsUint8s.map(uint8 => JSON.parse(new TextDecoder().decode(uint8)));

const configurations: Record<string, TCustomWidgetConfig> = {};
(await listConfigBlobs(blobStorage)).forEach(config => configurations[config.name] = config);

configs.forEach(config => configurations[config.name] = config);
(await Promise.all(overridesPromises)).forEach(({override, source}) => {
if (!override) return;
if (!override) {
const key = sourcesSessionKeys.find(key => window.sessionStorage.getItem(key) === source);
if (key) sessionStorage.removeItem(key);
return;
}

const href = new URL(source).href;
window.sessionStorage.setItem(Constants.overrideConfigSessionKeyPrefix + override.name, href);
const widgetSource = {...override, override: href ?? true};
configurations[override.name] = widgetSource

const sessionStorageKey = Constants.overrideToastSessionKeyPrefix + override.name
if (window.sessionStorage.getItem(sessionStorageKey)) return

let message = `Custom widget "${override.displayName}" URL is overridden`;
if (typeof widgetSource.override === "string") message += ` with ${widgetSource.override}`;
const toast = viewManager.addToast(override.displayName, message, [{
title: "Got it",
action: async () => {
window.sessionStorage.setItem(sessionStorageKey, "true");
viewManager.removeToast(toast);
}
}]);
showToast(viewManager, widgetSource);
});

return Object.values(configurations);
Expand Down
2 changes: 1 addition & 1 deletion src/components/custom-widget/customWidget.design.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IInjectorModule, IInjector } from "@paperbits/common/injection";
import { CustomWidgetEditorViewModel, CustomWidgetViewModel, CustomWidgetViewModelBinder } from "./ko";
import { CustomWidgetModelBinder } from ".";
import { ListenForSecretsRequests } from "./ListenForSecretsRequests";
import { ListenForSecretsRequests } from "./listenForSecretsRequests";

export class CustomWidgetDesignModule implements IInjectorModule {
public register(injector: IInjector): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IInjectorModule, IInjector } from "@paperbits/common/injection";
import { ListenForSecretsRequests } from "./ListenForSecretsRequests";
import { ListenForSecretsRequests } from "./listenForSecretsRequests";

export class CustomWidgetRuntimeModule implements IInjectorModule {
public register(injector: IInjector): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { APIM_ASK_FOR_SECRETS_MESSAGE_KEY, Secrets } from "@azure/api-management
import { ISettingsProvider } from "@paperbits/common/configuration";
import { AccessToken, IAuthenticator } from "../../authentication";
import { managementApiVersion, SettingNames } from "../../constants";
import { Utils } from "../../utils";

export class ListenForSecretsRequests {
constructor(
Expand All @@ -20,8 +21,11 @@ export class ListenForSecretsRequests {
: window.document.getElementById(instanceId)
) as HTMLIFrameElement;

const managementApiUrl = await settingsProvider.getSetting<string>(SettingNames.managementApiUrl)
const secrets: Secrets = { managementApiUrl, apiVersion: managementApiVersion };
const managementApiUrl = await settingsProvider.getSetting<string>(SettingNames.managementApiUrl);
const secrets: Secrets = {
managementApiUrl: Utils.ensureUrlArmified(managementApiUrl),
apiVersion: managementApiVersion
};

const token = await authenticator.getAccessTokenAsString();
if (token) {
Expand Down

0 comments on commit f7b3ef0

Please sign in to comment.