From e0cca13e502bd02e3b7558c572ed3b2838288384 Mon Sep 17 00:00:00 2001 From: Ian <52504170+ibacher@users.noreply.github.com> Date: Mon, 18 Jul 2022 21:50:09 -0400 Subject: [PATCH] (feat): Add implicit conditions to configuration schema (#475) --- .../src/shared-api-objects/current-user.ts | 24 ++- .../src/module-config/module-config.test.ts | 62 +++++- .../src/module-config/module-config.ts | 42 ++++- .../esm-config/src/module-config/state.ts | 14 +- packages/framework/esm-config/src/types.ts | 5 + .../esm-extensions/src/extensions.test.ts | 10 + .../esm-extensions/src/extensions.ts | 31 +++ .../framework/esm-extensions/src/store.ts | 1 + packages/framework/esm-framework/docs/API.md | 73 ++++---- .../docs/interfaces/AssignedExtension.md | 14 +- .../docs/interfaces/ComponentDefinition.md | 9 +- .../docs/interfaces/ConfigObject.md | 11 ++ .../docs/interfaces/ConnectedExtension.md | 8 +- .../DisplayConditionsConfigObject.md | 19 ++ .../docs/interfaces/ExtensionDefinition.md | 21 ++- .../docs/interfaces/ExtensionRegistration.md | 11 ++ .../docs/interfaces/ExtensionSlotConfig.md | 8 +- .../interfaces/ExtensionSlotConfigObject.md | 6 +- .../docs/interfaces/ExtensionSlotState.md | 4 +- .../docs/interfaces/ExtensionStore.md | 2 +- .../docs/interfaces/PageDefinition.md | 13 +- .../docs/interfaces/UserHasAccessProps.md | 2 +- packages/framework/esm-framework/mock.tsx | 3 +- .../extension-config.test.tsx | 177 +++++++++++++++++- packages/framework/esm-globals/src/types.ts | 5 +- .../esm-react-utils/src/UserHasAccess.tsx | 2 +- packages/shell/esm-app-shell/src/apps.ts | 7 +- packages/shell/esm-app-shell/src/helpers.ts | 10 +- packages/shell/esm-app-shell/src/run.ts | 1 - 29 files changed, 478 insertions(+), 117 deletions(-) create mode 100644 packages/framework/esm-framework/docs/interfaces/DisplayConditionsConfigObject.md diff --git a/packages/framework/esm-api/src/shared-api-objects/current-user.ts b/packages/framework/esm-api/src/shared-api-objects/current-user.ts index dd146c6ce..e370e73e8 100644 --- a/packages/framework/esm-api/src/shared-api-objects/current-user.ts +++ b/packages/framework/esm-api/src/shared-api-objects/current-user.ts @@ -1,6 +1,7 @@ /** @module @category API */ import { reportError } from "@openmrs/esm-error-handling"; import { createGlobalStore } from "@openmrs/esm-state"; +import isUndefined from "lodash-es/isUndefined"; import { Observable } from "rxjs"; import { openmrsFetch, sessionEndpoint } from "../openmrs-fetch"; import type { @@ -122,15 +123,28 @@ function setUserLanguage(data: Session) { } function userHasPrivilege( - requiredPrivilege: string, + requiredPrivilege: string | string[], user: { privileges: Array } ) { - return user.privileges.find((p) => requiredPrivilege === p.display); + if (typeof requiredPrivilege === "string") { + return !isUndefined( + user.privileges.find((p) => requiredPrivilege === p.display) + ); + } else if (Array.isArray(requiredPrivilege)) { + return requiredPrivilege.every( + (rp) => !isUndefined(user.privileges.find((p) => rp === p.display)) + ); + } else { + console.error(`Could not understand privileges "${requiredPrivilege}"`); + } + + return true; } function isSuperUser(user: { roles: Array }) { - const superUserRole = "System Developer"; - return user.roles.find((role) => role.display === superUserRole); + return !isUndefined( + user.roles.find((role) => role.display === "System Developer") + ); } /** @@ -179,7 +193,7 @@ export function clearCurrentUser() { } export function userHasAccess( - requiredPrivilege: string, + requiredPrivilege: string | string[], user: { privileges: Array; roles: Array } ) { return userHasPrivilege(requiredPrivilege, user) || isSuperUser(user); diff --git a/packages/framework/esm-config/src/module-config/module-config.test.ts b/packages/framework/esm-config/src/module-config/module-config.test.ts index c10a73a1c..f22d6a4da 100644 --- a/packages/framework/esm-config/src/module-config/module-config.test.ts +++ b/packages/framework/esm-config/src/module-config/module-config.test.ts @@ -201,6 +201,20 @@ describe("defineConfigSchema", () => { Config.defineConfigSchema("mod-mod", schema); expect(console.error).not.toHaveBeenCalled(); }); + + it("logs an error if the schema attempts to include a key named 'Display conditions'", () => { + const schema = { + "Display conditions": { + _type: Type.Array, + _default: [], + }, + }; + + Config.defineConfigSchema("mod-mod", schema); + expect(console.error).toHaveBeenCalledWith( + expect.stringMatching(/mod-mod.*\bDisplay conditions\b/) + ); + }); }); describe("getConfig", () => { @@ -301,7 +315,11 @@ describe("getConfig", () => { Config.provide(goodConfig); const result = await Config.getConfig("foo-module"); expect(result).toStrictEqual({ - bar: { a: { b: 0 }, c: 2, diff: 2 }, + bar: { + a: { b: 0 }, + c: 2, + diff: 2, + }, }); }); @@ -485,16 +503,14 @@ describe("getConfig", () => { Config.defineConfigSchema("object-def", { furi: { _type: Type.Object, - _elements: { - gohan: { _type: Type.String }, - }, - _default: { - kake: { gohan: "ok" }, - }, + _elements: { gohan: { _type: Type.String } }, + _default: { kake: { gohan: "ok" } }, }, }); const config = await Config.getConfig("object-def"); - expect(config).toStrictEqual({ furi: { kake: { gohan: "ok" } } }); + expect(config).toStrictEqual({ + furi: { kake: { gohan: "ok" } }, + }); }); it("interpolates freeform object element defaults", async () => { @@ -823,9 +839,29 @@ describe("implementer tools config", () => { _description: "All the foo", _validators: [], }, + "Display conditions": { + privileges: { + _default: [], + _description: + "The privilege(s) the user must have to use this extension", + _source: "default", + _type: Type.Array, + _value: [], + }, + }, }, "bar-module": { bar: { _value: "baz", _source: "my config source", _default: "quinn" }, + "Display conditions": { + privileges: { + _default: [], + _description: + "The privilege(s) the user must have to use this extension", + _source: "default", + _type: Type.Array, + _value: [], + }, + }, }, }); }); @@ -975,6 +1011,16 @@ describe("extension slot config", () => { remove: { _value: ["bar"], _source: "provided" }, }, }, + "Display conditions": { + privileges: { + _default: [], + _description: + "The privilege(s) the user must have to use this extension", + _source: "default", + _type: Type.Array, + _value: [], + }, + }, }, }); }); diff --git a/packages/framework/esm-config/src/module-config/module-config.ts b/packages/framework/esm-config/src/module-config/module-config.ts index a2527e681..8dab2a8a8 100644 --- a/packages/framework/esm-config/src/module-config/module-config.ts +++ b/packages/framework/esm-config/src/module-config/module-config.ts @@ -1,5 +1,5 @@ /** @module @category Config */ -import { clone, reduce, mergeDeepRight, equals } from "ramda"; +import { clone, reduce, mergeDeepRight, equals, omit } from "ramda"; import { Config, ConfigObject, @@ -27,7 +27,6 @@ import { implementerToolsConfigStore, temporaryConfigStore, getExtensionSlotsConfigStore, - ExtensionsConfigStore, } from "./state"; import type {} from "@openmrs/esm-globals"; import { TemporaryConfigStore } from ".."; @@ -192,9 +191,14 @@ function computeExtensionConfigs( */ export function defineConfigSchema(moduleName: string, schema: ConfigSchema) { validateConfigSchema(moduleName, schema); + const enhancedSchema = mergeDeepRight( + schema, + displayConditionsSchema + ) as ConfigSchema; + const state = configInternalStore.getState(); configInternalStore.setState({ - schemas: { ...state.schemas, [moduleName]: schema }, + schemas: { ...state.schemas, [moduleName]: enhancedSchema }, }); } @@ -220,14 +224,20 @@ export function defineExtensionConfigSchema( schema: ConfigSchema ) { validateConfigSchema(extensionName, schema); + const enhancedSchema = mergeDeepRight( + schema, + displayConditionsSchema + ) as ConfigSchema; + const state = configInternalStore.getState(); if (state.schemas[extensionName]) { console.warn( `Config schema for extension ${extensionName} already exists. If there are multiple extensions with this same name, one will probably crash.` ); } + configInternalStore.setState({ - schemas: { ...state.schemas, [extensionName]: schema }, + schemas: { ...state.schemas, [extensionName]: enhancedSchema }, }); } @@ -256,7 +266,8 @@ export function getConfig(moduleName: string): Promise { const store = getConfigStore(moduleName); function update(state: ConfigStore) { if (state.loaded && state.config) { - resolve(state.config); + const config = omit(["Display conditions"], state.config); + resolve(config); unsubscribe && unsubscribe(); } } @@ -362,7 +373,7 @@ function getSchemaWithValuesAndSources(schema) { return obj; }, {}); } else { - // Schema is bad; error will have been logged during schema validation + // at this point, the schema is bad and an error will have been logged during schema validation return {}; } } @@ -492,6 +503,12 @@ function validateConfigSchema( const thisKeyPath = keyPath + (keyPath && ".") + key; const schemaPart = schema[key] as ConfigSchema; + if (thisKeyPath === "Display conditions") { + console.error( + `${moduleName} declares a configuration option called "Display conditions"; the "Display conditions" option is a reserved name. ${updateMessage}` + ); + } + if (!isOrdinaryObject(schemaPart)) { console.error( `${moduleName} has bad config schema definition for key '${thisKeyPath}'. ${updateMessage}` @@ -863,3 +880,16 @@ function getExtensionNameFromId(extensionId: string) { const [extensionName] = extensionId.split("#"); return extensionName; } + +/** + * The displayConditionsSchema is implicitly included in every configuration schema + */ +const displayConditionsSchema: ConfigSchema = { + "Display conditions": { + privileges: { + _description: "The privilege(s) the user must have to use this extension", + _type: Type.Array, + _default: [], + }, + }, +}; diff --git a/packages/framework/esm-config/src/module-config/state.ts b/packages/framework/esm-config/src/module-config/state.ts index 15e2562ca..ab88467a4 100644 --- a/packages/framework/esm-config/src/module-config/state.ts +++ b/packages/framework/esm-config/src/module-config/state.ts @@ -1,5 +1,6 @@ /** @module @category Config */ import { createGlobalStore, getGlobalStore } from "@openmrs/esm-state"; +import { omit } from "ramda"; import { Config, ConfigObject, @@ -199,11 +200,16 @@ export function getExtensionsConfigStore() { /** @internal */ export function getExtensionConfig(slotName: string, extensionId: string) { - return getExtensionConfigFromStore( - getExtensionsConfigStore().getState(), - slotName, - extensionId + const extensionConfig = Object.assign( + {}, + getExtensionConfigFromStore( + getExtensionsConfigStore().getState(), + slotName, + extensionId + ) ); + extensionConfig.config = omit(["Display conditions"], extensionConfig.config); + return extensionConfig; } /** @internal */ diff --git a/packages/framework/esm-config/src/types.ts b/packages/framework/esm-config/src/types.ts index 0865feb47..6e2ca5fa2 100644 --- a/packages/framework/esm-config/src/types.ts +++ b/packages/framework/esm-config/src/types.ts @@ -27,6 +27,11 @@ export interface Config extends Object { export interface ConfigObject extends Object { [key: string]: any; + "Display conditions"?: DisplayConditionsConfigObject; +} + +export interface DisplayConditionsConfigObject { + privileges?: string[]; } export type ConfigValue = diff --git a/packages/framework/esm-extensions/src/extensions.test.ts b/packages/framework/esm-extensions/src/extensions.test.ts index d3acc558a..4258eee96 100644 --- a/packages/framework/esm-extensions/src/extensions.test.ts +++ b/packages/framework/esm-extensions/src/extensions.test.ts @@ -1,5 +1,15 @@ +import { createGlobalStore } from "@openmrs/esm-state"; import { attach, registerExtensionSlot } from "./extensions"; +const mockSessionStore = createGlobalStore("mock-session-store", { + loaded: false, + session: null, +}); + +jest.mock("@openmrs/esm-api", () => ({ + getSessionStore: jest.fn(() => mockSessionStore), +})); + describe("extensions system", () => { it("shouldn't crash when a slot is registered before the extensions that go in it", () => { attach("mario-slot", "mario-hat"); diff --git a/packages/framework/esm-extensions/src/extensions.ts b/packages/framework/esm-extensions/src/extensions.ts index ca18ba757..a10daf693 100644 --- a/packages/framework/esm-extensions/src/extensions.ts +++ b/packages/framework/esm-extensions/src/extensions.ts @@ -8,6 +8,7 @@ * - connected (computed from assigned using connectivity and online / offline) */ +import { getSessionStore, LoggedInUser, userHasAccess } from "@openmrs/esm-api"; import { ExtensionsConfigStore, ExtensionSlotConfigObject, @@ -19,6 +20,7 @@ import { getExtensionSlotsConfigStore, } from "@openmrs/esm-config"; import isEqual from "lodash-es/isEqual"; +import isUndefined from "lodash-es/isUndefined"; import { getExtensionInternalStore, ExtensionSlotState, @@ -301,6 +303,8 @@ function getAssignedExtensionsFromSlotData( const attachedIds = internalState.slots[slotName].attachedIds; const assignedIds = calculateAssignedIds(config, attachedIds); const extensions: Array = []; + let user: LoggedInUser | undefined = undefined; + for (let id of assignedIds) { const { config: extensionConfig } = getExtensionConfigFromStore( extensionConfigStoreState, @@ -311,6 +315,27 @@ function getAssignedExtensionsFromSlotData( const extension = internalState.extensions[name]; // if the extension has not been registered yet, do not include it if (extension) { + const requiredPrivileges = + extensionConfig?.["Display conditions"]?.privileges ?? + extension.privileges; + if ( + requiredPrivileges && + (typeof requiredPrivileges === "string" || + (Array.isArray(requiredPrivileges) && requiredPrivileges.length > 0)) + ) { + if (isUndefined(user)) { + user = getSessionStore().getState().session?.user; + } + + if (!user) { + continue; + } + + if (!userHasAccess(requiredPrivileges, user)) { + continue; + } + } + extensions.push({ id, name, @@ -325,6 +350,12 @@ function getAssignedExtensionsFromSlotData( return extensions; } +/** + * Gets the list of extensions assigned to a given slot + * + * @param slotName The slot to load the assigned extensions for + * @returns An array of extensions assigned to the named slot + */ export function getAssignedExtensions( slotName: string ): Array { diff --git a/packages/framework/esm-extensions/src/store.ts b/packages/framework/esm-extensions/src/store.ts index 2b23e1c80..0143aec39 100644 --- a/packages/framework/esm-extensions/src/store.ts +++ b/packages/framework/esm-extensions/src/store.ts @@ -20,6 +20,7 @@ export interface ExtensionRegistration { order?: number; online?: boolean | object; offline?: boolean | object; + privileges?: string | string[]; } export interface ExtensionInfo extends ExtensionRegistration { diff --git a/packages/framework/esm-framework/docs/API.md b/packages/framework/esm-framework/docs/API.md index 3da944066..f722e2134 100644 --- a/packages/framework/esm-framework/docs/API.md +++ b/packages/framework/esm-framework/docs/API.md @@ -198,7 +198,7 @@ ___ #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:16](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L16) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:17](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L17) ___ @@ -228,7 +228,7 @@ ___ #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:14](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L14) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:15](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L15) ___ @@ -245,7 +245,7 @@ ___ #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:21](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L21) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:22](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L22) ___ @@ -382,7 +382,7 @@ ___ #### Defined in -[packages/framework/esm-config/src/types.ts:32](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L32) +[packages/framework/esm-config/src/types.ts:37](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L37) ___ @@ -409,7 +409,7 @@ ___ #### Defined in -[packages/framework/esm-config/src/types.ts:60](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L60) +[packages/framework/esm-config/src/types.ts:65](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L65) ___ @@ -453,7 +453,7 @@ ___ #### Defined in -[packages/framework/esm-config/src/types.ts:67](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L67) +[packages/framework/esm-config/src/types.ts:72](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L72) ___ @@ -477,7 +477,7 @@ ___ #### Defined in -[packages/framework/esm-config/src/types.ts:65](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L65) +[packages/framework/esm-config/src/types.ts:70](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L70) ___ @@ -757,7 +757,7 @@ ___ #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:174](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L174) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:188](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L188) ___ @@ -823,7 +823,7 @@ leak and source of bugs. #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:70](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L70) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:71](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L71) ▸ **getCurrentUser**(`opts`): `Observable`<[`Session`](interfaces/Session.md)\> @@ -840,7 +840,7 @@ leak and source of bugs. #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:71](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L71) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:72](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L72) ▸ **getCurrentUser**(`opts`): `Observable`<[`LoggedInUser`](interfaces/LoggedInUser.md)\> @@ -857,7 +857,7 @@ leak and source of bugs. #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:72](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L72) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:73](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L73) ___ @@ -885,7 +885,7 @@ ___ #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:188](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L188) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:202](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L202) ___ @@ -899,7 +899,7 @@ ___ #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:206](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L206) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:220](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L220) ___ @@ -913,7 +913,7 @@ ___ #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:104](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L104) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:105](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L105) ___ @@ -1122,7 +1122,7 @@ refetchCurrentUser() #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:149](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L149) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:163](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L163) ___ @@ -1164,7 +1164,7 @@ ___ #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:215](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L215) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:229](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L229) ___ @@ -1365,24 +1365,24 @@ ___ ### userHasAccess -▸ **userHasAccess**(`requiredPrivilege`, `user`): `undefined` \| [`Privilege`](interfaces/Privilege.md) +▸ **userHasAccess**(`requiredPrivilege`, `user`): `boolean` #### Parameters | Name | Type | | :------ | :------ | -| `requiredPrivilege` | `string` | +| `requiredPrivilege` | `string` \| `string`[] | | `user` | `Object` | | `user.privileges` | [`Privilege`](interfaces/Privilege.md)[] | | `user.roles` | [`Role`](interfaces/Role.md)[] | #### Returns -`undefined` \| [`Privilege`](interfaces/Privilege.md) +`boolean` #### Defined in -[packages/framework/esm-api/src/shared-api-objects/current-user.ts:181](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L181) +[packages/framework/esm-api/src/shared-api-objects/current-user.ts:195](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-api/src/shared-api-objects/current-user.ts#L195) ___ @@ -1509,7 +1509,7 @@ for more information about defining a config schema. #### Defined in -[packages/framework/esm-config/src/module-config/module-config.ts:193](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/module-config/module-config.ts#L193) +[packages/framework/esm-config/src/module-config/module-config.ts:192](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/module-config/module-config.ts#L192) ___ @@ -1541,7 +1541,7 @@ for more information about defining a config schema. #### Defined in -[packages/framework/esm-config/src/module-config/module-config.ts:218](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/module-config/module-config.ts#L218) +[packages/framework/esm-config/src/module-config/module-config.ts:222](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/module-config/module-config.ts#L222) ___ @@ -1571,7 +1571,7 @@ of the execution of a function. #### Defined in -[packages/framework/esm-config/src/module-config/module-config.ts:254](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/module-config/module-config.ts#L254) +[packages/framework/esm-config/src/module-config/module-config.ts:264](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/module-config/module-config.ts#L264) ___ @@ -1592,7 +1592,7 @@ ___ #### Defined in -[packages/framework/esm-config/src/module-config/module-config.ts:234](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/module-config/module-config.ts#L234) +[packages/framework/esm-config/src/module-config/module-config.ts:244](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/module-config/module-config.ts#L244) ___ @@ -1608,6 +1608,7 @@ Use this React Hook to obtain your module's configuration. | Name | Type | Description | | :------ | :------ | :------ | +| `Display conditions?` | [`DisplayConditionsConfigObject`](interfaces/DisplayConditionsConfigObject.md) | - | | `constructor` | `Function` | The initial value of Object.prototype.constructor is the standard built-in Object constructor. | | `hasOwnProperty` | (`v`: `PropertyKey`) => `boolean` | Determines whether an object has a property with the specified name. | | `isPrototypeOf` | (`v`: `Object`) => `boolean` | Determines whether an object exists in another object's prototype chain. | @@ -2155,7 +2156,7 @@ writing a module for a specific implementation. #### Defined in -[packages/framework/esm-extensions/src/extensions.ts:172](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L172) +[packages/framework/esm-extensions/src/extensions.ts:174](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L174) ___ @@ -2178,7 +2179,7 @@ Avoid using this. Extension attachments should be considered declarative. #### Defined in -[packages/framework/esm-extensions/src/extensions.ts:203](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L203) +[packages/framework/esm-extensions/src/extensions.ts:205](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L205) ___ @@ -2200,7 +2201,7 @@ Avoid using this. Extension attachments should be considered declarative. #### Defined in -[packages/framework/esm-extensions/src/extensions.ts:227](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L227) +[packages/framework/esm-extensions/src/extensions.ts:229](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L229) ___ @@ -2208,19 +2209,23 @@ ___ ▸ **getAssignedExtensions**(`slotName`): [`AssignedExtension`](interfaces/AssignedExtension.md)[] +Gets the list of extensions assigned to a given slot + #### Parameters -| Name | Type | -| :------ | :------ | -| `slotName` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `slotName` | `string` | The slot to load the assigned extensions for | #### Returns [`AssignedExtension`](interfaces/AssignedExtension.md)[] +An array of extensions assigned to the named slot + #### Defined in -[packages/framework/esm-extensions/src/extensions.ts:328](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L328) +[packages/framework/esm-extensions/src/extensions.ts:359](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L359) ___ @@ -2246,7 +2251,7 @@ A list of extensions that should be rendered #### Defined in -[packages/framework/esm-extensions/src/extensions.ts:285](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L285) +[packages/framework/esm-extensions/src/extensions.ts:287](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L287) ___ @@ -2278,7 +2283,7 @@ getExtensionNameFromId("baz") #### Defined in -[packages/framework/esm-extensions/src/extensions.ts:116](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L116) +[packages/framework/esm-extensions/src/extensions.ts:118](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/extensions.ts#L118) ___ @@ -2296,7 +2301,7 @@ extension system. #### Defined in -[packages/framework/esm-extensions/src/store.ts:129](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L129) +[packages/framework/esm-extensions/src/store.ts:130](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L130) ___ diff --git a/packages/framework/esm-framework/docs/interfaces/AssignedExtension.md b/packages/framework/esm-framework/docs/interfaces/AssignedExtension.md index 07e033671..f1c6b3dd2 100644 --- a/packages/framework/esm-framework/docs/interfaces/AssignedExtension.md +++ b/packages/framework/esm-framework/docs/interfaces/AssignedExtension.md @@ -24,7 +24,7 @@ The extension's config. Note that this will be `null` until the slot is mounted. #### Defined in -[packages/framework/esm-extensions/src/store.ts:79](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L79) +[packages/framework/esm-extensions/src/store.ts:80](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L80) ___ @@ -34,7 +34,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/store.ts:74](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L74) +[packages/framework/esm-extensions/src/store.ts:75](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L75) ___ @@ -44,7 +44,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/store.ts:77](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L77) +[packages/framework/esm-extensions/src/store.ts:78](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L78) ___ @@ -54,7 +54,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/store.ts:76](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L76) +[packages/framework/esm-extensions/src/store.ts:77](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L77) ___ @@ -64,7 +64,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/store.ts:75](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L75) +[packages/framework/esm-extensions/src/store.ts:76](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L76) ___ @@ -74,7 +74,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/store.ts:81](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L81) +[packages/framework/esm-extensions/src/store.ts:82](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L82) ___ @@ -84,4 +84,4 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/store.ts:80](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L80) +[packages/framework/esm-extensions/src/store.ts:81](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L81) diff --git a/packages/framework/esm-framework/docs/interfaces/ComponentDefinition.md b/packages/framework/esm-framework/docs/interfaces/ComponentDefinition.md index 97c42295c..6fe1184aa 100644 --- a/packages/framework/esm-framework/docs/interfaces/ComponentDefinition.md +++ b/packages/framework/esm-framework/docs/interfaces/ComponentDefinition.md @@ -64,13 +64,14 @@ ___ ### privilege -• `Optional` **privilege**: `string` +• `Optional` **privilege**: `string` \| `string`[] -Defines the access privilege required for this component, if any. +Defines the access privilege(s) required for this component, if any. +If more than one privilege is provided, the user must have all specified permissions. #### Defined in -[packages/framework/esm-globals/src/types.ts:115](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L115) +[packages/framework/esm-globals/src/types.ts:116](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L116) ___ @@ -82,7 +83,7 @@ Defines resources that are loaded when the component should mount. #### Defined in -[packages/framework/esm-globals/src/types.ts:119](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L119) +[packages/framework/esm-globals/src/types.ts:120](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L120) ## Methods diff --git a/packages/framework/esm-framework/docs/interfaces/ConfigObject.md b/packages/framework/esm-framework/docs/interfaces/ConfigObject.md index 8b13089e3..c0fdb8d91 100644 --- a/packages/framework/esm-framework/docs/interfaces/ConfigObject.md +++ b/packages/framework/esm-framework/docs/interfaces/ConfigObject.md @@ -16,6 +16,7 @@ ### Properties +- [Display conditions](ConfigObject.md#display conditions) - [constructor](ConfigObject.md#constructor) ### Methods @@ -29,6 +30,16 @@ ## Properties +### Display conditions + +• `Optional` **Display conditions**: [`DisplayConditionsConfigObject`](DisplayConditionsConfigObject.md) + +#### Defined in + +[packages/framework/esm-config/src/types.ts:30](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L30) + +___ + ### constructor • **constructor**: `Function` diff --git a/packages/framework/esm-framework/docs/interfaces/ConnectedExtension.md b/packages/framework/esm-framework/docs/interfaces/ConnectedExtension.md index 331efb085..2cf5cfdce 100644 --- a/packages/framework/esm-framework/docs/interfaces/ConnectedExtension.md +++ b/packages/framework/esm-framework/docs/interfaces/ConnectedExtension.md @@ -21,7 +21,7 @@ The extension's config. Note that this will be `null` until the slot is mounted. #### Defined in -[packages/framework/esm-extensions/src/store.ts:89](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L89) +[packages/framework/esm-extensions/src/store.ts:90](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L90) ___ @@ -31,7 +31,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/store.ts:85](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L85) +[packages/framework/esm-extensions/src/store.ts:86](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L86) ___ @@ -41,7 +41,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/store.ts:87](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L87) +[packages/framework/esm-extensions/src/store.ts:88](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L88) ___ @@ -51,4 +51,4 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/store.ts:86](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L86) +[packages/framework/esm-extensions/src/store.ts:87](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L87) diff --git a/packages/framework/esm-framework/docs/interfaces/DisplayConditionsConfigObject.md b/packages/framework/esm-framework/docs/interfaces/DisplayConditionsConfigObject.md new file mode 100644 index 000000000..029bf4fc8 --- /dev/null +++ b/packages/framework/esm-framework/docs/interfaces/DisplayConditionsConfigObject.md @@ -0,0 +1,19 @@ +[@openmrs/esm-framework](../API.md) / DisplayConditionsConfigObject + +# Interface: DisplayConditionsConfigObject + +## Table of contents + +### Properties + +- [privileges](DisplayConditionsConfigObject.md#privileges) + +## Properties + +### privileges + +• `Optional` **privileges**: `string`[] + +#### Defined in + +[packages/framework/esm-config/src/types.ts:34](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L34) diff --git a/packages/framework/esm-framework/docs/interfaces/ExtensionDefinition.md b/packages/framework/esm-framework/docs/interfaces/ExtensionDefinition.md index 34dd465e9..484bf7534 100644 --- a/packages/framework/esm-framework/docs/interfaces/ExtensionDefinition.md +++ b/packages/framework/esm-framework/docs/interfaces/ExtensionDefinition.md @@ -54,7 +54,7 @@ ___ #### Defined in -[packages/framework/esm-globals/src/types.ts:134](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L134) +[packages/framework/esm-globals/src/types.ts:135](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L135) ___ @@ -66,7 +66,7 @@ The meta data used for reflection by other components #### Defined in -[packages/framework/esm-globals/src/types.ts:130](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L130) +[packages/framework/esm-globals/src/types.ts:131](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L131) ___ @@ -78,7 +78,7 @@ The name of the extension being registered #### Defined in -[packages/framework/esm-globals/src/types.ts:124](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L124) +[packages/framework/esm-globals/src/types.ts:125](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L125) ___ @@ -122,15 +122,16 @@ Specifies the relative order in which the extension renders in a slot #### Defined in -[packages/framework/esm-globals/src/types.ts:132](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L132) +[packages/framework/esm-globals/src/types.ts:133](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L133) ___ ### privilege -• `Optional` **privilege**: `string` +• `Optional` **privilege**: `string` \| `string`[] -Defines the access privilege required for this component, if any. +Defines the access privilege(s) required for this component, if any. +If more than one privilege is provided, the user must have all specified permissions. #### Inherited from @@ -138,7 +139,7 @@ Defines the access privilege required for this component, if any. #### Defined in -[packages/framework/esm-globals/src/types.ts:115](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L115) +[packages/framework/esm-globals/src/types.ts:116](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L116) ___ @@ -154,7 +155,7 @@ Defines resources that are loaded when the component should mount. #### Defined in -[packages/framework/esm-globals/src/types.ts:119](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L119) +[packages/framework/esm-globals/src/types.ts:120](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L120) ___ @@ -166,7 +167,7 @@ A slot to attach to #### Defined in -[packages/framework/esm-globals/src/types.ts:126](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L126) +[packages/framework/esm-globals/src/types.ts:127](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L127) ___ @@ -178,7 +179,7 @@ Slots to attach to #### Defined in -[packages/framework/esm-globals/src/types.ts:128](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L128) +[packages/framework/esm-globals/src/types.ts:129](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L129) ## Methods diff --git a/packages/framework/esm-framework/docs/interfaces/ExtensionRegistration.md b/packages/framework/esm-framework/docs/interfaces/ExtensionRegistration.md index 59a91b1bc..e205d9b2d 100644 --- a/packages/framework/esm-framework/docs/interfaces/ExtensionRegistration.md +++ b/packages/framework/esm-framework/docs/interfaces/ExtensionRegistration.md @@ -12,6 +12,7 @@ - [offline](ExtensionRegistration.md#offline) - [online](ExtensionRegistration.md#online) - [order](ExtensionRegistration.md#order) +- [privileges](ExtensionRegistration.md#privileges) ### Methods @@ -77,6 +78,16 @@ ___ [packages/framework/esm-extensions/src/store.ts:20](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L20) +___ + +### privileges + +• `Optional` **privileges**: `string` \| `string`[] + +#### Defined in + +[packages/framework/esm-extensions/src/store.ts:23](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L23) + ## Methods ### load diff --git a/packages/framework/esm-framework/docs/interfaces/ExtensionSlotConfig.md b/packages/framework/esm-framework/docs/interfaces/ExtensionSlotConfig.md index 999a0031a..7a7872bcd 100644 --- a/packages/framework/esm-framework/docs/interfaces/ExtensionSlotConfig.md +++ b/packages/framework/esm-framework/docs/interfaces/ExtensionSlotConfig.md @@ -19,7 +19,7 @@ #### Defined in -[packages/framework/esm-config/src/types.ts:41](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L41) +[packages/framework/esm-config/src/types.ts:46](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L46) ___ @@ -29,7 +29,7 @@ ___ #### Defined in -[packages/framework/esm-config/src/types.ts:44](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L44) +[packages/framework/esm-config/src/types.ts:49](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L49) ___ @@ -39,7 +39,7 @@ ___ #### Defined in -[packages/framework/esm-config/src/types.ts:43](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L43) +[packages/framework/esm-config/src/types.ts:48](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L48) ___ @@ -49,4 +49,4 @@ ___ #### Defined in -[packages/framework/esm-config/src/types.ts:42](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L42) +[packages/framework/esm-config/src/types.ts:47](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L47) diff --git a/packages/framework/esm-framework/docs/interfaces/ExtensionSlotConfigObject.md b/packages/framework/esm-framework/docs/interfaces/ExtensionSlotConfigObject.md index 6d35ca3da..e5a9d07fd 100644 --- a/packages/framework/esm-framework/docs/interfaces/ExtensionSlotConfigObject.md +++ b/packages/framework/esm-framework/docs/interfaces/ExtensionSlotConfigObject.md @@ -20,7 +20,7 @@ Additional extension IDs to assign to this slot, in addition to those `attach`ed #### Defined in -[packages/framework/esm-config/src/types.ts:53](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L53) +[packages/framework/esm-config/src/types.ts:58](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L58) ___ @@ -32,7 +32,7 @@ Overrides the default ordering of extensions. #### Defined in -[packages/framework/esm-config/src/types.ts:57](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L57) +[packages/framework/esm-config/src/types.ts:62](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L62) ___ @@ -44,4 +44,4 @@ Extension IDs which were `attach`ed to the slot but which should not be assigned #### Defined in -[packages/framework/esm-config/src/types.ts:55](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L55) +[packages/framework/esm-config/src/types.ts:60](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-config/src/types.ts#L60) diff --git a/packages/framework/esm-framework/docs/interfaces/ExtensionSlotState.md b/packages/framework/esm-framework/docs/interfaces/ExtensionSlotState.md index 8e3cc2116..d9961d0ac 100644 --- a/packages/framework/esm-framework/docs/interfaces/ExtensionSlotState.md +++ b/packages/framework/esm-framework/docs/interfaces/ExtensionSlotState.md @@ -17,7 +17,7 @@ #### Defined in -[packages/framework/esm-extensions/src/store.ts:70](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L70) +[packages/framework/esm-extensions/src/store.ts:71](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L71) ___ @@ -27,4 +27,4 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/store.ts:69](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L69) +[packages/framework/esm-extensions/src/store.ts:70](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L70) diff --git a/packages/framework/esm-framework/docs/interfaces/ExtensionStore.md b/packages/framework/esm-framework/docs/interfaces/ExtensionStore.md index 66aa2b4d5..88b859343 100644 --- a/packages/framework/esm-framework/docs/interfaces/ExtensionStore.md +++ b/packages/framework/esm-framework/docs/interfaces/ExtensionStore.md @@ -16,4 +16,4 @@ #### Defined in -[packages/framework/esm-extensions/src/store.ts:65](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L65) +[packages/framework/esm-extensions/src/store.ts:66](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-extensions/src/store.ts#L66) diff --git a/packages/framework/esm-framework/docs/interfaces/PageDefinition.md b/packages/framework/esm-framework/docs/interfaces/PageDefinition.md index e2054ca0f..9b89f92ca 100644 --- a/packages/framework/esm-framework/docs/interfaces/PageDefinition.md +++ b/packages/framework/esm-framework/docs/interfaces/PageDefinition.md @@ -82,15 +82,16 @@ The order in which to load the page. This determines DOM order. #### Defined in -[packages/framework/esm-globals/src/types.ts:145](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L145) +[packages/framework/esm-globals/src/types.ts:146](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L146) ___ ### privilege -• `Optional` **privilege**: `string` +• `Optional` **privilege**: `string` \| `string`[] -Defines the access privilege required for this component, if any. +Defines the access privilege(s) required for this component, if any. +If more than one privilege is provided, the user must have all specified permissions. #### Inherited from @@ -98,7 +99,7 @@ Defines the access privilege required for this component, if any. #### Defined in -[packages/framework/esm-globals/src/types.ts:115](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L115) +[packages/framework/esm-globals/src/types.ts:116](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L116) ___ @@ -114,7 +115,7 @@ Defines resources that are loaded when the component should mount. #### Defined in -[packages/framework/esm-globals/src/types.ts:119](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L119) +[packages/framework/esm-globals/src/types.ts:120](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L120) ___ @@ -126,7 +127,7 @@ The route of the page. #### Defined in -[packages/framework/esm-globals/src/types.ts:141](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L141) +[packages/framework/esm-globals/src/types.ts:142](https://github.com/openmrs/openmrs-esm-core/blob/master/packages/framework/esm-globals/src/types.ts#L142) ## Methods diff --git a/packages/framework/esm-framework/docs/interfaces/UserHasAccessProps.md b/packages/framework/esm-framework/docs/interfaces/UserHasAccessProps.md index 2db0c58d8..63c87a4f2 100644 --- a/packages/framework/esm-framework/docs/interfaces/UserHasAccessProps.md +++ b/packages/framework/esm-framework/docs/interfaces/UserHasAccessProps.md @@ -23,7 +23,7 @@ ___ ### privilege -• **privilege**: `string` +• **privilege**: `string` \| `string`[] #### Defined in diff --git a/packages/framework/esm-framework/mock.tsx b/packages/framework/esm-framework/mock.tsx index 8bd189c3f..b8c34b380 100644 --- a/packages/framework/esm-framework/mock.tsx +++ b/packages/framework/esm-framework/mock.tsx @@ -3,6 +3,7 @@ import type {} from "@openmrs/esm-globals"; import createStore, { Store } from "unistore"; import { never, of } from "rxjs"; import { interpolateUrl } from "@openmrs/esm-config"; +import { SessionStore } from "@openmrs/esm-api"; export { parseDate, formatDate, @@ -51,7 +52,7 @@ export function getCurrentUser() { return of({ authenticated: false }); } -export const mockSessionStore = createGlobalStore("mock-session-store", { +export const mockSessionStore = createGlobalStore("mock-session-store", { loaded: false, session: null, }); diff --git a/packages/framework/esm-framework/src/integration-tests/extension-config.test.tsx b/packages/framework/esm-framework/src/integration-tests/extension-config.test.tsx index 7d9f3248a..5ff799e80 100644 --- a/packages/framework/esm-framework/src/integration-tests/extension-config.test.tsx +++ b/packages/framework/esm-framework/src/integration-tests/extension-config.test.tsx @@ -3,8 +3,6 @@ import { attach, registerExtension, updateInternalExtensionStore, - getExtensionInternalStore, - getExtensionStore, } from "../../../esm-extensions"; import { ExtensionSlot, @@ -22,7 +20,17 @@ import { getExtensionSlotsConfigStore, } from "../../../esm-config/src"; import { act, render, screen, waitFor } from "@testing-library/react"; -import { waitForElementToBeRemoved } from "@testing-library/dom"; +import { Person } from "@openmrs/esm-api"; +import { mockSessionStore } from "../../mock"; + +jest.mock("@openmrs/esm-api", () => { + const original = jest.requireActual("@openmrs/esm-api"); + return { + ...original, + getSessionStore: () => mockSessionStore, + refetchCurrentUser: jest.fn(), + }; +}); describe("Interaction between configuration and extension systems", () => { beforeEach(() => { @@ -244,12 +252,172 @@ describe("Interaction between configuration and extension systems", () => { }); expect(screen.getByText(/clothes/)).toHaveTextContent(/tiger/); }); + + test("should not show extension when user lacks configured privilege", async () => { + mockSessionStore.setState({ + loaded: true, + session: { + authenticated: true, + sessionId: "1", + user: { + uuid: "1", + display: "Non-Admin", + username: "nonadmin", + systemId: "nonadmin", + userProperties: {}, + person: {} as Person, + privileges: [], + roles: [], + retired: false, + locale: "en", + allowedLocales: ["en"], + }, + }, + }); + + registerSimpleExtension("Schmoo", "esm-bedrock", true); + registerSimpleExtension("Wilma", "esm-flintstones", true); + attach("A slot", "Schmoo"); + attach("A slot", "Wilma"); + defineConfigSchema("esm-bedrock", {}); + defineConfigSchema("esm-flintstones", {}); + provide({ + "esm-bedrock": { + "Display conditions": { + privileges: ["Yabadabadoo!"], + }, + }, + }); + provide({ + "esm-flintstones": {}, + }); + + function RootComponent() { + return ( +
+ +
+ ); + } + const App = openmrsComponentDecorator({ + moduleName: "esm-bedrock", + featureName: "Bedrock", + disableTranslations: true, + })(RootComponent); + + render(); + await waitFor(() => expect(screen.getByTestId(/slot/)).toBeInTheDocument()); + expect(screen.getByText(/\bWilma\b/)).toBeVisible(); + expect(screen.queryAllByText(/\bSchmoo\b/)).toHaveLength(0); + }); + + test("should show extension when user has configured privilege", async () => { + mockSessionStore.setState({ + loaded: true, + session: { + authenticated: true, + sessionId: "1", + user: { + uuid: "1", + display: "Non-Admin", + username: "nonadmin", + systemId: "nonadmin", + userProperties: {}, + person: {} as Person, + privileges: [{ uuid: "1", display: "Yabadabadoo!" }], + roles: [], + retired: false, + locale: "en", + allowedLocales: ["en"], + }, + }, + }); + + registerSimpleExtension("Schmoo", "esm-bedrock", true); + attach("A slot", "Schmoo"); + defineConfigSchema("esm-bedrock", {}); + provide({ + "esm-bedrock": { + "Display conditions": { + privileges: ["Yabadabadoo!"], + }, + }, + }); + + function RootComponent() { + return ( +
+ +
+ ); + } + const App = openmrsComponentDecorator({ + moduleName: "esm-bedrock", + featureName: "Bedrock", + disableTranslations: true, + })(RootComponent); + + render(); + await waitFor(() => expect(screen.getByTestId(/slot/)).toBeInTheDocument()); + expect(screen.getByText(/\bSchmoo\b/)).toBeVisible(); + }); + + test("should only show extensions users have default privilege for", async () => { + mockSessionStore.setState({ + loaded: true, + session: { + authenticated: true, + sessionId: "1", + user: { + uuid: "1", + display: "Non-Admin", + username: "nonadmin", + systemId: "nonadmin", + userProperties: {}, + person: {} as Person, + privileges: [{ uuid: "1", display: "YOWTCH!" }], + roles: [], + retired: false, + locale: "en", + allowedLocales: ["en"], + }, + }, + }); + + registerSimpleExtension("Schmoo", "esm-bedrock", true, "Yabadabadoo!"); + registerSimpleExtension("Wilma", "esm-flintstones", true, "YOWTCH!"); + attach("A slot", "Schmoo"); + attach("A slot", "Wilma"); + defineConfigSchema("esm-bedrock", {}); + defineConfigSchema("esm-flintstones", {}); + provide({ "esm-bedrock": {} }); + provide({ "esm-flintstones": {} }); + + function RootComponent() { + return ( +
+ +
+ ); + } + const App = openmrsComponentDecorator({ + moduleName: "esm-bedrock", + featureName: "Bedrock", + disableTranslations: true, + })(RootComponent); + + render(); + await waitFor(() => expect(screen.getByTestId(/slot/)).toBeInTheDocument()); + expect(screen.getByText(/\bWilma\b/)).toBeVisible(); + expect(screen.queryAllByText(/\bSchmoo\b/)).toHaveLength(0); + }); }); function registerSimpleExtension( name: string, moduleName: string, - takesConfig: boolean = false + takesConfig: boolean = false, + privileges?: string | string[] ) { const SimpleComponent = () =>
{name}
; const ConfigurableComponent = () => { @@ -272,5 +440,6 @@ function registerSimpleExtension( } ), meta: {}, + privileges, }); } diff --git a/packages/framework/esm-globals/src/types.ts b/packages/framework/esm-globals/src/types.ts index 19b1a1b44..fb10d6a54 100644 --- a/packages/framework/esm-globals/src/types.ts +++ b/packages/framework/esm-globals/src/types.ts @@ -110,9 +110,10 @@ export interface ComponentDefinition { */ offline?: boolean | object; /** - * Defines the access privilege required for this component, if any. + * Defines the access privilege(s) required for this component, if any. + * If more than one privilege is provided, the user must have all specified permissions. */ - privilege?: string; + privilege?: string | string[]; /** * Defines resources that are loaded when the component should mount. */ diff --git a/packages/framework/esm-react-utils/src/UserHasAccess.tsx b/packages/framework/esm-react-utils/src/UserHasAccess.tsx index edceeef40..f2d42e675 100644 --- a/packages/framework/esm-react-utils/src/UserHasAccess.tsx +++ b/packages/framework/esm-react-utils/src/UserHasAccess.tsx @@ -3,7 +3,7 @@ import { getCurrentUser, LoggedInUser, userHasAccess } from "@openmrs/esm-api"; import React, { useEffect, useState } from "react"; export interface UserHasAccessProps { - privilege: string; + privilege: string | string[]; fallback?: React.ReactNode; } diff --git a/packages/shell/esm-app-shell/src/apps.ts b/packages/shell/esm-app-shell/src/apps.ts index 146d56e3d..33eaf2c78 100644 --- a/packages/shell/esm-app-shell/src/apps.ts +++ b/packages/shell/esm-app-shell/src/apps.ts @@ -47,10 +47,10 @@ function trySetup(appName: string, setup: () => any): any { function getLoader( load: () => Promise, resources?: Record, - privilege?: string + privileges?: string | string[] ): () => Promise { - if (typeof privilege === "string") { - load = wrapLifecycle(load, privilege); + if (typeof privileges === "string" || Array.isArray(privileges)) { + load = wrapLifecycle(load, privileges); } if (typeof resources === "object") { @@ -207,6 +207,7 @@ To fix this, ensure that you define a "load" function inside the extension defin moduleName, offline: extension.offline, online: extension.online, + privileges: extension.privilege, }); for (const slot of slots) { diff --git a/packages/shell/esm-app-shell/src/helpers.ts b/packages/shell/esm-app-shell/src/helpers.ts index 96515bace..7fcc9d361 100644 --- a/packages/shell/esm-app-shell/src/helpers.ts +++ b/packages/shell/esm-app-shell/src/helpers.ts @@ -25,14 +25,12 @@ export function routeRegex(regex: RegExp, location: Location) { export function wrapLifecycle( load: () => Promise, - requiredPrivilege: string + requiredPrivileges: string | Array ): () => Promise { return async () => { const user = await getLoggedInUser(); - if (user && userHasAccess(requiredPrivilege, user)) { - return load(); - } - - return emptyLifecycle; + return user && userHasAccess(requiredPrivileges, user) + ? load() + : emptyLifecycle; }; } diff --git a/packages/shell/esm-app-shell/src/run.ts b/packages/shell/esm-app-shell/src/run.ts index d623d6328..9d1f62ce1 100644 --- a/packages/shell/esm-app-shell/src/run.ts +++ b/packages/shell/esm-app-shell/src/run.ts @@ -21,7 +21,6 @@ import { dispatchPrecacheStaticDependencies, activateOfflineCapability, subscribePrecacheStaticDependencies, - openmrsFetch, } from "@openmrs/esm-framework/src/internal"; import { finishRegisteringAllApps,