Skip to content

Commit

Permalink
(feat) O3-3266: Optional deps can set feature flags (#1028)
Browse files Browse the repository at this point in the history
* (feat) O3-3266: Optional deps can set feature flags

* Core review suggestion

---------

Co-authored-by: Dennis Kigen <[email protected]>
  • Loading branch information
ibacher and denniskigen authored Jun 5, 2024
1 parent 5e39216 commit fb2bcf8
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 8 deletions.
12 changes: 6 additions & 6 deletions packages/framework/esm-config/src/module-config/module-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,11 @@ export function defineConfigSchema(moduleName: string, schema: ConfigSchema) {
validateConfigSchema(moduleName, schema);
const enhancedSchema = mergeDeepRight(schema, implicitConfigSchema) as ConfigSchema;

const state = configInternalStore.getState();
configInternalStore.setState({
configInternalStore.setState((state) => ({
...state,
schemas: { ...state.schemas, [moduleName]: enhancedSchema },
moduleLoaded: { ...state.moduleLoaded, [moduleName]: true },
});
}));
}

/**
Expand Down Expand Up @@ -239,10 +239,10 @@ export function defineExtensionConfigSchema(extensionName: string, schema: Confi
}

export function provide(config: Config, sourceName = 'provided') {
const state = configInternalStore.getState();
configInternalStore.setState({
configInternalStore.setState((state) => ({
...state,
providedConfigs: [...state.providedConfigs, { source: sourceName, config }],
});
}));
}

/**
Expand Down
8 changes: 7 additions & 1 deletion packages/framework/esm-globals/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ export interface OpenmrsAppRoutes {
/**
* The feature flag to enable if this backend dependency is present
*/
feature?: string;
feature?: FeatureFlagDefinition;
};
};
/**
Expand All @@ -364,6 +364,12 @@ export interface OpenmrsAppRoutes {
workspaces?: Array<WorkspaceDefinition>;
}

export interface FeatureFlagDefinition {
flagName: string;
label: string;
description: string;
}

/**
* This interfaces describes the format of the overall rotues.json loaded by the app shell.
* Basically, this is the same as the app routes, with each routes definition keyed by the app's name
Expand Down
73 changes: 73 additions & 0 deletions packages/shell/esm-app-shell/src/optionaldeps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
type FeatureFlagDefinition,
openmrsFetch,
registerFeatureFlag,
setFeatureFlag,
getCurrentUser,
restBaseUrl,
} from '@openmrs/esm-framework/src/internal';
import { satisfies } from 'semver';

export function registerOptionalDependencyHandler() {
const subscription = getCurrentUser().subscribe((session) => {
if (session.authenticated) {
subscription.unsubscribe();
setupOptionalDependencies();
}
});

return Promise.resolve();
}

function setupOptionalDependencies() {
const optionalDependencyFlags = window.installedModules.reduce<
Map<string, { version: string; feature: FeatureFlagDefinition }>
>((curr, module) => {
if (module[1]?.optionalBackendDependencies) {
Object.entries(module[1].optionalBackendDependencies).forEach((optionalDependency) => {
const optionalDependencyDescriptor = optionalDependency[1];
if (typeof optionalDependencyDescriptor !== 'string') {
if (
typeof optionalDependencyDescriptor.feature === 'object' &&
optionalDependencyDescriptor.feature &&
optionalDependencyDescriptor.feature.flagName?.length > 0 &&
optionalDependencyDescriptor.feature.label?.length > 0 &&
optionalDependencyDescriptor.feature.description?.length > 0
) {
curr.set(optionalDependency[0], {
version: optionalDependencyDescriptor.version,
feature: optionalDependencyDescriptor.feature,
});
} else {
console.warn(
`Feature flag descriptor for ${module[0]} does not match expected type. Feature flags must define a 'flagName', 'label', and 'description'`,
);
}
}
});
}

return curr;
}, new Map());

if (optionalDependencyFlags.size > 0) {
openmrsFetch<{ results: { uuid: string; version: string }[] }>(`${restBaseUrl}/module?v=custom:(uuid,version)`)
.then((response) => {
(response.data.results ?? []).forEach((backendModule) => {
if (optionalDependencyFlags.has(backendModule.uuid)) {
const optionalDependency = optionalDependencyFlags.get(backendModule.uuid);
if (optionalDependency && satisfies(backendModule.version, optionalDependency.version)) {
registerFeatureFlag(
optionalDependency.feature.flagName,
optionalDependency.feature.label,
optionalDependency.feature.description,
);

setFeatureFlag(optionalDependency.feature.flagName, true);
}
}
});
})
.catch(() => {}); // swallow any issues fetching
}
}
4 changes: 3 additions & 1 deletion packages/shell/esm-app-shell/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
} from '@openmrs/esm-framework/src/internal';
import { finishRegisteringAllApps, registerApp, tryRegisterExtension } from './apps';
import { setupI18n } from './locale';
import { registerOptionalDependencyHandler } from './optionaldeps';
import { appName, getCoreExtensions } from './ui';

// @internal
Expand Down Expand Up @@ -414,5 +415,6 @@ export function run(configUrls: Array<string>) {
.then(runShell)
.catch(handleInitFailure)
.then(closeLoading)
.then(offlineEnabled ? setupOffline : undefined);
.then(offlineEnabled ? setupOffline : undefined)
.then(registerOptionalDependencyHandler);
}

0 comments on commit fb2bcf8

Please sign in to comment.