diff --git a/PACKAGES.md b/PACKAGES.md index b2d83ed86478..cb62eb4ea8e5 100644 --- a/PACKAGES.md +++ b/PACKAGES.md @@ -183,7 +183,7 @@ The dependencies between layers are enforced by the layer-check command._ | Packages | Layer Dependencies | | --- | --- | -| - [@fluidframework/azure-client](/packages/service-clients/azure-client)
- [@fluidframework/odsp-client](/packages/service-clients/odsp-client)
- [@fluidframework/tinylicious-client](/packages/service-clients/tinylicious-client)
 
 
 
 
 
 
 
 
  | - [Core-Interfaces](#Core-Interfaces)
- [Driver-Definitions](#Driver-Definitions)
- [Container-Definitions](#Container-Definitions)
- [Core-Utils](#Core-Utils)
- [Telemetry-Utils](#Telemetry-Utils)
- [Driver-Utils](#Driver-Utils)
- [Other-Utils](#Other-Utils)
- [Driver](#Driver)
- [Loader](#Loader)
- [Runtime](#Runtime)
- [Framework](#Framework)
- [Routerlicious-Driver](#Routerlicious-Driver) | +| - [@fluidframework/azure-client](/packages/service-clients/azure-client)
- [@fluidframework/odsp-client](/packages/service-clients/odsp-client)
- [@fluidframework/tinylicious-client](/packages/service-clients/tinylicious-client)
 
 
 
 
 
 
 
 
 
  | - [Core-Interfaces](#Core-Interfaces)
- [Driver-Definitions](#Driver-Definitions)
- [Container-Definitions](#Container-Definitions)
- [Core-Utils](#Core-Utils)
- [Client-Utils](#Client-Utils)
- [Telemetry-Utils](#Telemetry-Utils)
- [Driver-Utils](#Driver-Utils)
- [Other-Utils](#Other-Utils)
- [Driver](#Driver)
- [Loader](#Loader)
- [Runtime](#Runtime)
- [Framework](#Framework)
- [Routerlicious-Driver](#Routerlicious-Driver) | ### Examples diff --git a/packages/service-clients/odsp-client/api-report/odsp-client.alpha.api.md b/packages/service-clients/odsp-client/api-report/odsp-client.alpha.api.md index 8a0462d5c31d..b68a069090f5 100644 --- a/packages/service-clients/odsp-client/api-report/odsp-client.alpha.api.md +++ b/packages/service-clients/odsp-client/api-report/odsp-client.alpha.api.md @@ -7,6 +7,12 @@ // @beta export type IOdspAudience = IServiceAudience; +// @beta @sealed +export interface IOdspContainerServicesEvents extends IEvent { + (event: "readOnlyStateChanged", listener: (readonly: boolean) => void): void; + (event: "sensitivityLabelChanged", listener: (sensitivityLabelsInfo: string) => void): void; +} + // @beta export interface IOdspFluidContainer extends IFluidContainer { attach(props?: ContainerAttachProps): Promise; @@ -54,9 +60,11 @@ export interface OdspContainerAttachProps { filePath: string | undefined; } -// @beta -export interface OdspContainerServices { +// @beta @sealed +export interface OdspContainerServices extends IEventProvider, IDisposable { audience: IOdspAudience; + getReadOnlyState(): boolean | undefined; + getSensitivityLabelsInfo(): string | undefined; } // @beta diff --git a/packages/service-clients/odsp-client/api-report/odsp-client.beta.api.md b/packages/service-clients/odsp-client/api-report/odsp-client.beta.api.md index fc893e97c251..7eca80eab09c 100644 --- a/packages/service-clients/odsp-client/api-report/odsp-client.beta.api.md +++ b/packages/service-clients/odsp-client/api-report/odsp-client.beta.api.md @@ -7,6 +7,12 @@ // @beta export type IOdspAudience = IServiceAudience; +// @beta @sealed +export interface IOdspContainerServicesEvents extends IEvent { + (event: "readOnlyStateChanged", listener: (readonly: boolean) => void): void; + (event: "sensitivityLabelChanged", listener: (sensitivityLabelsInfo: string) => void): void; +} + // @beta export interface IOdspFluidContainer extends IFluidContainer { attach(props?: ContainerAttachProps): Promise; @@ -54,9 +60,11 @@ export interface OdspContainerAttachProps { filePath: string | undefined; } -// @beta -export interface OdspContainerServices { +// @beta @sealed +export interface OdspContainerServices extends IEventProvider, IDisposable { audience: IOdspAudience; + getReadOnlyState(): boolean | undefined; + getSensitivityLabelsInfo(): string | undefined; } // @beta diff --git a/packages/service-clients/odsp-client/package.json b/packages/service-clients/odsp-client/package.json index b54a896c7d30..d23e4ef12911 100644 --- a/packages/service-clients/odsp-client/package.json +++ b/packages/service-clients/odsp-client/package.json @@ -104,6 +104,7 @@ "temp-directory": "nyc/.nyc_output" }, "dependencies": { + "@fluid-internal/client-utils": "workspace:~", "@fluidframework/container-definitions": "workspace:~", "@fluidframework/container-loader": "workspace:~", "@fluidframework/core-interfaces": "workspace:~", diff --git a/packages/service-clients/odsp-client/src/index.ts b/packages/service-clients/odsp-client/src/index.ts index e59015a7ccfd..f44926900ec7 100644 --- a/packages/service-clients/odsp-client/src/index.ts +++ b/packages/service-clients/odsp-client/src/index.ts @@ -15,6 +15,7 @@ export type { IOdspAudience, + IOdspContainerServicesEvents, IOdspFluidContainer, OdspClientProps, OdspConnectionConfig, diff --git a/packages/service-clients/odsp-client/src/interfaces.ts b/packages/service-clients/odsp-client/src/interfaces.ts index 2096dd4333d7..ca1da7656d0f 100644 --- a/packages/service-clients/odsp-client/src/interfaces.ts +++ b/packages/service-clients/odsp-client/src/interfaces.ts @@ -5,6 +5,9 @@ import type { IConfigProviderBase, + IDisposable, + IEvent, + IEventProvider, ITelemetryBaseLogger, } from "@fluidframework/core-interfaces"; import type { @@ -79,6 +82,23 @@ export interface OdspContainerAttachProps { fileName: string | undefined; } +/** + * Events emitted by the ODSP container service to notify consumers of select + * container changes. + * @beta + * @sealed + */ +export interface IOdspContainerServicesEvents extends IEvent { + /** + * Emitted when the read-only state of the container changes. + */ + (event: "readOnlyStateChanged", listener: (readonly: boolean) => void): void; + /** + * Emitted when the sensitivity label of the container changes. + */ + (event: "sensitivityLabelChanged", listener: (sensitivityLabelsInfo: string) => void): void; +} + /** * ODSP version of the IFluidContainer interface. * @beta @@ -106,12 +126,33 @@ export interface IOdspFluidContainer< * how the data is handled within the FluidContainer itself, i.e. which data objects or DDSes to * use, will not be included here but rather on the FluidContainer class itself. * @beta + * @sealed */ -export interface OdspContainerServices { +export interface OdspContainerServices + extends IEventProvider, + IDisposable { /** * Provides an object that facilitates obtaining information about users present in the Fluid session, as well as listeners for roster changes triggered by users joining or leaving the session. */ audience: IOdspAudience; + + /** + * Gets the read-only state of the container, if available. + * This is not available until the container is in the "Connected" state. + * @remarks + * In the case that the read-only state cannot be determined, wait for the "readOnlyStateChanged" event to be emitted. + * @returns The read-only state (true when readonly, false when editable), or undefined if not available. + */ + getReadOnlyState(): boolean | undefined; + /** + * Gets the sensitivity labels info of the container, if available. + * This is not available until the container is in the "Connected" state, and will only be available + * if sensitivity labels have been applied to the container. + * @remarks + * In the case that the sensitivity labels info are expected but cannot be determined, wait for the "sensitivityLabelChanged" event to be emitted. + * @returns The sensitivity labels info string, or undefined if not available. + */ + getSensitivityLabelsInfo(): string | undefined; } /** diff --git a/packages/service-clients/odsp-client/src/odspContainerServices.ts b/packages/service-clients/odsp-client/src/odspContainerServices.ts index 10cf8926293e..e729e8fabad1 100644 --- a/packages/service-clients/odsp-client/src/odspContainerServices.ts +++ b/packages/service-clients/odsp-client/src/odspContainerServices.ts @@ -3,25 +3,71 @@ * Licensed under the MIT License. */ +import { TypedEventEmitter } from "@fluid-internal/client-utils"; import type { IContainer } from "@fluidframework/container-definitions/internal"; +import type { IDisposable } from "@fluidframework/core-interfaces"; import { createServiceAudience } from "@fluidframework/fluid-static/internal"; import type { IOdspAudience, OdspContainerServices as IOdspContainerServices, + IOdspContainerServicesEvents, } from "./interfaces.js"; import { createOdspAudienceMember } from "./odspAudience.js"; /** * @internal */ -export class OdspContainerServices implements IOdspContainerServices { +export class OdspContainerServices + extends TypedEventEmitter + implements IOdspContainerServices, IDisposable +{ + #disposed = false; + readonly #container: IContainer; + public readonly audience: IOdspAudience; public constructor(container: IContainer) { + super(); + this.#container = container; + this.#container.on("readonly", this.#readonlyEventHandler); + this.#container.on("metadataUpdate", this.#metadataUpdateEventHandler); this.audience = createServiceAudience({ - container, + container: this.#container, createServiceMember: createOdspAudienceMember, }); } + + readonly #readonlyEventHandler = (readonly: boolean): void => { + this.emit("readOnlyStateChanged", readonly); + }; + + readonly #metadataUpdateEventHandler = (metadata: Record): void => { + if (metadata.sensitivityLabelsInfo !== undefined) { + this.emit("sensitivityLabelChanged", metadata.sensitivityLabelsInfo); + } + }; + + public get disposed(): boolean { + return this.#disposed; + } + + public dispose(): void { + if (this.#disposed) { + return; + } + + this.#disposed = true; + this.#container.off("readonly", this.#readonlyEventHandler); + this.#container.off("metadataUpdate", this.#metadataUpdateEventHandler); + this.removeAllListeners(); + } + + public getReadOnlyState(): boolean | undefined { + return this.#container.readOnlyInfo.readonly; + } + + public getSensitivityLabelsInfo(): string | undefined { + return this.#container.containerMetadata.sensitivityLabelsInfo; + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 27c9dcd13b8b..318eecf7f252 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13546,6 +13546,9 @@ importers: packages/service-clients/odsp-client: dependencies: + '@fluid-internal/client-utils': + specifier: workspace:~ + version: link:../../common/client-utils '@fluidframework/container-definitions': specifier: workspace:~ version: link:../../common/container-definitions