-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into filrak-patch-2
- Loading branch information
Showing
80 changed files
with
10,018 additions
and
10,547 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,262 @@ | ||
# Orchestration | ||
|
||
Orchestration is a powerful way to optimize server requests. This guide will take you through the key aspects of using the orchestration feature, focusing on integrating extensions and how to get the best out of your frontend code. | ||
|
||
## Orchestration for Frontend Optimization | ||
|
||
One of the primary advantages of orchestration is the ability to combine multiple requests into a single endpoint. This can drastically reduce the overhead on the frontend, especially in applications where numerous server requests might be initiated simultaneously. | ||
|
||
### Benefits: | ||
|
||
- Reduced Network Calls: Fewer calls to the server means reduced latency and faster performance. | ||
- Simplified Frontend Logic: Grouping related server requests can help simplify frontend code and logic. | ||
- Consistent Data Retrieval: With orchestration, you can ensure data from multiple integrations is fetched consistently and returned in a unified format. | ||
|
||
### Implementation: | ||
|
||
## The `getApiClient` Method | ||
|
||
If you want to retrieve a loaded integration within the context of another, you can use the `getApiClient` method. This method serves as a bridge between various integrations, ensuring seamless data flow and interaction. | ||
|
||
Usage: | ||
|
||
```javascript | ||
const sapcc = context.getApiClient("sapcc"); | ||
``` | ||
|
||
The `getApiClient` method takes a single argument, which is the key of the api client you wish to retrieve. This is the key you would define in the `middleware.config.js` file for the integration you wish to retrieve. The key is essentially an identifier for the integration. | ||
|
||
Here's a basic example of what this might look like: | ||
|
||
```javascript | ||
export const integrations = { | ||
sapcc: { | ||
location: "@vsf-enterprise/sapcc-api/server", | ||
configuration: { | ||
// ... | ||
}, | ||
extensions: (extensions) => [ | ||
...extensions, | ||
{ | ||
name: "sapcc-contentful-extension", | ||
extendApiMethods: { | ||
getPDP: async (context, params: { id: string }) => { | ||
const sapcc = context.getApiClient("sapcc"); | ||
const contentful = context.getApiClient("contentful"); | ||
|
||
const [product, content] = Promise.all( | ||
sapcc.api.getProduct({ id: params.id }), | ||
contentful.api.getEntries({ | ||
content_type: "product", | ||
"fields.sku": params.id, | ||
}) | ||
); | ||
|
||
return { | ||
product, | ||
content, | ||
}; | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
contentful: { | ||
location: "@vsf-enterprise/contentful-api/server", | ||
configuration: { | ||
// ... | ||
}, | ||
}, | ||
}; | ||
``` | ||
|
||
1. Extend the integration with new endpoint: Create a new endpoint that will act as the main entry point for the grouped requests. | ||
|
||
2. Group Server Requests: Within this endpoint, utilize the `getApiClient` method to retrieve and interact with the required integrations. | ||
|
||
3. Aggregate Data: Once data from all required integrations is retrieved, aggregate and format it as needed. | ||
|
||
4. Return Unified Response: Send a consolidated response back to the frontend.: | ||
|
||
### Using orchestration methods in the frontend | ||
|
||
To call the orchestration endpoint, you need to send a POST request to the endpoint URL. In our example it would be `POST http://localhost:8080/sapcc/getPDP`. The request body should contain the parameters required by the endpoint. | ||
|
||
When it comes to extending the Vue Storefront integrations like `sapcc` or `contentful`, you can use the SDK extension mechanism as described in the [Extending a Module](/sdk/advanced/extending-module) guide. | ||
|
||
To extend the `sapcc-sdk` with our new `getPDP` method, we would need to create a new extension: | ||
|
||
```javascript [sapccExtension.ts] | ||
import { client } from "@vsf-enterprise/sapcc-sdk"; | ||
|
||
export const sapccExtension = { | ||
extend: { | ||
getPDP: async (params: { id: string }) => { | ||
const { product, content } = await client.post("/getPDP", params); | ||
|
||
return { | ||
product, | ||
content, | ||
}; | ||
}, | ||
}, | ||
}; | ||
``` | ||
|
||
Of course, you can use another fetching mechanism, like `axios` or `fetch`, but the `client` is already configured with the correct URL and headers. | ||
|
||
## Examples | ||
|
||
Let's take a look at some examples of how you can use orchestration. | ||
|
||
### Example 1: Fetching Custom Product Properties from Legacy Systems | ||
|
||
This use case involves calling the commerce backend to fetch specific product data. Additionally, a separate call is made to a legacy custom system of the customer, to retrieve a custom product property (e.g., stock of the product). This data is used, for example, to display stock information on the product page. | ||
|
||
Example implementation might look like this: | ||
|
||
```typescript [middleware.config.ts] | ||
export const integrations = { | ||
sapcc: { | ||
// ... | ||
extensions: (extensions) => [ | ||
...extensions, | ||
{ | ||
name: "orchestration-extension", | ||
extendApiMethods: { | ||
enrichedSearch: async (context, params: { productId: string }) => { | ||
const sapcc = context.getApiClient("sapcc"); | ||
const legacyCustomSystem = context.getApiClient("legacyCustomSystem"); | ||
|
||
const prouctStock = await legacyCustomSystem.api.getProductStock({ | ||
productId: params.productId, | ||
}); | ||
|
||
const product = await sapcc.api.getProduct({ | ||
{ id: params.productId }, | ||
}); | ||
|
||
return { | ||
...product, | ||
stock: productStock, | ||
}; | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
legacyCustomSystem: { | ||
// ... | ||
}, | ||
}; | ||
``` | ||
|
||
### Example 2: Product Slider Orchestration with Commerce Backend Data | ||
|
||
In this scenario, the CMS returns a product slider component that lacks sufficient product data (or contains only product IDs). The orchestration layer enhances the product slider by adding product data from the commerce backend, ensuring a comprehensive and informative display. | ||
|
||
```typescript [middleware.config.ts] | ||
export const integrations = { | ||
sapcc: { | ||
// ... | ||
extensions: (extensions) => [ | ||
...extensions, | ||
{ | ||
name: "orchestration-extension", | ||
extendApiMethods: { | ||
getProductSlider: async ( | ||
context: IntegrationContext, | ||
props: { entryId: string } | ||
) => { | ||
const contentful: ApiClient<ContentfulEndpoints> = | ||
context.getApiClient("contentful"); | ||
const sapcc: ApiClient<SapccEndpoints> = | ||
context.getApiClient("sapcc"); | ||
|
||
const content = await contentful.api.getEntry(props.entryId); | ||
const productItems = await Promise.all( | ||
content.fields.items.map(async (item) => { | ||
const product = await sapcc.api.getProduct({ | ||
id: item.fields.productId, | ||
}); | ||
|
||
return { | ||
...item, | ||
fields: { | ||
...item.fields, | ||
...product, | ||
}, | ||
}; | ||
}) | ||
); | ||
|
||
return { | ||
...content, | ||
fields: { | ||
...content.fields, | ||
items: productItems, | ||
}, | ||
}; | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
contentful: { | ||
// ... | ||
}, | ||
}; | ||
``` | ||
|
||
## TypeScript Support | ||
|
||
`getApiClient` helper returns the `ApiClient` interface, which is a generic type. It takes three type arguments: | ||
|
||
- `Api` - the type of the API object returned by the integration, | ||
- `Config` - the type of the configuration object passed to the integration, | ||
- `Client` - the type of the HTTP client object returned by the integration. | ||
|
||
Usually, an integration exports those types. For example, the `sapcc` integration exports the following types: | ||
|
||
```typescript | ||
import { | ||
Endpoints, | ||
MiddlewareConfig, | ||
AxiosInstance, | ||
} from "@vsf-enterprise/sapcc-api"; | ||
``` | ||
|
||
### Endpoints and the `context` object | ||
|
||
Each method of the integration api client contains the `context` object as the first argument. However, the `context` is not something that is passed by the developer to the method during the call. This is because the `context` is passed automatically by the `@vue-storefront/middleware` package logic. Because of this, the `context` object should be excluded from the `Api` interface passed to the `ApiClient` type. | ||
|
||
To achieve that, the `@vue-storefront/middleware` export the interface `ContextualizedApi`, which basically removes the context from the API. | ||
|
||
### Example | ||
|
||
Let's take a look at the `sapcc` integration. The `sapcc` integration exports the following types: | ||
|
||
```typescript | ||
import { | ||
Endpoints, | ||
MiddlewareConfig, | ||
AxiosInstance, | ||
} from "@vsf-enterprise/sapcc-api"; | ||
import { ContextualizedApi } from "@vue-storefront/middleware"; | ||
|
||
// ... | ||
|
||
const sapcc = context.getApiClient< | ||
ContextualizedApi<Endpoints>, | ||
MiddlewareConfig, | ||
AxiosInstance | ||
>("sapcc"); | ||
|
||
// sapcc.api now is aware of the SAPCC methods that are not expecting the `context` object | ||
// sapcc.config is now aware of the SAPCC configuration object | ||
// sapcc.client is now aware of the SAPCC HTTP client object | ||
``` | ||
|
||
:::tip Type of endpoints | ||
Sometimes, the `Endpoints` type is not exported by the integration. If that's the case, you can import the `XyzIntegrationContext` type from the integration package. For example, the `sapcc` integration exports the `SapccIntegrationContext` type, which contains the endpoints type as `SapccIntegrationContext['api']`. It contains also the type of the configuration object as `SapccIntegrationContext['config']` and the type of the HTTP client object as `SapccIntegrationContext['client']`. This should be applied to all integrations. | ||
::: |
Oops, something went wrong.