Skip to content
This repository has been archived by the owner on Oct 31, 2024. It is now read-only.

feat(composables): provide useProductAssociations for getting associations #1392

Merged
merged 9 commits into from
Apr 20, 2021
15 changes: 15 additions & 0 deletions api/composables.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Cart } from '@shopware-pwa/commons/interfaces/models/checkout/cart/Cart
import { CmsPage } from '@shopware-pwa/commons/interfaces/models/content/cms/CmsPage';
import { ComputedRef } from '@vue/composition-api';
import { Country } from '@shopware-pwa/commons/interfaces/models/system/country/Country';
import { CrossSelling } from '@shopware-pwa/commons/interfaces/models/content/product/Product';
import { Currency } from '@shopware-pwa/commons/interfaces/models/system/currency/Currency';
import { Customer } from '@shopware-pwa/commons/interfaces/models/checkout/customer/Customer';
import { CustomerAddress } from '@shopware-pwa/commons/interfaces/models/checkout/customer/CustomerAddress';
Expand Down Expand Up @@ -348,6 +349,17 @@ export interface IUseNavigation {
navigationElements: ComputedRef<StoreNavigationElement[] | null>;
}

// @beta
export interface IUseProductAssociations {
isLoading: ComputedRef<boolean>;
loadAssociations: (params: {
params: unknown;
method: "post" | "get";
}) => Promise<void>;
// (undocumented)
productAssociations: ComputedRef<CrossSelling[]>;
}

// @beta
export interface IUseProductConfigurator {
// (undocumented)
Expand Down Expand Up @@ -645,6 +657,9 @@ export interface UseProduct<PRODUCT, SEARCH> {
// @beta (undocumented)
export const useProduct: (rootContext: ApplicationVueContext, loadedProduct?: any) => UseProduct<Product, Search>;

// @beta
export function useProductAssociations(rootContext: ApplicationVueContext, product: Product, association: "cross-selling" | "reviews"): IUseProductAssociations;

// @beta
export const useProductConfigurator: (rootContext: ApplicationVueContext, product: Product) => IUseProductConfigurator;

Expand Down
4 changes: 0 additions & 4 deletions api/shopware-6-client.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ export function clearCart(contextInstance?: ShopwareApiInstance): Promise<Contex
// @beta (undocumented)
export interface ClientSettings {
accessToken?: string;
auth?: {
username: string;
password: string;
};
contextToken?: string;
defaultPaginationLimit?: number;
endpoint?: string;
Expand Down
6 changes: 5 additions & 1 deletion docs/.vuepress/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ module.exports = {
path: "/landing/getting-started/",
title: "Getting Started",
collapsable: false,
children: ["/landing/getting-started/prepare-shopware", "/landing/project/contribution", "/landing/getting-started/local-environment"],
children: [
"/landing/getting-started/prepare-shopware",
"/landing/project/contribution",
"/landing/getting-started/local-environment",
],
},
{
path: "/landing/fundamentals/",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@shopware-pwa/composables](./composables.md) &gt; [IUseProductAssociations](./composables.iuseproductassociations.md) &gt; [isLoading](./composables.iuseproductassociations.isloading.md)

## IUseProductAssociations.isLoading property

> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.
>

If it's loading - indicator

<b>Signature:</b>

```typescript
isLoading: ComputedRef<boolean>;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@shopware-pwa/composables](./composables.md) &gt; [IUseProductAssociations](./composables.iuseproductassociations.md) &gt; [loadAssociations](./composables.iuseproductassociations.loadassociations.md)

## IUseProductAssociations.loadAssociations property

> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.
>

Start loading resources

<b>Signature:</b>

```typescript
loadAssociations: (params: {
params: unknown;
method: "post" | "get";
}) => Promise<void>;
```
25 changes: 25 additions & 0 deletions docs/landing/resources/api/composables.iuseproductassociations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@shopware-pwa/composables](./composables.md) &gt; [IUseProductAssociations](./composables.iuseproductassociations.md)

## IUseProductAssociations interface

> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.
>

interface for [IUseProductAssociations](./composables.iuseproductassociations.md) composable

<b>Signature:</b>

```typescript
export interface IUseProductAssociations
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [isLoading](./composables.iuseproductassociations.isloading.md) | ComputedRef&lt;boolean&gt; | <b><i>(BETA)</i></b> If it's loading - indicator |
| [loadAssociations](./composables.iuseproductassociations.loadassociations.md) | (params: { params: unknown; method: "post" \| "get"; }) =&gt; Promise&lt;void&gt; | <b><i>(BETA)</i></b> Start loading resources |
| [productAssociations](./composables.iuseproductassociations.productassociations.md) | ComputedRef&lt;CrossSelling\[\]&gt; | <b><i>(BETA)</i></b> |

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@shopware-pwa/composables](./composables.md) &gt; [IUseProductAssociations](./composables.iuseproductassociations.md) &gt; [productAssociations](./composables.iuseproductassociations.productassociations.md)

## IUseProductAssociations.productAssociations property

> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.
>

<b>Signature:</b>

```typescript
productAssociations: ComputedRef<CrossSelling[]>;
```
2 changes: 2 additions & 0 deletions docs/landing/resources/api/composables.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
| [getDefaultApiParams()](./composables.getdefaultapiparams.md) | <b><i>(BETA)</i></b> Returns default system API params |
| [useBreadcrumbs(rootContext, params)](./composables.usebreadcrumbs.md) | <b><i>(BETA)</i></b> Composable for displaying and setting breadcrumbs for page. |
| [useCms(rootContext)](./composables.usecms.md) | <b><i>(BETA)</i></b> |
| [useProductAssociations(rootContext, product, association)](./composables.useproductassociations.md) | <b><i>(BETA)</i></b> Get product association entity. Options - [IUseProductAssociations](./composables.iuseproductassociations.md) |

## Interfaces

Expand All @@ -27,6 +28,7 @@
| [IUseIntercept](./composables.iuseintercept.md) | <b><i>(BETA)</i></b> interface for [useIntercept](./composables.useintercept.md) composable |
| [IUseListing](./composables.iuselisting.md) | <b><i>(BETA)</i></b> Listing interface, can be used to display category products, search products or any other Shopware search interface (ex. orders with pagination) |
| [IUseNavigation](./composables.iusenavigation.md) | <b><i>(BETA)</i></b> interface for [useNavigation](./composables.usenavigation.md) composable<!-- -->Provides state for navigation trees depending on navigation type. |
| [IUseProductAssociations](./composables.iuseproductassociations.md) | <b><i>(BETA)</i></b> interface for [IUseProductAssociations](./composables.iuseproductassociations.md) composable |
| [IUseProductConfigurator](./composables.iuseproductconfigurator.md) | <b><i>(BETA)</i></b> interface for [useProductConfigurator](./composables.useproductconfigurator.md) composable |
| [IUseProductQuickSearch](./composables.iuseproductquicksearch.md) | <b><i>(BETA)</i></b> |
| [IUseSessionContext](./composables.iusesessioncontext.md) | <b><i>(BETA)</i></b> interface for [useSessionContext](./composables.usesessioncontext.md) composable |
Expand Down
41 changes: 41 additions & 0 deletions docs/landing/resources/api/composables.useproductassociations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@shopware-pwa/composables](./composables.md) &gt; [useProductAssociations](./composables.useproductassociations.md)

## useProductAssociations() function

> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.
>

Get product association entity. Options - [IUseProductAssociations](./composables.iuseproductassociations.md)

<b>Signature:</b>

```typescript
export declare function useProductAssociations(rootContext: ApplicationVueContext, product: Product, association: "cross-selling" | "reviews"): IUseProductAssociations;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| rootContext | [ApplicationVueContext](./composables.applicationvuecontext.md) | |
| product | Product | |
| association | "cross-selling" \| "reviews" | |

<b>Returns:</b>

[IUseProductAssociations](./composables.iuseproductassociations.md)

## Example

Example of possibilities:

```ts
const { loading, loadAssociations, productAssociations } = useProductAssociation(root, product, "cross-selling")
if (!productAssociations.value) {
await loadAssociations()
}

```

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export interface ClientSettings
| Property | Type | Description |
| --- | --- | --- |
| [accessToken?](./shopware-6-client.clientsettings.accesstoken.md) | string | <b><i>(BETA)</i></b> <i>(Optional)</i> id specific for each sales channel |
| [auth?](./shopware-6-client.clientsettings.auth.md) | { username: string; password: string; } | <b><i>(BETA)</i></b> <i>(Optional)</i> credentials for HTTP basic auth |
| [contextToken?](./shopware-6-client.clientsettings.contexttoken.md) | string | <b><i>(BETA)</i></b> <i>(Optional)</i> session id (dynamic) |
| [defaultPaginationLimit?](./shopware-6-client.clientsettings.defaultpaginationlimit.md) | number | <b><i>(BETA)</i></b> <i>(Optional)</i> default amount of products shown on listings |
| [endpoint?](./shopware-6-client.clientsettings.endpoint.md) | string | <b><i>(BETA)</i></b> <i>(Optional)</i> shopware URL |
Expand Down
169 changes: 169 additions & 0 deletions packages/composables/__tests__/useProductAssociation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import Vue from "vue";
import VueCompositionApi from "@vue/composition-api";
Vue.use(VueCompositionApi);

import * as Composables from "@shopware-pwa/composables";
jest.mock("@shopware-pwa/composables");
const mockedComposables = Composables as jest.Mocked<typeof Composables>;
import { useProductAssociations } from "../src/logic/useProductAssociations";
import * as shopwareClient from "@shopware-pwa/shopware-6-client";

jest.mock("@shopware-pwa/shopware-6-client");
const mockedAxios = shopwareClient as jest.Mocked<typeof shopwareClient>;

describe("Composables - useProductAssociations", () => {
const consoleErrorSpy = jest.spyOn(console, "error");
const mockedInvokePost = jest.fn();
const mockedInvokeGet = jest.fn();
const rootContextMock: any = {
$shopwareApiInstance: {
defaults: {
headers: {},
},
invokePost: mockedInvokePost,
invokeGet: mockedInvokeGet,
},
};

beforeEach(() => {
jest.resetAllMocks();
consoleErrorSpy.mockImplementationOnce(() => {});
mockedAxios.getProductDetailsEndpoint.mockReturnValue(
"/product/v4/product-id"
);
mockedComposables.getApplicationContext.mockImplementation(() => {
return {
apiInstance: rootContextMock.$shopwareApiInstance,
contextName: "useProductAssociations",
} as any;
});
});
describe("on init", () => {
it("should have empty (array) associations computed property and isLoading to false", () => {
const { productAssociations, isLoading } = useProductAssociations(
rootContextMock,
{ id: "product-id" } as any,
"cross-selling"
);
expect(isLoading.value).toBeFalsy();
expect(productAssociations.value).toStrictEqual([]);
});
});
describe("methods", () => {
describe("loadAssociations", () => {
it("should catch the error on apiClient rejection", async () => {
mockedAxios.invokePost.mockRejectedValueOnce(
new Error("An error occured")
);
const { loadAssociations } = useProductAssociations(
rootContextMock,
{ id: "product-id" } as any,
"cross-selling"
);
await loadAssociations(undefined as any);
expect(consoleErrorSpy).toBeCalledWith(
`[useProductAssociations][loadAssociations][error]:`,
expect.any(Error)
);
});
it("should invoke GET request (default is POST) for given association within a product endpoint", async () => {
mockedAxios.invokePost.mockResolvedValueOnce({
data: {
associatedProducts: [],
},
});
const {
loadAssociations,
productAssociations,
} = useProductAssociations(
rootContextMock,
{ id: "product-id" } as any,
"cross-selling"
);
await loadAssociations(undefined as any);
expect(mockedAxios.invokePost).toBeCalledWith(
{
address: "/product/v4/product-id/cross-selling",
payload: undefined,
},
{
defaults: { headers: {} },
invokeGet: expect.any(Function),
invokePost: expect.any(Function),
}
);

expect(productAssociations.value).toStrictEqual({
associatedProducts: [],
});
});
it("should not set incoming associations if response does not match for POST", async () => {
mockedAxios.invokePost.mockResolvedValueOnce(undefined);
const {
loadAssociations,
productAssociations,
} = useProductAssociations(
rootContextMock,
{ id: "product-id" } as any,
"cross-selling"
);
await loadAssociations(undefined as any);
expect(productAssociations.value).toStrictEqual([]);
});
it("should set incoming associations if response matches for GET", async () => {
mockedAxios.invokeGet.mockResolvedValueOnce({ data: 12345 });
const {
loadAssociations,
productAssociations,
} = useProductAssociations(
rootContextMock,
{ id: "product-id" } as any,
"cross-selling"
);
await loadAssociations({ method: "get" } as any);
expect(productAssociations.value).toStrictEqual(12345);
});
it("should invoke GET request for given association within a product endpoint", async () => {
const { loadAssociations } = useProductAssociations(
rootContextMock,
{ id: "product-id" } as any,
"cross-selling"
);
await loadAssociations({ method: "get" } as any);
expect(mockedAxios.invokeGet).toBeCalledWith(
{
address: "/product/v4/product-id/cross-selling",
payload: undefined,
},
{
defaults: { headers: {} },
invokeGet: expect.any(Function),
invokePost: expect.any(Function),
}
);
});
it("should invoke GET request for given association within a product endpoint - including params", async () => {
const { loadAssociations } = useProductAssociations(
rootContextMock,
{ id: "product-id" } as any,
"cross-selling"
);
await loadAssociations({
method: "get",
params: "?someParam=true",
} as any);
expect(mockedAxios.invokeGet).toBeCalledWith(
{
address: "/product/v4/product-id/cross-selling?someParam=true",
payload: undefined,
},
{
defaults: { headers: {} },
invokeGet: expect.any(Function),
invokePost: expect.any(Function),
}
);
});
});
});
});
Loading