diff --git a/.changeset/cyan-seahorses-film.md b/.changeset/cyan-seahorses-film.md new file mode 100644 index 0000000000..80a27baec0 --- /dev/null +++ b/.changeset/cyan-seahorses-film.md @@ -0,0 +1,11 @@ +--- +'@backstage/core-plugin-api': minor +--- + +**BREAKING CHANGE** The `StorageApi` has received several updates that fills in gaps for some use-cases and makes it easier to avoid mistakes: + +- The `StorageValueChange` type has been renamed to `StorageValueSnapshot`, the `newValue` property has been renamed to `value`, the stored value type has been narrowed to `JsonValue`, and it has received a new `presence` property that is `'unknown'`, `'absent'`, or `'present'`. +- The `get` method has been deprecated in favor of a new `snapshot` method, which returns a `StorageValueSnapshot`. +- The `observe$` method has had its contract changed. It should now emit values when the `presence` of a key changes, this may for example happen when remotely stored values are requested on page load and the presence switches from `'unknown'` to either `'absent'` or `'present'`. + +The above changes have been made with deprecations in place to maintain much of the backwards compatibility for consumers of the `StorageApi`. The only breaking change is the narrowing of the stored value type, which may in some cases require the addition of an explicit type parameter to the `get` and `observe$` methods. diff --git a/.changeset/gorgeous-beers-teach.md b/.changeset/gorgeous-beers-teach.md new file mode 100644 index 0000000000..014398a5be --- /dev/null +++ b/.changeset/gorgeous-beers-teach.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': minor +--- + +Updated `WebStorageApi` to reflect the `StorageApi` changes in `@backstage/core-plugin-api`. diff --git a/.changeset/green-timers-film.md b/.changeset/green-timers-film.md new file mode 100644 index 0000000000..be94b13f0d --- /dev/null +++ b/.changeset/green-timers-film.md @@ -0,0 +1,5 @@ +--- +'@backstage/test-utils': minor +--- + +Updated `MockStorageApi` to reflect the `StorageApi` changes in `@backstage/core-plugin-api`. diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 8746211a45..f94979f975 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -39,6 +39,7 @@ import { gitlabAuthApiRef } from '@backstage/core-plugin-api'; import { googleAuthApiRef } from '@backstage/core-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { IdentityApi } from '@backstage/core-plugin-api'; +import { JsonValue } from '@backstage/types'; import { microsoftAuthApiRef } from '@backstage/core-plugin-api'; import { OAuthApi } from '@backstage/core-plugin-api'; import { OAuthRequestApi } from '@backstage/core-plugin-api'; @@ -59,7 +60,7 @@ import { RouteRef } from '@backstage/core-plugin-api'; import { SessionApi } from '@backstage/core-plugin-api'; import { SessionState } from '@backstage/core-plugin-api'; import { StorageApi } from '@backstage/core-plugin-api'; -import { StorageValueChange } from '@backstage/core-plugin-api'; +import { StorageValueSnapshot } from '@backstage/core-plugin-api'; import { SubRouteRef } from '@backstage/core-plugin-api'; // @public @@ -606,10 +607,12 @@ export class WebStorage implements StorageApi { // (undocumented) get(key: string): T | undefined; // (undocumented) - observe$(key: string): Observable>; + observe$(key: string): Observable>; // (undocumented) remove(key: string): Promise; // (undocumented) set(key: string, data: T): Promise; + // (undocumented) + snapshot(key: string): StorageValueSnapshot; } ``` diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index c9045a21d4..928a90cee5 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -10,6 +10,7 @@ import { ComponentType } from 'react'; import { Config } from '@backstage/config'; import { IconComponent as IconComponent_2 } from '@backstage/core-plugin-api'; import { IdentityApi as IdentityApi_2 } from '@backstage/core-plugin-api'; +import { JsonValue } from '@backstage/types'; import { Observable } from '@backstage/types'; import { ProfileInfo as ProfileInfo_2 } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; @@ -762,20 +763,37 @@ export type SignInResult = { // @public export interface StorageApi { forBucket(name: string): StorageApi; - get(key: string): T | undefined; - observe$(key: string): Observable>; + // @deprecated + get(key: string): T | undefined; + observe$( + key: string, + ): Observable>; remove(key: string): Promise; - set(key: string, data: any): Promise; + set(key: string, data: T): Promise; + snapshot(key: string): StorageValueSnapshot; } // @public export const storageApiRef: ApiRef; +// @public @deprecated (undocumented) +export type StorageValueChange = + StorageValueSnapshot; + // @public -export type StorageValueChange = { - key: string; - newValue?: T; -}; +export type StorageValueSnapshot = + | { + key: string; + presence: 'unknown' | 'absent'; + value?: undefined; + newValue?: undefined; + } + | { + key: string; + presence: 'present'; + value: TValue; + newValue?: TValue; + }; // @public export type SubRouteRef = { diff --git a/packages/core-plugin-api/src/apis/definitions/StorageApi.ts b/packages/core-plugin-api/src/apis/definitions/StorageApi.ts index 8dcfe6739a..4bd49f9b83 100644 --- a/packages/core-plugin-api/src/apis/definitions/StorageApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/StorageApi.ts @@ -38,7 +38,10 @@ export type StorageValueSnapshot = newValue?: TValue; }; -/** @deprecated Use StorageValueSnapshot instead */ +/** + * @public + * @deprecated Use StorageValueSnapshot instead + */ export type StorageValueChange = StorageValueSnapshot; diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index 24f17e0b84..a75a6d958e 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -12,13 +12,14 @@ import { ErrorApi } from '@backstage/core-plugin-api'; import { ErrorApiError } from '@backstage/core-plugin-api'; import { ErrorApiErrorContext } from '@backstage/core-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; +import { JsonValue } from '@backstage/types'; import { Observable } from '@backstage/types'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; import { RenderResult } from '@testing-library/react'; import { RouteRef } from '@backstage/core-plugin-api'; import { StorageApi } from '@backstage/core-plugin-api'; -import { StorageValueChange } from '@backstage/core-plugin-api'; +import { StorageValueSnapshot } from '@backstage/core-plugin-api'; // @public export type AsyncLogCollector = () => Promise; @@ -81,11 +82,13 @@ export class MockStorageApi implements StorageApi { // (undocumented) get(key: string): T | undefined; // (undocumented) - observe$(key: string): Observable>; + observe$(key: string): Observable>; // (undocumented) remove(key: string): Promise; // (undocumented) set(key: string, data: T): Promise; + // (undocumented) + snapshot(key: string): StorageValueSnapshot; } // @public