Author:
Lesley Dean
Changed on:
8 July 2024
`byName`
The Fluent UX Framework provides a Component Library for quickly configuring Fluent web apps.
Currently these Components receive data from the Page Query, which is GraphQL. The Fluent Platform includes a number of REST APIs, not available via GraphQL. This means that the standard Components can't display REST based data.
Imagine if you could easily use the UX Framework Component Library to build and extend screens with data from these APIs?
This article will guide you through implementing a custom data provider component that makes it possible to display REST based data in the standard UX Framework Component Library.
Since the standard Components accept datasources from the page query defined in the manifest, the only way to display data from REST APIs is to write custom components using the Component SDK.
This would need to be done for each component where a custom data source is required, and potentially adds significant effort to implementation.
While the Component SDK provides the tools to achieve this, it would be ideal if we could reuse the existing component library, and configure an alternate data source in the manifest.
Imagine you needed to provide an enhanced Activity Viewer, Webhooks Dashboard, or display information from the Event API on your Orders Dashboard.
Additionally, you need to achieve this while maintaining a consistent look, feel, and configuration experience, while minimising the total cost of custom code components?
What if this could be achieved with one simple custom component, and work for all components in the library?
An example Dashboard using Event API as a datasource for the standard Dashboard Tile Component
Following the pattern of the default page component, let's decouple the data fetching and display logic so that we can use the standard cards, lists, and other library components.
Let's write a simple custom component as a manifest configurable data provider for nested components to use.
In this approach, the data provider component could accept an
`endpoint`
`props`
Example manifest usage:
1{
2 "component": "devrel.provider.rest",
3 "props": {
4 "endpoint": "api/v4.1/event",
5 "props": {
6 "context.entityType": "ORDER",
7 "eventType": "ORCHESTRATION",
8 "eventStatus": "FAILED",
9 "from": "{{dateStringFormatter (dateAdd hours=-24) 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' true}}"
10 }
11 },
12 "descendants": [
13 /* display components here... */
14 ]
15}
Language: plain_text
Name: Example REST Data Provider Configuration
Description:
Example target manifest configuration experience for components using REST data
Using the Component SDK, create a new file in the components folder called
`RestProvider.tsx`
Create a new Interface to accept the RestProviderProps:
1export interface RestProviderProps {
2 data: any;
3 endpoint: string;
4 props: Record<string, string>;
5}
Language: tsx
Name: RestProviderProps
Description:
Define the manifest configurable props for the RestDataProvider
The
`data`
The
`endpoint`
`props`
Now you can create the Component:
1export const RestProvider: FC<RestProviderProps> = ({
2 data,
3 endpoint,
4 props,
5 }) => {
6 // TODO: Implementation Code here...
7};
Language: tsx
Name: RestProviderComponent Definition
Description:
Define the RestProvider Component
A you can see, we make use of a React Function Component (
`FC`
`react`
The UX Framework provides the ability to pass page query and URL parameters into the props values. The best way to handle this is to support template strings as props, which then need to be “rendered”. The TemplateRegistry provides a render function to do this for you. So all we need to do is loop through the provided props, and call the render function.
Let’s create a separate function in the same file for this:
1const getRenderedProps = (props: Record<string, string>, data: any) => {
2 return Object.fromEntries(
3 Object.entries(props).map(([key, value]) => [
4 key,
5 TemplateRegistry.render(value, data)
6 ])
7 );
8};
Language: tsx
Name: RestDataProvider getRenderedProps function
Description:
Render configured template strings as values for the props
You will need to import the TemplateRegistry from
`mystique/registry/TemplateRegistry`
Now we can use this function in our Component implementation and build the endpoint:
1 const [endPointWithProps] = useState(
2 () => `${endpoint}?${new URLSearchParams(getRenderedProps(props, data))}`
3 );
Language: tsx
Name: RestDataProvider - build endpoint
Description:
Build the REST API Endpoint with URL parameters
The
`useState`
`react`
As you can see from the code above, we will keep state of the provided parameters, so as not to re-render  the component unnecessarily if the props values are changing all the time. This is relevant to our example use case, where we will be providing a “Last 24 Hours” date range to the request, and this would be changing every millisecond, triggering a re-render unnecessarily.
The Component SDK provides a
`useRest`
1const response = useRest(endPointWithProps);
Language: tsx
Name: RestDataProvider - useRest
Description:
Call the REST API
Import the
`useRest`
`mystique/hooks/useRest`
The
`response`
`status`
`result`
`status`
In the event that the API call is still executing, it is helpful to show the
`Loading`
In the event that the API returns an error, we can log the rendered request to the console to help developers debug the problem, and depending on the use case present something else to the UI.
When the API returns successfully, the status will be
`ok`
`result`
`Children`
`data`
Let’s implement a simple switch statement to render feedback to the user:
1switch (response.status) {
2 case "ok":
3 return <Children dataOverride={decorateQueryResult(response.result)} />;
4
5 case "loading":
6 return <Loading />;
7
8 default:
9 // includes "error"
10 console.error(
11 `Could not retrieve data from REST API: ${JSON.stringify(endPointWithProps)}\n Response: ${JSON.stringify(response, null, 2)}`
12 );
13 return <div>Error occurred loading data from REST API</div>;
14}
Language: tsx
Name: RestDataProvider - switch return
Description:
Handle each API Response
Import
`Children`
`mystique/components/Children`
`Loading`
`mystique/components/Loading`
`decorateQueryResult`
`'../../../lib/mystique-export'`
In the
`index.tsx`
1ComponentRegistry.register(["devrel.provider.rest"], RestProvider, {category: "content"});
Language: tsx
Name: RestDataProvider - register component
Description:
Register the RestDataProvider Component in the ComponentRegistry
You'll need to import
`RestProvider`
`'./components/providers/RestProvider'`
Start your local server by running
`yarn start`
1{
2 "type": "url",
3 "src": "http://localhost:3001"
4}
Language: json
Name: RestDataProvider - add local plugin to manifest
Description:
Add your localhost plugin to the app manifest
Since we’ll try this using the Event API, let’s use the Insights section of the Fluent OMS webapp. If you haven’t already, you will need to override the default Insights fragment for Fluent OMS.
Let’s add a new Page to the Insights fragment, and try out the component:
1{
2 "type": "page",
3 "path": "/devrel-events-dashboard",
4 "nav": {
5 "label": "DevRel Events Dashboard",
6 "icon": "MdEvent"
7 },
8 "component": "fc.page",
9 "props": {
10 "title": "DevRel Events Dashboard - Updated on: {{dateStringFormatter (dateAdd)}}"
11 },
12 "descendants": []
13}
Language: json
Name: RestDataProvider - add new Insights page manifest
Description:
Add a new "Events Dashboard" page in the Insights manifest fragment
To test the REST Provider Component, we’ll use the standard
`fc.dashboard.threshold`
1{
2 "component": "devrel.provider.rest",
3 "props": {
4 "endpoint": "api/v4.1/event",
5 "props": {
6 "context.entityType": "ORDER",
7 "eventType": "ORCHESTRATION",
8 "eventStatus": "FAILED",
9 "from": "{{dateStringFormatter (dateAdd hours=-24) 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' true}}"
10 }
11 },
12 "descendants": [
13 {
14 "component": "fc.dashboard.threshold",
15 "props": {
16 "label": "Failed",
17 "value": "{{ results.length }}",
18 "thresholdLow": 1,
19 "thresholdHigh": 10,
20 "link": "#/events?context.entityType=ORDER&eventType=ORCHESTRATION&eventStatus=FAILED&from={{dateStringFormatter (dateAdd hours=-24) 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' true}}"
21 }
22 }
23 ]
24}
Language: json
Name: RestDataProvider - configure component in manifest
Description:
Use the RestDataProvider to display Event data in a standard Dashboard Threshold Tile Component
As you can see above, we’re configuring the
`devrel.provider.rest`
A standard Dashboard Tile Component displaying failed Order Orchestration Events
The applications for this include composing various UI capabilities for the data exposed by the Fluent Platform APIs. You can build out Event Dashboards, Webhook Dashboards (coming soon), enhanced Activity views for entities, display execution paths and status transition information, or even display a List of Workflows and a detailed Rule Library.
Combining this with standard GraphQL page queries, you can build richer domain or entity based dashboards, activity views, etc.
You can make further enhancements to this component, such as rendering the Loading and Error responses in a card.
Additionally, you can swap out the
`useRest`
`getRest`
Finally, while this component won't allow support for all child component features, such as list paging and filtering, you can combine this with a custom List wrapper component, to enable these capabilities.
The REST API Data Provider demonstrates how quick and easy it is to extend the UX Framework, and maximise reuse of standard library components for REST based data.
As you can see, this little custom component makes it super simple to enable presentation of REST API based data with the standard Component Library.
Copyright © 2024 Fluent Retail Pty Ltd (trading as Fluent Commerce). All rights reserved. No materials on this docs.fluentcommerce.com site may be used in any way and/or for any purpose without prior written authorisation from Fluent Commerce. Current customers and partners shall use these materials strictly in accordance with the terms and conditions of their written agreements with Fluent Commerce or its affiliates.