const { getOne } = ServiceFactory.build({
destinationAddress: "",
basePath: `${URL}/foo`,
});
As a KOS model proceeds through its lifecycle there are hooks where it makes sense to hydrate the model with a baseline set of data from the backend.
Typically, models use a "baseline and delta" approach where models are populated with an initial set of data and then updated via events published through the WebSocket transport.
Data Services provide a useful pattern for managing the initial hydration phase and are typically called in either the loading
or activate
lifecycle hooks on the model.
The KOS SDK exposes a ServiceFactory
utility that provides access to some common data access functions that simplify interactions with the KOS backend
const { getOne } = ServiceFactory.build({
destinationAddress: "",
basePath: `${URL}/foo`,
});
The Service Factory will provide a number of useful functions including:
getOne: perform a GET operation that will return a single object
getAll: similar to getOne
with the difference being that the operation is expected to return a collection of items.
deleteModel: perform a DELETE operation that will remove a given model from the backend.
postModel: perform a POST operation that will add a given model to the backend
putModel: perform a PUT operation that will modify an existing model
The Service Factory can be initialized with some configuration data to provide default behavior to the returned helper functions.
basePath: the root path where all operations will be executed. This can be overridden on a per-operation basis.
getAllPath: overrides the base path for all calls to getAll.
getOnePath: overrides the base path for all calls to getOne
.
deleteModelPath: overrides the base path for all calls to deleteModel
.
addModelPath: overrides the base path for all calls to postModel
.
modifyModelPath: overrides the base path for all calls to putModel
.
all of these paths can be further overridden at the time the function is called as well |
A service module will export one or more services that can be invoked at the model layer.
For example, in the demo project, there is a basic service that will fetch the details for our newly created Demo Model.
// src/models/demo/services/demo-services.ts
import {resolveServiceUrl, ServiceFactory} from "@coca-cola/kos-ui-core";
const { URL } = resolveServiceUrl("DEMO_SERVICE");
const { getOne } = ServiceFactory.build({
destinationAddress: "",
basePath: `${URL}/foo`,
});
// interface for the Model Data Transfer Object. This is independent of the model specification as there are frequently subtle differences between the data received from the backend and how it is represented in the model.
interface DemoResponse {
id: string;
name: string;
}
/**
* @category Service
* Retrieves a single demo model
*/
export const getDemo = async (id: string) => {
// Overriding the URL to append the passed in model ID.
const response = await getOne<DemoResponse>({
urlOverride: `${URL}/foo/${id}`,
});
return response;
};
A KOS model can either import the service directly OR pass it in via the model Options depending on your requirements. In cases where a single model might need access to different services depending on the context, passing them in via the options provides a decoupled Inversion Of Control (IoC) option. This is also useful for isolated testing where it can be preferable to inject mock services rather than executing against the live backend. For basic cases, you can just import the service directly.
In the demo model the load
lifecycle hook is an appropriate location to invoke the service and update the model:
// src/models/demo/demo-model.ts
import {getDemo} from "./services/demo-services";
...
async load(): Promise<void> {
try {
log.info("loading demo model");
const response = await getDemo(this.id);
log.debug(`received response ${response}`);
// In async cases, use the kosAction wrapper to provide a
// stable closure that can reliably update the model
kosAction(() => {
const newName = response?.data.name;
if (newName) {
this.name = newName;
}
});
} catch(e) {
log.error(e);
throw e;
}
}