From fc23e54a4eb0349d68ab9a717e64be1ebb10b62a Mon Sep 17 00:00:00 2001 From: Michael Beckemeyer Date: Fri, 28 Jun 2024 16:22:56 +0200 Subject: [PATCH 1/2] Describe how to use package properties --- docs/README.md | 1 + docs/tutorials/HowToUseProperties.md | 335 ++++++++++++++++++ .../HowToUseProperties_CustomBackendUrl.png | Bin 0 -> 7536 bytes .../HowToUseProperties_DefaultBackendUrl.png | Bin 0 -> 7215 bytes .../HowToUseProperties_InitialAppStart.png | Bin 0 -> 17481 bytes 5 files changed, 336 insertions(+) create mode 100644 docs/tutorials/HowToUseProperties.md create mode 100644 docs/tutorials/HowToUseProperties_CustomBackendUrl.png create mode 100644 docs/tutorials/HowToUseProperties_DefaultBackendUrl.png create mode 100644 docs/tutorials/HowToUseProperties_InitialAppStart.png diff --git a/docs/README.md b/docs/README.md index 6bb1255..656e953 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,6 +18,7 @@ - [How to theme an app](./tutorials/HowToThemeAnApp.md) - [How to add custom styles](./tutorials/HowToAddCustomStyles.md) - [How to translate an App](./tutorials/HowToTranslateAnApp.md) + - [How to use properties](./tutorials/HowToUseProperties.md) - [How to provide an API](./tutorials/HowToProvideAnAPI.md) - [How to write tests](./tutorials/HowToWriteTests.md) - [How to publish a package](./tutorials/HowToPublishAPackage.md) diff --git a/docs/tutorials/HowToUseProperties.md b/docs/tutorials/HowToUseProperties.md new file mode 100644 index 0000000..b98b1d6 --- /dev/null +++ b/docs/tutorials/HowToUseProperties.md @@ -0,0 +1,335 @@ +# How to use properties + +Properties are configurable values associated with a [Package](../reference/Package.md). +Packages can use their [build.config.mjs](../reference/Package.md#properties) to define which properties they support and assign some optional default values. +Properties are often document as part of a package's public API. + +An app that uses a package can assign custom values to those properties, which may alter the behavior of the the package at runtime. + +It is best to think of package properties as _global_ configuration: given a property `prop` of a package `pkg`, all services and UI components implemented in that package will receive the _same_ value of `prop` when the application runs. +They are therefore not suited for fine grained customization, for example to provide different values for each function call or UI component. +Use JavaScript function parameters or React props for those use cases instead. + +## Using properties in a package + +We will implement a simple `search-service` package that "searches" for results using a backend service. +The URL to that backend service shall be configurable as a property. +We're going to develop the property configuration but we will skip the actual searching part of the implementation. + +### Setup + +First of all, create the `search-service` package: + +```jsonc +// src/packages/search-service/package.json +{ + "name": "search-service", + "private": true, + "peerDependencies": { + "@open-pioneer/runtime": "" + } +} +``` + +```js +// src/packages/search-service/build.config.mjs +import { defineBuildConfig } from "@open-pioneer/build-support"; + +export default defineBuildConfig({ + services: { + SearchService: { + provides: "search-service.SearchService" + } + } +}); +``` + +Execute `pnpm install` to set up the package's dependencies. + +The search service starts off as an empty class: + +```ts +// src/packages/search-service/SearchService.ts +export class SearchService { + constructor() { + console.debug("SearchService constructor: not implemented yet"); + } +} +``` + +Then, export the service from the package's `services.ts` entry point: + +```ts +// src/packages/search-service/services.ts +export { SearchService } from "./SearchService"; +``` + +Finally, we will also "use" the new service in our app: + +```jsonc +// src/apps/empty +{ + // ... + "dependencies": { + // Add the dependency + "search-service": "workspace:^" + } +} +``` + +```js +// src/apps/empty/build.config.mjs +import { defineBuildConfig } from "@open-pioneer/build-support"; + +export default defineBuildConfig({ + // ... + ui: { + references: ["search-service.SearchService"] + } +}); +``` + +Execute `pnpm install` again so the application can find the package. + +The user interface our of application access the service, but doesn't do anything with it yet: + +```tsx +import { Button, Container } from "@open-pioneer/chakra-integration"; +import { useService } from "open-pioneer:react-hooks"; + +export function AppUI() { + // TypeScript integration skipped in this tutorial, see "How to create a service" tutorial. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const searchService = useService("search-service.SearchService"); + return ( + + + + ); +} +``` + +The app should start and the service's constructor should run: + +![Initial app startup](./HowToUseProperties_InitialAppStart.png) + +### Defining the property + +We define the property `backendUrl` in the package `search-service` to control which backend service gets used by the service. +For this example, we also define a default value: + +```js +// src/packages/search-service/build.config.mjs +import { defineBuildConfig } from "@open-pioneer/build-support"; + +export default defineBuildConfig({ + // ... + properties: { + // Defines the property 'backendUrl' with the given default value. + // Use `null` if you don't want to define a default value (`propertiesMeta` can be used + // make those properties required). + backendUrl: "http://search-backend.example.com" + } +}); +``` + +Next, we will update the implementation of `SearchService` to read the property's value. +Just like service references, properties are passed to the service constructor by the framework: + +```ts +// src/packages/search-service/SearchService.ts +import { ServiceOptions } from "@open-pioneer/runtime"; + +export class SearchService { + private _backendUrl: string; + + // (1) + constructor(serviceOptions: ServiceOptions) { + this._backendUrl = serviceOptions.properties.backendUrl as string; // (2) + } + + search() { + console.log(`Searching using ${this._backendUrl} ...`); // (3) + } +} +``` + +- **(1)** + The `serviceOptions` parameter is provided by the framework. +- **(2)** + `serviceOptions.properties.backendUrl` contains the value of the property. +- **(3)** + Triggers a "search" and prints the current value of the backend url, for inspection. + +We will quickly update the application's UI to try the new method: + +```diff +import { Button, Container } from "@open-pioneer/chakra-integration"; +import { useService } from "open-pioneer:react-hooks"; + +export function AppUI() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const searchService = useService("search-service.SearchService"); + return ( + + + + ); +} +``` + +Clicking on the button unsurprisingly prints: + +![Console contains the property's default value.](./HowToUseProperties_DefaultBackendUrl.png) + +> NOTE: You can also read package properties in UI components using the `useProperties` hook. +> The values returned from that hook will be the same as in `serviceOptions.properties`. + +## Setting properties in an app + +Let's imagine that the `search-service` package is used across many apps with different deployment environments. +For one of those apps, we want to use a different `backendUrl` without altering the package's source code. + +To do so, simply update your `app.ts` to override the property: + +```ts +// src/apps/empty/app.ts +// ... +const Element = createCustomElement({ + component: AppUI, + appMetadata, + config: { + // (1) + properties: { + // (2) + "search-service": { + backendUrl: "http://custom-search-backend.example.com" + } + } + } +}); +``` + +- **(1)** + The `properties` object can define (or override) properties for every package in the application. +- **(2)** + Property values for the `search-service` package. + The key must be the exact name of the package. + The properties defined here should be supported by the package. + +Thats it. After reloading the application, pressing the button now prints: + +![Console contains the property's custom value.](./HowToUseProperties_CustomBackendUrl.png) + +## Providing TypeScript support + +Writing raw JSON objects as in **(2)** above can be error prone. + +Consider exporting a TypeScript interface that matches the properties defined by your package: + +```ts +// Somewhere in the `search-service` package +export interface SearchServiceProperties { + // Documentation... + backendUrl?: string; +} +``` + +Then your can use that interface in your `app.ts` to guard against typos and to provide suggestions: + +```ts +// src/apps/empty/app.ts +import { SearchServiceProperties } from "..."; + +const Element = createCustomElement({ + component: AppUI, + appMetadata, + config: { + properties: { + "search-service": { + backendUrl: "http://custom-search-backend.example.com" + } satisfies SearchServiceProperties + } + } +}); +``` + +## Dynamic property values + +The shown way to define properties in your `app.ts` defines a _fixed_ value for all instances of your application. +This is fine if you only have a single instance, and if that instance's configuration does not depend on any dynamic state. + +For example, if your application was used twice in the same `.html` file, both instances would use the same `backendUrl`: + +```html + +
+ + +
+ +``` + +To configure dynamic property values per application _instance_, use the `resolveConfig` callback. +This callback is called for every separate application instance. +The result of `resolveConfig` has priority over the contents of `config`, if both are present. + +```ts +// src/apps/empty/app.ts +import { createCustomElement } from "@open-pioneer/runtime"; +import * as appMetadata from "open-pioneer:app"; +import { AppUI } from "./AppUI"; + +const Element = createCustomElement({ + component: AppUI, + appMetadata, + + // Provide dynamic property values. + // In this case, we read an attribute from the host element (in the html file) + // and it to configure the backendUrl. + async resolveConfig(ctx) { + const backendUrl = ctx.getAttribute("backend-url"); + if (!backendUrl) { + throw new Error("backend url was not configured"); + } + return { + properties: { + "search-service": { + backendUrl: backendUrl + } + } + }; + } +}); + +customElements.define("empty-app", Element); +``` + +Now you can define the `backend-url` attribute on your web component: + +```html + +``` + +`resolveConfig` is the most powerful way to define properties or other configuration options. +Since it supports promises you can also use it to load properties from other data sources, such as a rest service (possibly even dependant on the current user session via cookies). +It should not be overused: a long running `resolveConfig` call will block the start of your application. + +## Further reading + +- [How to create a service](./HowToCreateAService.md) +- [Package reference](../reference/Package.md) +- [Services Reference](../reference/Services.md) diff --git a/docs/tutorials/HowToUseProperties_CustomBackendUrl.png b/docs/tutorials/HowToUseProperties_CustomBackendUrl.png new file mode 100644 index 0000000000000000000000000000000000000000..769b7b2b1de089f68257d2ba9bf0cdade1548ddc GIT binary patch literal 7536 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 jSd23HA3F)}eMSgoCqg9Cy~d{s%2) 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 literal 0 HcmV?d00001 diff --git a/docs/tutorials/HowToUseProperties_InitialAppStart.png b/docs/tutorials/HowToUseProperties_InitialAppStart.png new file mode 100644 index 0000000000000000000000000000000000000000..7c43d173a35425c71916d98b0ea46c7898a35b3d GIT binary patch literal 17481 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 literal 0 HcmV?d00001 From c840bafbce04dd98456b226d73d118d0742beb16 Mon Sep 17 00:00:00 2001 From: Antonia van Eek Date: Tue, 9 Jul 2024 09:37:37 +0200 Subject: [PATCH 2/2] Fix typos --- docs/tutorials/HowToUseProperties.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/tutorials/HowToUseProperties.md b/docs/tutorials/HowToUseProperties.md index b98b1d6..8fdf2c4 100644 --- a/docs/tutorials/HowToUseProperties.md +++ b/docs/tutorials/HowToUseProperties.md @@ -2,19 +2,19 @@ Properties are configurable values associated with a [Package](../reference/Package.md). Packages can use their [build.config.mjs](../reference/Package.md#properties) to define which properties they support and assign some optional default values. -Properties are often document as part of a package's public API. +Properties are often documented as part of a package's public API. -An app that uses a package can assign custom values to those properties, which may alter the behavior of the the package at runtime. +An app that uses a package can assign custom values to those properties, which may alter the behavior of the package at runtime. It is best to think of package properties as _global_ configuration: given a property `prop` of a package `pkg`, all services and UI components implemented in that package will receive the _same_ value of `prop` when the application runs. -They are therefore not suited for fine grained customization, for example to provide different values for each function call or UI component. +They are therefore not suited for fine-grained customization, for example to provide different values for each function call or UI component. Use JavaScript function parameters or React props for those use cases instead. ## Using properties in a package We will implement a simple `search-service` package that "searches" for results using a backend service. The URL to that backend service shall be configurable as a property. -We're going to develop the property configuration but we will skip the actual searching part of the implementation. +We're going to develop the property configuration, but we will skip the actual searching part of the implementation. ### Setup @@ -230,13 +230,13 @@ const Element = createCustomElement({ The key must be the exact name of the package. The properties defined here should be supported by the package. -Thats it. After reloading the application, pressing the button now prints: +That's it. After reloading the application, pressing the button now prints: ![Console contains the property's custom value.](./HowToUseProperties_CustomBackendUrl.png) ## Providing TypeScript support -Writing raw JSON objects as in **(2)** above can be error prone. +Writing raw JSON objects as in **(2)** above can be error-prone. Consider exporting a TypeScript interface that matches the properties defined by your package: @@ -248,7 +248,7 @@ export interface SearchServiceProperties { } ``` -Then your can use that interface in your `app.ts` to guard against typos and to provide suggestions: +Then you can use that interface in your `app.ts` to guard against typos and to provide suggestions: ```ts // src/apps/empty/app.ts @@ -272,7 +272,7 @@ const Element = createCustomElement({ The shown way to define properties in your `app.ts` defines a _fixed_ value for all instances of your application. This is fine if you only have a single instance, and if that instance's configuration does not depend on any dynamic state. -For example, if your application was used twice in the same `.html` file, both instances would use the same `backendUrl`: +For example, if your application is used twice in the same `.html` file, both instances would use the same `backendUrl`: ```html @@ -299,7 +299,7 @@ const Element = createCustomElement({ // Provide dynamic property values. // In this case, we read an attribute from the host element (in the html file) - // and it to configure the backendUrl. + // and use it to configure the backendUrl. async resolveConfig(ctx) { const backendUrl = ctx.getAttribute("backend-url"); if (!backendUrl) { @@ -326,7 +326,7 @@ Now you can define the `backend-url` attribute on your web component: `resolveConfig` is the most powerful way to define properties or other configuration options. Since it supports promises you can also use it to load properties from other data sources, such as a rest service (possibly even dependant on the current user session via cookies). -It should not be overused: a long running `resolveConfig` call will block the start of your application. +It should not be overused: a long-running `resolveConfig` call will block the start of your application. ## Further reading