Skip to content

Commit

Permalink
feat: createShopware to setup composables, devtools integration and i…
Browse files Browse the repository at this point in the history
…mproved interceptors mechanism (#1708)
  • Loading branch information
patzick authored Oct 19, 2021
1 parent a65f075 commit 1c799d2
Show file tree
Hide file tree
Showing 46 changed files with 1,102 additions and 261 deletions.
95 changes: 84 additions & 11 deletions api/composables.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { AddressType } from '@shopware-pwa/commons/interfaces/models/checkout/customer/CustomerAddress';
import { ApiDefaults } from '@shopware-pwa/commons';
import { App } from 'vue-demi';
import { BillingAddress } from '@shopware-pwa/commons/interfaces/models/checkout/customer/BillingAddress';
import { Breadcrumb } from '@shopware-pwa/commons/interfaces/models/content/cms/CmsPage';
import { Cart } from '@shopware-pwa/commons/interfaces/models/checkout/cart/Cart';
Expand All @@ -22,6 +23,7 @@ import { CustomerResetPasswordParam } from '@shopware-pwa/shopware-6-client';
import { CustomerUpdateEmailParam } from '@shopware-pwa/shopware-6-client';
import { CustomerUpdatePasswordParam } from '@shopware-pwa/shopware-6-client';
import { CustomerUpdateProfileParam } from '@shopware-pwa/shopware-6-client';
import { EffectScope } from 'vue-demi';
import { EntityError } from '@shopware-pwa/commons/interfaces/models/common/EntityError';
import { Includes } from '@shopware-pwa/commons/interfaces/search/SearchCriteria';
import { LineItem } from '@shopware-pwa/commons/interfaces/models/checkout/cart/line-item/LineItem';
Expand Down Expand Up @@ -53,13 +55,70 @@ export function createListingComposable<ELEMENTS_TYPE>({ searchMethod, searchDef
listingKey: string;
}): IUseListing<ELEMENTS_TYPE>;

// @beta
export function createShopware(app: App, options: {
initialStore: any;
shopwareDefaults: ApiDefaults;
apiInstance: ShopwareApiInstance;
}): {
_a: App;
_e: EffectScope;
apiInstance: ShopwareApiInstance;
state: {
interceptors: {};
sharedStore: any;
shopwareDefaults: {
[x: string]: {
p?: number | undefined;
limit?: number | undefined;
sort?: string | undefined;
order?: string | undefined;
term?: string | undefined;
ids?: string[] | undefined;
associations?: {
[x: string]: {
associations?: any | undefined;
sort?: string | {
field: string;
order: string;
naturalSorting: boolean;
}[] | undefined;
};
} | undefined;
grouping?: {
field: string;
} | undefined;
properties?: string | never[] | undefined;
manufacturer?: string | never[] | undefined;
includes?: {
[x: string]: string[];
} | undefined;
query?: string | undefined;
};
};
} | undefined;
};

// @alpha
export function extendScopeContext(scope: any, app: any): void;

// @beta
export function getApplicationContext(params?: {
contextName?: string;
}): {
apiInstance: any;
router: any;
route: any;
routing: SwRouting;
i18n: any;
cookies: any;
shopwareDefaults: any;
interceptors: any;
sharedStore: any;
devtools: any;
isServer: boolean;
contextName: string;
} | {
apiInstance: ShopwareApiInstance;
router: any;
route: any;
Expand All @@ -73,14 +132,15 @@ export function getApplicationContext(params?: {
};
isServer: boolean;
contextName: string;
devtools?: undefined;
};

// @beta
export function getDefaultApiParams(): {
[composableName: string]: ShopwareSearchParams;
};

// @beta
// @public
export interface IInterceptorCallbackFunction {
// (undocumented)
(payload: any): void;
Expand Down Expand Up @@ -286,13 +346,6 @@ export interface IUseCustomerPassword {
updatePassword: (updatePasswordData: CustomerUpdatePasswordParam) => Promise<boolean>;
}

// @beta
export interface IUseIntercept {
broadcast: (broadcastKey: string, value?: any) => void;
disconnect: (broadcastKey: string, method: IInterceptorCallbackFunction) => void;
intercept: (broadcastKey: string, method: IInterceptorCallbackFunction) => void;
}

// @public
export interface IUseListing<ELEMENTS_TYPE> {
// (undocumented)
Expand Down Expand Up @@ -573,9 +626,20 @@ export interface ShopwareDomain {
url: string;
}

// @beta (undocumented)
export const ShopwareVuePlugin: (_Vue: any, pluginOptions: {
enableDevtools: boolean;
}) => void;

// @beta (undocumented)
export type SwInterceptor = {
name: string;
handler: IInterceptorCallbackFunction;
};

// @beta (undocumented)
export type SwInterceptors = {
[broadcastKey: string]: Array<IInterceptorCallbackFunction>;
[broadcastKey: string]: Array<SwInterceptor>;
};

// @beta
Expand Down Expand Up @@ -650,8 +714,17 @@ export function useDefaults(params: {
getDefaults: () => ShopwareSearchParams;
};

// @beta
export function useIntercept(): IUseIntercept;
// @public
export function useIntercept(): {
broadcast: (broadcastKey: string, value?: any) => void;
intercept: (broadcastKey: string, handler: IInterceptorCallbackFunction) => void;
disconnect: (broadcastKey: string, interceptor: string | IInterceptorCallbackFunction) => void;
on: (params: {
broadcastKey: string;
name: string;
handler: IInterceptorCallbackFunction;
}) => void;
};

// @beta (undocumented)
export function useListing(params?: {
Expand Down
6 changes: 5 additions & 1 deletion docs/.vuepress/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ module.exports = {
path: "/landing/fundamentals/",
title: "Fundamentals",
collapsable: false,
children: ["/landing/fundamentals/cli", "/landing/fundamentals/security"],
children: [
"/landing/fundamentals/cli",
"/landing/fundamentals/devtools",
"/landing/fundamentals/security",
],
},
{
path: "/landing/cookbook/",
Expand Down
Binary file added docs/landing/assets/devtools-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/landing/assets/devtools-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/landing/assets/devtools-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/landing/assets/devtools-events-overall.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/landing/assets/devtools-grouped-events.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/landing/assets/devtools-timeline-grouped.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/landing/assets/devtools-timeline-log.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/landing/assets/devtools-timeline-show.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 7 additions & 3 deletions docs/landing/concepts/api-client-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,13 @@ export default ({ app }) => {
});
const logger = getLogger(); // get the logstash client instance
const { intercept } = useIntercept();
intercept(INTERCEPTOR_KEYS.ERROR, (payload, rootContext) => {
logger.error(payload); // send the error to the logstash server
const { on } = useIntercept();
on({
broadcastKey: INTERCEPTOR_KEYS.ERROR,
name: "logstash-logger",
handler: (payload, rootContext) => {
logger.error(payload); // send the error to the logstash server
},
});
};
```
Expand Down
9 changes: 6 additions & 3 deletions docs/landing/concepts/interceptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,16 @@ import {
import { getTranslatedProperty } from "@shopware-pwa/helpers";

export default async ({ app }) => {
const { intercept } = useIntercept();
const { on } = useIntercept();
const { pushSuccess } = useNotifications(app);
intercept(INTERCEPTOR_KEYS.ADD_TO_CART, ({ product }) => {
on({
broadcastKey: INTERCEPTOR_KEYS.ADD_TO_CART,
name: "show-success-notification"
handler: ({ product }) => {
pushSuccess(
`${getTranslatedProperty(product, "name")} has been added to cart.`
);
});
}});
};
```

Expand Down
6 changes: 6 additions & 0 deletions docs/landing/fundamentals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Shopware PWA comes with a command line interace `shopware-pwa` with useful funct

[CLI](./cli)

## Devtools <Badge text="new" type="info"/>

See how do we want to support development process and your understanding of what's going on when the application is running.

[Devtools](./devtools)

## Security

It is critical to understand which authentication mechanisms are utilized to make sure that your application is safe. Read this to see how Shopware PWA handles this regarding the usage of APIs.
Expand Down
143 changes: 143 additions & 0 deletions docs/landing/fundamentals/devtools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Devtools <Badge text="from v1.0.0-RC.3" type="info"/>

We want to support your development process all the way. Tracking what's going on inside the application might be overwhelming. Let us help here.

## Install Vue Devtools

You can find the documentation for Vue Devtool [here](https://devtools.vuejs.org/). To be able to see what's inside the Shopware PWA you need to download the beta version of Vue Devtools (with Vue 3 support) [instalation guide](https://devtools.vuejs.org/guide/installation.html). Quicklinks:

- [Chrome](https://chrome.google.com/webstore/detail/vuejs-devtools/ljjemllljcmogpfapbkkighbhhppjdbg)
- [Firefox](https://devtools.vuejs.org/guide/installation.html#beta)

## Enable devtools

In development mode, devtools are enabled by default. We rely on the two env variables, so the devtools are enabled when:

- `NODE_ENV` is different that `production`
- or `ENABLE_DEVTOOLS` has w `true` value

You can set these values manually in your `.env` file inside the root directory

```env
ENABLE_DEVTOOLS=true
NODE_ENV=dev
```

If you have the Devtools installed, you can just start your project `yarn dev`, and inside your browser Devtools you should see something like this:

![Shopware devtools](./../assets/devtools-1.png)

So what can you do with the devtools?

## Preview the Shared State

Every time you're using `sharedRef` from [useStaredState](/landing/resources/api/composables.usesharedstate.html) composable, then you can share your state within the entire application. Other Shopware composables are also using this. So with this option, you can dive into what's inside this state.

![Devtools - shared state](./../assets/devtools-2.png)

:::warning Important!
The keys of the shared state can change. You have an overview of the Application State, but when you rely directly on a specific key (for example `const cmsSearchPath = sharedRef('useCms-searchPath')`) mind this key is internal and can be changed.
:::

## Preview registered interceptors

You can see registered [interceptors](/landing/concepts/interceptor.html) with the proper description, so you can see which interceptors are invoked during specific events.

![Devtools - interceptors](./../assets/devtools-3.png)

## API client settings and API defaults

You can also see what the current configuration of the [API client](landing/resources/api/shopware-6-client.clientsettings.html) is or what are [API defaults](/landing/cookbook/#overwrite-api-defaults) used by the Shopware PWA composables.

## Inspect the timeline

Vue devtools are providing the Timeline feature, and you can access it here:

![Devtools - timeline show](./../assets/devtools-timeline-show.png)

We added the Shopware PWA layer to see related events.

### Track events

You can track the events, which appears in the timeline. Events often contain additional parameters (as JSON object) with useful data.

![Devtools - events overall](./../assets/devtools-events-overall.png)

### Track group events

Some of the events are grouped. This way, you can see the time between first and last event log and filter related events. For example, a `broadcast` event from `useIntercept` composable is invoking all registered interceptors. You can track the invocations, and which invocation changed the payload.

![Devtools - grouped events](./../assets/devtools-grouped-events.png)

## Add a custom devtools events

If you want to add some events to debug using devtools there's a way for that! We opened the API to add your custom logs inside the timeline.

:::warning Note
Custom events inside the devtools are in the experimental phase. The API of this feature might change a bit, but using them with optional chaining (for example `devtools?.log('something')`) will ensure the application stability.
:::

### Add timeline log, warning or error <Badge text="experimental" type="warning"/>

You can add a custom log to the devtools using one of the three methods:

```ts
function log(label: string, params: any): void;
function warning(label: string, params: any): void;
function error(label: string, params: any): void;
```

Example - we want to track sorting order changes

```ts
setup() {
const {getCurrentSortingOrder, changeCurrentSortingOrder} = useListing({ listingType: 'categoryListing' })
const { devtools } = getApplicationContext()


const currentSortingOrder = computed({
get: () => getCurrentSortingOrder.value,
set: (order) => {
devtools?.log('Changing sorting order', order)
changeCurrentSortingOrder(order)
},
})
}
```

so now, every time you change the sort order on the category page, you can see:

![Devtools - timeline log](./../assets/devtools-timeline-log.png)

### Add timeline event group

You can also group your timeline events to see how much time is needed for specific actions to complete and see the data flow inside.

Example - track the user registration process with the registration result, so we can change the `invokeRegister` method in `SwRegister` component.

```ts
setup() {
const {register} = useUser()
const { devtools } = getApplicationContext()


async function invokeRegister() {
$v.value.$reset()
const isFormCorrect = await $v.value.$validate()
if (!isFormCorrect) {
return
}
const event = devtools?.trackEvent(
"User registration",
registrationFormData.value
)
const isSuccess = await register(registrationFormData.value)
event?.log("Registration completed", { isSuccess })
isSuccess && emit("success")
}
}
```

This way, we see grouped events and the time this event took. Inside `group` tab, we can see all the logs connected to this event with passed parameters.

![Devtools - timeline grouped log](./../assets/devtools-timeline-grouped.png)
Loading

1 comment on commit 1c799d2

@vercel
Copy link

@vercel vercel bot commented on 1c799d2 Oct 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.