Webhook query dry run (#2960)
This commit is contained in:
parent
7b8ececc3d
commit
6dbb8e4ecc
30 changed files with 3262 additions and 3158 deletions
3763
introspection.json
3763
introspection.json
File diff suppressed because it is too large
Load diff
|
@ -51,6 +51,10 @@
|
|||
"context": "tabel column header",
|
||||
"string": "Total"
|
||||
},
|
||||
"+RffqY": {
|
||||
"context": "Dry run dialog object title",
|
||||
"string": "Select object type to perform dry run on provided query"
|
||||
},
|
||||
"+RjQjs": {
|
||||
"context": "return button",
|
||||
"string": "Return / Replace order"
|
||||
|
@ -390,6 +394,10 @@
|
|||
"context": "selected customer channel subtitle",
|
||||
"string": "Customer will be sent the gift card code via this channels email address"
|
||||
},
|
||||
"0u9Ng0": {
|
||||
"context": "Dry run no objects found",
|
||||
"string": "No objects found in the provided query"
|
||||
},
|
||||
"0vsMRq": {
|
||||
"context": "delete custom app",
|
||||
"string": "Deleting this app, you will delete all the data and webhooks regarding this app."
|
||||
|
@ -800,6 +808,10 @@
|
|||
"context": "open full-screen",
|
||||
"string": "Open"
|
||||
},
|
||||
"48dMqY": {
|
||||
"context": "Dry run trigger button",
|
||||
"string": "Run"
|
||||
},
|
||||
"4B32Ba": {
|
||||
"context": "delete page",
|
||||
"string": "Are you sure you want to delete {title}?"
|
||||
|
@ -993,6 +1005,10 @@
|
|||
"context": "tabel column header",
|
||||
"string": "Price"
|
||||
},
|
||||
"5b0Boq": {
|
||||
"context": "Dry run items list default message",
|
||||
"string": "Choose the object"
|
||||
},
|
||||
"5bJ26s": {
|
||||
"string": "Successfully created page type"
|
||||
},
|
||||
|
@ -1591,6 +1607,10 @@
|
|||
"context": "sale status",
|
||||
"string": "Active"
|
||||
},
|
||||
"ApNw0L": {
|
||||
"context": "Dry run objects unavailable",
|
||||
"string": "The following objects are currently not available for dry run:"
|
||||
},
|
||||
"AqHafs": {
|
||||
"context": "provided email input placeholder",
|
||||
"string": "Provided email address"
|
||||
|
@ -3651,6 +3671,10 @@
|
|||
"context": "modal button url upload",
|
||||
"string": "Upload URL"
|
||||
},
|
||||
"Q2us5X": {
|
||||
"context": "Dry run icon label",
|
||||
"string": "Dry run"
|
||||
},
|
||||
"Q47ovw": {
|
||||
"context": "activate app",
|
||||
"string": "Are you sure you want to activate this app? Activating will start gathering events."
|
||||
|
@ -4767,6 +4791,10 @@
|
|||
"context": "header",
|
||||
"string": "Edit Authorization Field"
|
||||
},
|
||||
"Xz/sDf": {
|
||||
"context": "Dry run items list item",
|
||||
"string": "Item:"
|
||||
},
|
||||
"Y1B0PN": {
|
||||
"context": "error message",
|
||||
"string": "There's no payment associated with the order"
|
||||
|
@ -4962,6 +4990,10 @@
|
|||
"context": "subsection header",
|
||||
"string": "Shipping Address"
|
||||
},
|
||||
"ZepEWY": {
|
||||
"context": "Dry run item",
|
||||
"string": "Item"
|
||||
},
|
||||
"Zg0dRo": {
|
||||
"context": "dialog description",
|
||||
"string": "You have changed customer assigned to this order. What would you like to do with the shipping address?"
|
||||
|
@ -5647,6 +5679,10 @@
|
|||
"fhksPD": {
|
||||
"string": "Number of Orders"
|
||||
},
|
||||
"fi9Qa/": {
|
||||
"context": "Dry run dialog header",
|
||||
"string": "Dry run"
|
||||
},
|
||||
"fjPWOA": {
|
||||
"context": "header",
|
||||
"string": "Customer Overview"
|
||||
|
@ -7472,6 +7508,10 @@
|
|||
"ubmFc8": {
|
||||
"string": "Install"
|
||||
},
|
||||
"uccjUM": {
|
||||
"context": "Dry run objects",
|
||||
"string": "Objects"
|
||||
},
|
||||
"ud0w8h": {
|
||||
"context": "number of postal code ranges",
|
||||
"string": "{number} postal code ranges"
|
||||
|
|
804
schema.graphql
804
schema.graphql
File diff suppressed because it is too large
Load diff
42
src/components/DryRun/DryRun.test.tsx
Normal file
42
src/components/DryRun/DryRun.test.tsx
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { MockedProvider, MockedResponse } from "@apollo/client/testing";
|
||||
import { WebhookEventTypeAsyncEnum } from "@dashboard/graphql";
|
||||
import { ThemeProvider } from "@saleor/macaw-ui";
|
||||
import productsMocks from "@test/mocks/products";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import React from "react";
|
||||
|
||||
import DryRun from "./DryRun";
|
||||
|
||||
const mocks: MockedResponse[] = [...productsMocks];
|
||||
|
||||
jest.mock("react-intl", () => ({
|
||||
useIntl: jest.fn(() => ({
|
||||
formatMessage: jest.fn(x => x.defaultMessage),
|
||||
})),
|
||||
defineMessages: jest.fn(x => x),
|
||||
}));
|
||||
|
||||
describe("DryRun", () => {
|
||||
it("Dialog is available on the webhook page", async () => {
|
||||
// Arrange
|
||||
const props = {
|
||||
query: "",
|
||||
showDialog: true,
|
||||
setShowDialog: jest.fn(),
|
||||
asyncEvents: [] as WebhookEventTypeAsyncEnum[],
|
||||
setResult: jest.fn(),
|
||||
};
|
||||
|
||||
// Act
|
||||
render(
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<ThemeProvider>
|
||||
<DryRun {...props} />
|
||||
</ThemeProvider>
|
||||
</MockedProvider>,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(screen.queryByTestId("dry-run")).toBeInTheDocument();
|
||||
});
|
||||
});
|
171
src/components/DryRun/DryRun.tsx
Normal file
171
src/components/DryRun/DryRun.tsx
Normal file
|
@ -0,0 +1,171 @@
|
|||
import Grid from "@dashboard/components/Grid";
|
||||
import { useStyles } from "@dashboard/custom-apps/components/WebhookEvents/styles";
|
||||
import {
|
||||
useTriggerWebhookDryRunMutation,
|
||||
WebhookEventTypeAsyncEnum,
|
||||
} from "@dashboard/graphql";
|
||||
import {
|
||||
capitalize,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
DialogHeader,
|
||||
List,
|
||||
ListBody,
|
||||
ListHeader,
|
||||
ListItem,
|
||||
ListItemCell,
|
||||
} from "@saleor/macaw-ui";
|
||||
import React, { Dispatch, SetStateAction, useState } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import DryRunItemsList from "../DryRunItemsList/DryRunItemsList";
|
||||
import { messages } from "./messages";
|
||||
import { getObjects } from "./utils";
|
||||
|
||||
interface DryRunProps {
|
||||
query: string;
|
||||
showDialog: boolean;
|
||||
setShowDialog: Dispatch<SetStateAction<boolean>>;
|
||||
asyncEvents: WebhookEventTypeAsyncEnum[];
|
||||
setResult: Dispatch<SetStateAction<string>>;
|
||||
}
|
||||
|
||||
export const DryRun: React.FC<DryRunProps> = ({
|
||||
setResult,
|
||||
showDialog,
|
||||
setShowDialog,
|
||||
query,
|
||||
}: DryRunProps) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
const [objectId, setObjectId] = useState<string | null>(null);
|
||||
const [triggerWebhookDryRun] = useTriggerWebhookDryRunMutation();
|
||||
const availableObjects = getObjects(query);
|
||||
const unavailableObjects = getObjects(query, false);
|
||||
|
||||
const [object, setObject] = useState<string | null>(null);
|
||||
|
||||
const dryRun = async () => {
|
||||
const { data } = await triggerWebhookDryRun({
|
||||
variables: { objectId, query },
|
||||
});
|
||||
setResult(JSON.stringify(JSON.parse(data.webhookDryRun.payload), null, 2));
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
setShowDialog(false);
|
||||
setObjectId(null);
|
||||
setObject(null);
|
||||
setShowDialog(false);
|
||||
};
|
||||
|
||||
if (!showDialog) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={showDialog} fullWidth maxWidth="md" data-test-id="dry-run">
|
||||
<DialogHeader onClose={closeDialog}>
|
||||
{intl.formatMessage(messages.header)}
|
||||
</DialogHeader>
|
||||
<DialogContent style={{ overflow: "scroll" }}>
|
||||
<DialogContentText>
|
||||
{intl.formatMessage(messages.selectObject)}
|
||||
</DialogContentText>
|
||||
|
||||
{!!unavailableObjects.length && (
|
||||
<Alert variant="warning" close={false}>
|
||||
<Typography>
|
||||
{intl.formatMessage(messages.unavailableObjects)}
|
||||
|
||||
<strong>{unavailableObjects.join(", ")}</strong>
|
||||
</Typography>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Grid variant="uniform">
|
||||
<div className={classes.objectsWrapper}>
|
||||
<List gridTemplate={["1fr 50px"]}>
|
||||
<ListHeader>
|
||||
<ListItem className={classes.listHeader}>
|
||||
<ListItemCell className={classes.listItemCell}>
|
||||
{intl.formatMessage(messages.objects)}
|
||||
</ListItemCell>
|
||||
<ListItemCell></ListItemCell>
|
||||
</ListItem>
|
||||
</ListHeader>
|
||||
<ListBody className={classes.listBody}>
|
||||
{!availableObjects.length && (
|
||||
<Typography>
|
||||
{intl.formatMessage(messages.noObjects)}
|
||||
</Typography>
|
||||
)}
|
||||
{availableObjects.map((object, idx) => (
|
||||
<ListItem
|
||||
key={idx}
|
||||
className={classes.listItem}
|
||||
onClick={() =>
|
||||
setObject(object.split(" ").join("_").toUpperCase())
|
||||
}
|
||||
>
|
||||
<ListItemCell className={classes.listItemCell}>
|
||||
<strong>
|
||||
{capitalize(object.replaceAll("_", " ").toLowerCase())}
|
||||
</strong>
|
||||
</ListItemCell>
|
||||
<ListItemCell></ListItemCell>
|
||||
</ListItem>
|
||||
))}
|
||||
</ListBody>
|
||||
</List>
|
||||
</div>
|
||||
<div className={classes.eventsWrapper}>
|
||||
{object ? (
|
||||
<DryRunItemsList
|
||||
setObjectId={setObjectId}
|
||||
objectId={objectId}
|
||||
object={object}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<ListHeader>
|
||||
<ListItem className={classes.listHeader}>
|
||||
<ListItemCell className={classes.listItemCell}>
|
||||
{intl.formatMessage(messages.item)}
|
||||
</ListItemCell>
|
||||
</ListItem>
|
||||
</ListHeader>
|
||||
<ListBody className={classes.listBody}>
|
||||
<Typography>
|
||||
{intl.formatMessage(messages.itemsDefaultMessage)}
|
||||
</Typography>
|
||||
</ListBody>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="primary"
|
||||
onClick={dryRun}
|
||||
disabled={!availableObjects.length}
|
||||
>
|
||||
{intl.formatMessage(messages.run)}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
DryRun.displayName = "DryRun";
|
||||
export default DryRun;
|
2
src/components/DryRun/index.ts
Normal file
2
src/components/DryRun/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./DryRun";
|
||||
export * from "./DryRun";
|
45
src/components/DryRun/messages.ts
Normal file
45
src/components/DryRun/messages.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
header: {
|
||||
id: "fi9Qa/",
|
||||
defaultMessage: "Dry run",
|
||||
description: "Dry run dialog header",
|
||||
},
|
||||
selectObject: {
|
||||
id: "+RffqY",
|
||||
defaultMessage: "Select object type to perform dry run on provided query",
|
||||
description: "Dry run dialog object title",
|
||||
},
|
||||
unavailableObjects: {
|
||||
id: "ApNw0L",
|
||||
defaultMessage:
|
||||
"The following objects are currently not available for dry run:",
|
||||
description: "Dry run objects unavailable",
|
||||
},
|
||||
objects: {
|
||||
id: "uccjUM",
|
||||
defaultMessage: "Objects",
|
||||
description: "Dry run objects",
|
||||
},
|
||||
noObjects: {
|
||||
id: "0u9Ng0",
|
||||
defaultMessage: "No objects found in the provided query",
|
||||
description: "Dry run no objects found",
|
||||
},
|
||||
item: {
|
||||
id: "ZepEWY",
|
||||
defaultMessage: "Item",
|
||||
description: "Dry run item",
|
||||
},
|
||||
itemsDefaultMessage: {
|
||||
id: "5b0Boq",
|
||||
defaultMessage: "Choose the object",
|
||||
description: "Dry run items list default message",
|
||||
},
|
||||
run: {
|
||||
id: "48dMqY",
|
||||
defaultMessage: "Run",
|
||||
description: "Dry run trigger button",
|
||||
},
|
||||
});
|
13
src/components/DryRun/mutations.ts
Normal file
13
src/components/DryRun/mutations.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { gql } from "@apollo/client";
|
||||
|
||||
export const triggerWebhookDryRun = gql`
|
||||
mutation TriggerWebhookDryRun($objectId: ID!, $query: String!) {
|
||||
webhookDryRun(objectId: $objectId, query: $query) {
|
||||
payload
|
||||
errors {
|
||||
field
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
59
src/components/DryRun/utils.ts
Normal file
59
src/components/DryRun/utils.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { AsyncWebhookTypes } from "@dashboard/custom-apps/components/WebhookEvents";
|
||||
import { InlineFragmentNode, ObjectFieldNode, parse, visit } from "graphql";
|
||||
import uniq from "lodash/uniq";
|
||||
|
||||
import { ExcludedDocumentMap } from "../DryRunItemsList/utils";
|
||||
|
||||
const getEventsFromQuery = (query: string) => {
|
||||
if (query.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const ast = parse(query);
|
||||
const events: string[] = [];
|
||||
|
||||
visit(ast, {
|
||||
SelectionSet(node, _key, parent) {
|
||||
if ((parent as ObjectFieldNode).name?.value === "event") {
|
||||
const queryEvents = node.selections.map(
|
||||
selection =>
|
||||
(selection as InlineFragmentNode).typeCondition.name.value,
|
||||
);
|
||||
|
||||
queryEvents.map(event => events.push(event));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return events;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const getObjects = (query: string, available = true) => {
|
||||
const queryEvents = getEventsFromQuery(query);
|
||||
|
||||
return uniq(
|
||||
queryEvents.map(event => {
|
||||
const object = event.split(/(?=[A-Z])/).slice(0, -1);
|
||||
if (
|
||||
Object.keys(AsyncWebhookTypes)
|
||||
.filter(object =>
|
||||
available
|
||||
? !Object.keys(ExcludedDocumentMap).includes(object.toUpperCase())
|
||||
: Object.keys(ExcludedDocumentMap).includes(object.toUpperCase()),
|
||||
)
|
||||
.includes(object.join("_").toUpperCase())
|
||||
) {
|
||||
return object.join(" ");
|
||||
}
|
||||
|
||||
return event
|
||||
.split(/(?=[A-Z])/)
|
||||
.slice(0, -2)
|
||||
.join(" ");
|
||||
}),
|
||||
).filter(object => object.length > 0);
|
||||
};
|
39
src/components/DryRunItemsList/DryRunItemsList.test.tsx
Normal file
39
src/components/DryRunItemsList/DryRunItemsList.test.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { MockedProvider, MockedResponse } from "@apollo/client/testing";
|
||||
import { ThemeProvider } from "@saleor/macaw-ui";
|
||||
import { productsMocks } from "@test/mocks/products";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import React from "react";
|
||||
|
||||
import DryRunItemsList from "./DryRunItemsList";
|
||||
|
||||
const mocks: MockedResponse[] = [...productsMocks];
|
||||
|
||||
jest.mock("react-intl", () => ({
|
||||
useIntl: jest.fn(() => ({
|
||||
formatMessage: jest.fn(x => x.defaultMessage),
|
||||
})),
|
||||
defineMessages: jest.fn(x => x),
|
||||
}));
|
||||
|
||||
describe("DryRunItemsList", () => {
|
||||
it("is available on the webhook page", async () => {
|
||||
// Arrange
|
||||
const props = {
|
||||
objectId: null,
|
||||
setObjectId: jest.fn(),
|
||||
object: "PRODUCT",
|
||||
};
|
||||
|
||||
// Act
|
||||
render(
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<ThemeProvider>
|
||||
<DryRunItemsList {...props} />
|
||||
</ThemeProvider>
|
||||
</MockedProvider>,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(screen.queryByTestId("dry-run-items-list")).toBeInTheDocument();
|
||||
});
|
||||
});
|
106
src/components/DryRunItemsList/DryRunItemsList.tsx
Normal file
106
src/components/DryRunItemsList/DryRunItemsList.tsx
Normal file
|
@ -0,0 +1,106 @@
|
|||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import { useStyles } from "@dashboard/custom-apps/components/WebhookEvents/styles";
|
||||
import { useQuery } from "@dashboard/hooks/graphql";
|
||||
import { mapEdgesToItems } from "@dashboard/utils/maps";
|
||||
import { Radio } from "@material-ui/core";
|
||||
import {
|
||||
List,
|
||||
ListBody,
|
||||
ListHeader,
|
||||
ListItem,
|
||||
ListItemCell,
|
||||
useListWidths,
|
||||
} from "@saleor/macaw-ui";
|
||||
import camelCase from "lodash/camelCase";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import Avatar from "../TableCellAvatar/Avatar";
|
||||
import { messages } from "./messages";
|
||||
import { DocumentMap, TData, TVariables } from "./utils";
|
||||
interface DryRunItemsListProps {
|
||||
objectId: string;
|
||||
setObjectId: React.Dispatch<any>;
|
||||
object: string;
|
||||
}
|
||||
|
||||
const DryRunItemsList = (props: DryRunItemsListProps) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
const { checkbox } = useListWidths();
|
||||
const { object, objectId, setObjectId } = props;
|
||||
const objectDocument = DocumentMap[object];
|
||||
const objectCollection =
|
||||
objectDocument.collection ?? camelCase(`${object.toLowerCase()}s`);
|
||||
|
||||
const { data, loading } = useQuery<TData, TVariables>(
|
||||
objectDocument.document,
|
||||
{
|
||||
displayLoader: true,
|
||||
variables: objectDocument.variables,
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<List
|
||||
gridTemplate={["1fr", checkbox, checkbox]}
|
||||
data-test-id="dry-run-items-list"
|
||||
>
|
||||
<ListHeader>
|
||||
<ListItem className={classes.listHeader}>
|
||||
<ListItemCell className={classes.listItemCell}>
|
||||
{intl.formatMessage(messages.item)}
|
||||
|
||||
{objectDocument.collection
|
||||
?.split(/(?=[A-Z])/)
|
||||
.map(item => item.toLowerCase())
|
||||
.join(" ")}
|
||||
|
||||
{objectDocument.displayedAttribute}
|
||||
</ListItemCell>
|
||||
</ListItem>
|
||||
</ListHeader>
|
||||
<ListBody className={classes.listBody}>
|
||||
{loading ? (
|
||||
<ListItem className={classes.listItem}>
|
||||
<ListItemCell className={classes.listItemCell}>
|
||||
<Skeleton />
|
||||
</ListItemCell>
|
||||
<ListItemCell>
|
||||
<Skeleton />
|
||||
</ListItemCell>
|
||||
<ListItemCell>
|
||||
<Skeleton />
|
||||
</ListItemCell>
|
||||
</ListItem>
|
||||
) : (
|
||||
(mapEdgesToItems<any>(data[objectCollection]) || []).map(
|
||||
(item, idx) => (
|
||||
<ListItem
|
||||
className={classes.listItem}
|
||||
key={idx}
|
||||
onClick={() => setObjectId(item.id)}
|
||||
>
|
||||
<ListItemCell className={classes.listItemCell}>
|
||||
{item.name ||
|
||||
item[objectDocument.displayedAttribute] ||
|
||||
item.id ||
|
||||
item.__typename}
|
||||
</ListItemCell>
|
||||
<ListItemCell>
|
||||
{item.thumbnail && <Avatar thumbnail={item.thumbnail?.url} />}
|
||||
</ListItemCell>
|
||||
<ListItemCell>
|
||||
<Radio checked={item.id === objectId} />
|
||||
</ListItemCell>
|
||||
</ListItem>
|
||||
),
|
||||
)
|
||||
)}
|
||||
</ListBody>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
DryRunItemsList.displayName = "DryRunItemsList";
|
||||
export default DryRunItemsList;
|
2
src/components/DryRunItemsList/index.ts
Normal file
2
src/components/DryRunItemsList/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./DryRunItemsList";
|
||||
export * from "./DryRunItemsList";
|
9
src/components/DryRunItemsList/messages.ts
Normal file
9
src/components/DryRunItemsList/messages.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
item: {
|
||||
id: "Xz/sDf",
|
||||
defaultMessage: "Item:",
|
||||
description: "Dry run items list item",
|
||||
},
|
||||
});
|
30
src/components/DryRunItemsList/queries.ts
Normal file
30
src/components/DryRunItemsList/queries.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { gql } from "@apollo/client";
|
||||
|
||||
export const checkouts = gql`
|
||||
query CheckoutList($first: Int, $after: String, $last: Int, $before: String) {
|
||||
checkouts(before: $before, after: $after, first: $first, last: $last) {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
created
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasPreviousPage
|
||||
hasNextPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const channels = gql`
|
||||
query ChannelList {
|
||||
channels {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
262
src/components/DryRunItemsList/utils.ts
Normal file
262
src/components/DryRunItemsList/utils.ts
Normal file
|
@ -0,0 +1,262 @@
|
|||
import {
|
||||
AppsListDocument,
|
||||
AppsListQuery,
|
||||
AppsListQueryVariables,
|
||||
AttributeListDocument,
|
||||
AttributeListQuery,
|
||||
AttributeListQueryVariables,
|
||||
CategoryDetailsQuery,
|
||||
CategoryDetailsQueryVariables,
|
||||
ChannelListDocument,
|
||||
CheckoutListDocument,
|
||||
CheckoutListQuery,
|
||||
CheckoutListQueryVariables,
|
||||
CollectionListDocument,
|
||||
CollectionListQuery,
|
||||
CollectionListQueryVariables,
|
||||
CustomerAddressesDocument,
|
||||
CustomerAddressesQuery,
|
||||
CustomerAddressesQueryVariables,
|
||||
CustomerDetailsQuery,
|
||||
CustomerDetailsQueryVariables,
|
||||
GiftCardListDocument,
|
||||
GiftCardListQuery,
|
||||
GiftCardListQueryVariables,
|
||||
ListCustomersDocument,
|
||||
ListCustomersQuery,
|
||||
ListCustomersQueryVariables,
|
||||
MenuListDocument,
|
||||
MenuListQuery,
|
||||
MenuListQueryVariables,
|
||||
OrderFulfillDataDocument,
|
||||
OrderFulfillDataQuery,
|
||||
OrderFulfillDataQueryVariables,
|
||||
OrderListDocument,
|
||||
OrderListQuery,
|
||||
OrderListQueryVariables,
|
||||
PageListDocument,
|
||||
PageListQuery,
|
||||
PageListQueryVariables,
|
||||
ProductListDocument,
|
||||
ProductListQuery,
|
||||
ProductListQueryVariables,
|
||||
ProductVariantListDocument,
|
||||
ProductVariantListQuery,
|
||||
ProductVariantListQueryVariables,
|
||||
RootCategoriesDocument,
|
||||
RootCategoriesQuery,
|
||||
RootCategoriesQueryVariables,
|
||||
SaleListDocument,
|
||||
SaleListQuery,
|
||||
SaleListQueryVariables,
|
||||
ShippingZonesDocument,
|
||||
StaffListDocument,
|
||||
StaffListQuery,
|
||||
StaffListQueryVariables,
|
||||
VoucherListDocument,
|
||||
VoucherListQuery,
|
||||
VoucherListQueryVariables,
|
||||
WarehouseListDocument,
|
||||
WarehouseListQuery,
|
||||
WarehouseListQueryVariables,
|
||||
} from "@dashboard/graphql";
|
||||
import { DocumentNode } from "graphql";
|
||||
|
||||
const DefaultVariables = {
|
||||
first: 100,
|
||||
};
|
||||
|
||||
export type TData =
|
||||
| ProductListQuery
|
||||
| OrderListQuery
|
||||
| GiftCardListQuery
|
||||
| CustomerAddressesQuery
|
||||
| AppsListQuery
|
||||
| AttributeListQuery
|
||||
| CategoryDetailsQuery
|
||||
| CheckoutListQuery
|
||||
| GiftCardListQuery
|
||||
| CollectionListQuery
|
||||
| CustomerDetailsQuery
|
||||
| OrderFulfillDataQuery
|
||||
| ListCustomersQuery
|
||||
| MenuListQuery
|
||||
| OrderListQuery
|
||||
| PageListQuery
|
||||
| ProductListQuery
|
||||
| ProductVariantListQuery
|
||||
| RootCategoriesQuery
|
||||
| SaleListQuery
|
||||
| StaffListQuery
|
||||
| VoucherListQuery
|
||||
| WarehouseListQuery;
|
||||
|
||||
export type TVariables =
|
||||
| ProductListQueryVariables
|
||||
| OrderListQueryVariables
|
||||
| GiftCardListQueryVariables
|
||||
| CustomerAddressesQueryVariables
|
||||
| AppsListQueryVariables
|
||||
| AttributeListQueryVariables
|
||||
| CategoryDetailsQueryVariables
|
||||
| ListCustomersQueryVariables
|
||||
| CheckoutListQueryVariables
|
||||
| GiftCardListQueryVariables
|
||||
| CollectionListQueryVariables
|
||||
| CustomerDetailsQueryVariables
|
||||
| OrderFulfillDataQueryVariables
|
||||
| MenuListQueryVariables
|
||||
| OrderListQueryVariables
|
||||
| PageListQueryVariables
|
||||
| ProductListQueryVariables
|
||||
| ProductVariantListQueryVariables
|
||||
| RootCategoriesQueryVariables
|
||||
| SaleListQueryVariables
|
||||
| StaffListQueryVariables
|
||||
| VoucherListQueryVariables
|
||||
| WarehouseListQueryVariables;
|
||||
|
||||
interface Document {
|
||||
document: DocumentNode;
|
||||
variables: TVariables;
|
||||
collection?: string;
|
||||
displayedAttribute?: string;
|
||||
}
|
||||
|
||||
export const DocumentMap: Record<string, Document> = {
|
||||
APP: {
|
||||
document: AppsListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "name",
|
||||
},
|
||||
ATTRIBUTE: {
|
||||
document: AttributeListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "name",
|
||||
},
|
||||
CATEGORY: {
|
||||
document: RootCategoriesDocument,
|
||||
variables: DefaultVariables,
|
||||
collection: "categories",
|
||||
displayedAttribute: "name",
|
||||
},
|
||||
GIFT_CARD: {
|
||||
document: GiftCardListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "last4CodeChars",
|
||||
},
|
||||
CHECKOUT: {
|
||||
document: CheckoutListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "id",
|
||||
},
|
||||
COLLECTION: {
|
||||
document: CollectionListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "name",
|
||||
},
|
||||
CUSTOMER: {
|
||||
document: ListCustomersDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "email",
|
||||
// TODO inverted name
|
||||
},
|
||||
|
||||
INVOICE: {
|
||||
document: OrderListDocument,
|
||||
variables: DefaultVariables,
|
||||
collection: "orders",
|
||||
displayedAttribute: "number",
|
||||
},
|
||||
MENU: {
|
||||
document: MenuListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "name",
|
||||
},
|
||||
ORDER: {
|
||||
document: OrderListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "number",
|
||||
},
|
||||
PAGE: {
|
||||
document: PageListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "title",
|
||||
},
|
||||
PRODUCT: {
|
||||
document: ProductListDocument,
|
||||
variables: {
|
||||
first: 100,
|
||||
hasChannel: true,
|
||||
hasSelectedAttributes: true,
|
||||
},
|
||||
displayedAttribute: "name",
|
||||
},
|
||||
SALE: {
|
||||
document: SaleListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "name",
|
||||
},
|
||||
SHIPPING_PRICE: {
|
||||
document: ShippingZonesDocument,
|
||||
variables: DefaultVariables,
|
||||
collection: "shippingZones",
|
||||
displayedAttribute: "name",
|
||||
},
|
||||
SHIPPING_ZONE: {
|
||||
document: ShippingZonesDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "name",
|
||||
},
|
||||
STAFF: {
|
||||
document: StaffListDocument,
|
||||
variables: DefaultVariables,
|
||||
collection: "staffUsers",
|
||||
displayedAttribute: "email",
|
||||
},
|
||||
VOUCHER: {
|
||||
document: VoucherListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "code",
|
||||
},
|
||||
WAREHOUSE: {
|
||||
document: WarehouseListDocument,
|
||||
variables: DefaultVariables,
|
||||
displayedAttribute: "name",
|
||||
},
|
||||
};
|
||||
|
||||
// Documents which require parent object or can't be handled ATM
|
||||
//
|
||||
export const ExcludedDocumentMap: Record<string, Document> = {
|
||||
ADDRESS: {
|
||||
document: CustomerAddressesDocument,
|
||||
variables: {
|
||||
// USER ID REQUIRED
|
||||
first: 100,
|
||||
},
|
||||
},
|
||||
// it's not a countable collection
|
||||
CHANNEL: {
|
||||
document: ChannelListDocument,
|
||||
variables: {},
|
||||
},
|
||||
FULFILLMENT: {
|
||||
document: OrderFulfillDataDocument,
|
||||
variables: {
|
||||
// ORDER ID REQUIRED
|
||||
first: 100,
|
||||
},
|
||||
},
|
||||
PRODUCT_VARIANT: {
|
||||
document: ProductVariantListDocument,
|
||||
variables: {
|
||||
// PRODUCT ID REQUIRED
|
||||
first: 100,
|
||||
},
|
||||
},
|
||||
TRANSLATION: {
|
||||
document: null,
|
||||
variables: {},
|
||||
},
|
||||
};
|
|
@ -1,7 +1,9 @@
|
|||
import { WebhookEventTypeAsyncEnum } from "@dashboard/graphql";
|
||||
import {
|
||||
CopyIcon,
|
||||
GraphiQLProvider,
|
||||
GraphiQLProviderProps,
|
||||
PlayIcon,
|
||||
PrettifyIcon,
|
||||
QueryEditor,
|
||||
ToolbarButton,
|
||||
|
@ -18,8 +20,16 @@ import {
|
|||
WriteableEditorProps,
|
||||
} from "@graphiql/react";
|
||||
import clsx from "clsx";
|
||||
import React, { ComponentType, PropsWithChildren, ReactNode } from "react";
|
||||
import React, {
|
||||
ComponentType,
|
||||
PropsWithChildren,
|
||||
ReactNode,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import DryRun from "../DryRun";
|
||||
import { messages } from "./messages";
|
||||
import {
|
||||
useDashboardTheme,
|
||||
useEditorStyles,
|
||||
|
@ -68,7 +78,7 @@ export function GraphiQL({
|
|||
visiblePlugin,
|
||||
defaultHeaders,
|
||||
...props
|
||||
}: GraphiQLProps) {
|
||||
}: GraphiQLProps & { asyncEvents: WebhookEventTypeAsyncEnum[] }) {
|
||||
// Ensure props are correct
|
||||
if (typeof fetcher !== "function") {
|
||||
throw new TypeError(
|
||||
|
@ -76,6 +86,9 @@ export function GraphiQL({
|
|||
);
|
||||
}
|
||||
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
const [result, setResult] = useState("");
|
||||
|
||||
return (
|
||||
<GraphiQLProvider
|
||||
getDefaultFieldNames={getDefaultFieldNames}
|
||||
|
@ -106,7 +119,19 @@ export function GraphiQL({
|
|||
validationRules={validationRules}
|
||||
variables={variables}
|
||||
>
|
||||
<GraphiQLInterface {...props} />
|
||||
<GraphiQLInterface
|
||||
{...props}
|
||||
showDialog={showDialog}
|
||||
setShowDialog={setShowDialog}
|
||||
result={result}
|
||||
/>
|
||||
<DryRun
|
||||
showDialog={showDialog}
|
||||
setShowDialog={setShowDialog}
|
||||
query={query}
|
||||
setResult={setResult}
|
||||
asyncEvents={props.asyncEvents}
|
||||
/>
|
||||
</GraphiQLProvider>
|
||||
);
|
||||
}
|
||||
|
@ -127,9 +152,13 @@ export type GraphiQLInterfaceProps = WriteableEditorProps &
|
|||
defaultEditorToolsVisibility?: boolean | "variables" | "headers";
|
||||
isHeadersEditorEnabled?: boolean;
|
||||
toolbar?: GraphiQLToolbarConfig;
|
||||
showDialog?: boolean;
|
||||
setShowDialog?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
result?: string;
|
||||
};
|
||||
|
||||
export function GraphiQLInterface(props: GraphiQLInterfaceProps) {
|
||||
const intl = useIntl();
|
||||
const editorContext = useEditorContext({ nonNull: true });
|
||||
const pluginContext = usePluginContext();
|
||||
|
||||
|
@ -150,6 +179,13 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) {
|
|||
isChildComponentType(child, GraphiQL.Toolbar),
|
||||
) || (
|
||||
<>
|
||||
<ToolbarButton
|
||||
onClick={() => props.setShowDialog(true)}
|
||||
label={intl.formatMessage(messages.toolbarButonLabel)}
|
||||
>
|
||||
<PlayIcon className="graphiql-toolbar-icon" aria-hidden="true" />
|
||||
</ToolbarButton>
|
||||
|
||||
<ToolbarButton
|
||||
onClick={() => prettify()}
|
||||
label="Prettify query (Shift-Ctrl-P)"
|
||||
|
@ -266,6 +302,14 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) {
|
|||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div ref={editorResize.dragBarRef}>
|
||||
<div className="graphiql-horizontal-drag-bar" />
|
||||
</div>
|
||||
<div ref={editorResize.secondRef}>
|
||||
<div className="graphiql-response">
|
||||
<pre className={classes.pre}>{props.result}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
9
src/components/GraphiQL/messages.ts
Normal file
9
src/components/GraphiQL/messages.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
toolbarButonLabel: {
|
||||
id: "Q2us5X",
|
||||
defaultMessage: "Dry run",
|
||||
description: "Dry run icon label",
|
||||
},
|
||||
});
|
|
@ -8,6 +8,9 @@ import { useEffect } from "react";
|
|||
|
||||
export const useStyles = makeStyles(
|
||||
() => ({
|
||||
pre: {
|
||||
whiteSpace: "break-spaces",
|
||||
},
|
||||
scrollable: {
|
||||
// Overrides inline styling which breaks scroll
|
||||
// on doc explorer plugin
|
||||
|
|
|
@ -9,6 +9,7 @@ import { capitalize } from "@dashboard/misc";
|
|||
import { Card, CardContent, Checkbox, Typography } from "@material-ui/core";
|
||||
import {
|
||||
List,
|
||||
ListBody,
|
||||
ListHeader,
|
||||
ListItem,
|
||||
ListItemCell,
|
||||
|
@ -105,7 +106,7 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
|
|||
<ListItemCell></ListItemCell>
|
||||
</ListItem>
|
||||
</ListHeader>
|
||||
<div className={classes.listItems}>
|
||||
<ListBody className={classes.listBody}>
|
||||
{Object.keys(EventTypes[tab]).map((object, idx) => (
|
||||
<ListItem
|
||||
key={idx}
|
||||
|
@ -130,7 +131,7 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
|
|||
</ListItemCell>
|
||||
</ListItem>
|
||||
))}
|
||||
</div>
|
||||
</ListBody>
|
||||
</List>
|
||||
</PageTabPanel>
|
||||
</div>
|
||||
|
@ -143,6 +144,7 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
|
|||
</ListItemCell>
|
||||
</ListItem>
|
||||
</ListHeader>
|
||||
<ListBody className={classes.listBody}>
|
||||
{object &&
|
||||
EventTypes[tab][object] &&
|
||||
EventTypes[tab][object].map((event, idx) => (
|
||||
|
@ -165,6 +167,7 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
|
|||
</ListItemCell>
|
||||
</ListItem>
|
||||
))}
|
||||
</ListBody>
|
||||
</List>
|
||||
</div>
|
||||
</Grid>
|
||||
|
@ -177,7 +180,7 @@ export default WebhookEvents;
|
|||
|
||||
type Actions = string[];
|
||||
|
||||
const AsyncWebhookTypes: Record<string, Actions> = {
|
||||
export const AsyncWebhookTypes: Record<string, Actions> = {
|
||||
ADDRESS: ["CREATED", "UPDATED", "DELETED"],
|
||||
APP: ["INSTALLED", "UPDATED", "DELETED"],
|
||||
ATTRIBUTE: ["CREATED", "UPDATED", "DELETED"],
|
||||
|
|
|
@ -3,7 +3,6 @@ import { makeStyles } from "@saleor/macaw-ui";
|
|||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
eventsWrapper: {
|
||||
overflow: "scroll",
|
||||
padding: theme.spacing(4),
|
||||
paddingLeft: 0,
|
||||
},
|
||||
|
@ -17,6 +16,10 @@ export const useStyles = makeStyles(
|
|||
padding: theme.spacing(1),
|
||||
minHeight: 0,
|
||||
},
|
||||
listBody: {
|
||||
height: 300,
|
||||
overflow: "scroll",
|
||||
},
|
||||
listItem: {
|
||||
minHeight: 0,
|
||||
gap: 0,
|
||||
|
@ -24,10 +27,7 @@ export const useStyles = makeStyles(
|
|||
},
|
||||
listItemCell: {
|
||||
paddingLeft: "0 !important",
|
||||
},
|
||||
listItems: {
|
||||
height: 300,
|
||||
overflow: "scroll",
|
||||
wordBreak: "break-all",
|
||||
},
|
||||
checkbox: {
|
||||
padding: 0,
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
WebhookEventTypeSyncEnum,
|
||||
} from "@dashboard/graphql";
|
||||
import { Fetcher } from "@graphiql/toolkit";
|
||||
import { ApolloMockedProvider } from "@test/ApolloMockedProvider";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import React from "react";
|
||||
|
||||
|
@ -51,7 +52,11 @@ describe("WebhookSubscriptionQuery", () => {
|
|||
// const user = userEvent.setup();
|
||||
|
||||
// Act
|
||||
render(<WebhookSubscriptionQuery {...props} />);
|
||||
render(
|
||||
<ApolloMockedProvider>
|
||||
<WebhookSubscriptionQuery {...props} />
|
||||
</ApolloMockedProvider>,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(screen.queryByTestId("graphiql-container")).toBeInTheDocument();
|
||||
|
|
|
@ -59,6 +59,7 @@ const WebhookSubscriptionQuery: React.FC<WebhookSubscriptionQueryProps> = ({
|
|||
onEditQuery={setQuery}
|
||||
plugins={[explorerPlugin]}
|
||||
isHeadersEditorEnabled={false}
|
||||
asyncEvents={data.asyncEvents}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
@ -1,88 +1,6 @@
|
|||
import { MultiAutocompleteChoiceType } from "@dashboard/components/MultiAutocompleteSelectField";
|
||||
import { WebhookEventTypeAsyncEnum } from "@dashboard/graphql";
|
||||
|
||||
import { filterSelectedAsyncEvents, mapAsyncEventsToChoices } from "./utils";
|
||||
|
||||
describe("Custom Apps mapping events", () => {
|
||||
it("should return enabled async events choices when not any event selected", () => {
|
||||
// Arrange
|
||||
const events: WebhookEventTypeAsyncEnum[] = [
|
||||
WebhookEventTypeAsyncEnum.ANY_EVENTS,
|
||||
WebhookEventTypeAsyncEnum.PAGE_CREATED,
|
||||
WebhookEventTypeAsyncEnum.PRODUCT_CREATED,
|
||||
];
|
||||
const selectedEvents: WebhookEventTypeAsyncEnum[] = [
|
||||
WebhookEventTypeAsyncEnum.PAGE_CREATED,
|
||||
];
|
||||
|
||||
// Act
|
||||
const asyncEvents = mapAsyncEventsToChoices(events, selectedEvents);
|
||||
|
||||
// Assert
|
||||
const expectedAsyncEvents: MultiAutocompleteChoiceType[] = [
|
||||
{
|
||||
label: WebhookEventTypeAsyncEnum.ANY_EVENTS,
|
||||
value: WebhookEventTypeAsyncEnum.ANY_EVENTS,
|
||||
badge: undefined,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
label: WebhookEventTypeAsyncEnum.PAGE_CREATED,
|
||||
value: WebhookEventTypeAsyncEnum.PAGE_CREATED,
|
||||
badge: undefined,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
label: WebhookEventTypeAsyncEnum.PRODUCT_CREATED,
|
||||
value: WebhookEventTypeAsyncEnum.PRODUCT_CREATED,
|
||||
badge: undefined,
|
||||
disabled: false,
|
||||
},
|
||||
];
|
||||
expect(asyncEvents).toHaveLength(3);
|
||||
expect(asyncEvents).toEqual(expectedAsyncEvents);
|
||||
});
|
||||
|
||||
it("should return disabled async events choices when any event selected", () => {
|
||||
// Arrange
|
||||
const events: WebhookEventTypeAsyncEnum[] = [
|
||||
WebhookEventTypeAsyncEnum.ANY_EVENTS,
|
||||
WebhookEventTypeAsyncEnum.PAGE_CREATED,
|
||||
WebhookEventTypeAsyncEnum.PRODUCT_CREATED,
|
||||
];
|
||||
const selectedEvents: WebhookEventTypeAsyncEnum[] = [
|
||||
WebhookEventTypeAsyncEnum.ANY_EVENTS,
|
||||
WebhookEventTypeAsyncEnum.PAGE_CREATED,
|
||||
];
|
||||
|
||||
// Act
|
||||
const asyncEvents = mapAsyncEventsToChoices(events, selectedEvents);
|
||||
|
||||
// Assert
|
||||
const expectedAsyncEvents: MultiAutocompleteChoiceType[] = [
|
||||
{
|
||||
label: WebhookEventTypeAsyncEnum.ANY_EVENTS,
|
||||
value: WebhookEventTypeAsyncEnum.ANY_EVENTS,
|
||||
badge: undefined,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
label: WebhookEventTypeAsyncEnum.PAGE_CREATED,
|
||||
value: WebhookEventTypeAsyncEnum.PAGE_CREATED,
|
||||
badge: undefined,
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: WebhookEventTypeAsyncEnum.PRODUCT_CREATED,
|
||||
value: WebhookEventTypeAsyncEnum.PRODUCT_CREATED,
|
||||
badge: undefined,
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
expect(asyncEvents).toHaveLength(3);
|
||||
expect(asyncEvents).toEqual(expectedAsyncEvents);
|
||||
});
|
||||
});
|
||||
import { filterSelectedAsyncEvents } from "./utils";
|
||||
|
||||
describe("Custom Apps filtering events", () => {
|
||||
it("should return selected async event types when not any event selected", () => {
|
||||
|
|
|
@ -1,71 +1,9 @@
|
|||
import { MultiAutocompleteChoiceType } from "@dashboard/components/MultiAutocompleteSelectField";
|
||||
import PreviewPill from "@dashboard/components/PreviewPill";
|
||||
import {
|
||||
WebhookEventTypeAsyncEnum,
|
||||
WebhookEventTypeSyncEnum,
|
||||
WebhookFragment,
|
||||
} from "@dashboard/graphql";
|
||||
import React from "react";
|
||||
import { WebhookEventTypeAsyncEnum, WebhookFragment } from "@dashboard/graphql";
|
||||
|
||||
export function isUnnamed(webhook: WebhookFragment | undefined): boolean {
|
||||
return !webhook?.name;
|
||||
}
|
||||
|
||||
type WebhookEventType = WebhookEventTypeSyncEnum | WebhookEventTypeAsyncEnum;
|
||||
|
||||
const isWebhookInPreview = (webhook: WebhookEventType) =>
|
||||
(
|
||||
[
|
||||
WebhookEventTypeSyncEnum.CHECKOUT_CALCULATE_TAXES,
|
||||
WebhookEventTypeSyncEnum.ORDER_CALCULATE_TAXES,
|
||||
] as WebhookEventType[]
|
||||
).includes(webhook);
|
||||
|
||||
const isAsyncWebhookInPreview = (webhook: WebhookEventType) =>
|
||||
(
|
||||
[
|
||||
WebhookEventTypeAsyncEnum.GIFT_CARD_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.ORDER_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.CUSTOMER_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.COLLECTION_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.PRODUCT_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.PRODUCT_VARIANT_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.CHECKOUT_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.FULFILLMENT_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.SHIPPING_ZONE_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.TRANSACTION_ITEM_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.WAREHOUSE_METADATA_UPDATED,
|
||||
WebhookEventTypeAsyncEnum.VOUCHER_METADATA_UPDATED,
|
||||
] as WebhookEventType[]
|
||||
).includes(webhook);
|
||||
|
||||
export function mapSyncEventsToChoices(
|
||||
events: WebhookEventTypeSyncEnum[],
|
||||
): MultiAutocompleteChoiceType[] {
|
||||
return events.map(event => ({
|
||||
label: event,
|
||||
value: event,
|
||||
badge: isWebhookInPreview(event) ? <PreviewPill /> : undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
export function mapAsyncEventsToChoices(
|
||||
events: WebhookEventTypeAsyncEnum[],
|
||||
selectedEvents: WebhookEventTypeAsyncEnum[],
|
||||
): MultiAutocompleteChoiceType[] {
|
||||
const isAnyAsyncEventSelected = selectedEvents.includes(
|
||||
WebhookEventTypeAsyncEnum.ANY_EVENTS,
|
||||
);
|
||||
|
||||
return events.map(event => ({
|
||||
label: event,
|
||||
value: event,
|
||||
badge: isAsyncWebhookInPreview(event) ? <PreviewPill /> : undefined,
|
||||
disabled:
|
||||
event !== WebhookEventTypeAsyncEnum.ANY_EVENTS && isAnyAsyncEventSelected,
|
||||
}));
|
||||
}
|
||||
|
||||
export const filterSelectedAsyncEvents = (
|
||||
asyncEvents: WebhookEventTypeAsyncEnum[],
|
||||
) => {
|
||||
|
|
|
@ -297,9 +297,9 @@
|
|||
"ProductVariant",
|
||||
"Product",
|
||||
"ProductType",
|
||||
"Collection",
|
||||
"Category",
|
||||
"ProductMedia",
|
||||
"Category",
|
||||
"Collection",
|
||||
"PageType"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -5095,6 +5095,129 @@ export function useAddressValidationRulesLazyQuery(baseOptions?: ApolloReactHook
|
|||
export type AddressValidationRulesQueryHookResult = ReturnType<typeof useAddressValidationRulesQuery>;
|
||||
export type AddressValidationRulesLazyQueryHookResult = ReturnType<typeof useAddressValidationRulesLazyQuery>;
|
||||
export type AddressValidationRulesQueryResult = Apollo.QueryResult<Types.AddressValidationRulesQuery, Types.AddressValidationRulesQueryVariables>;
|
||||
export const TriggerWebhookDryRunDocument = gql`
|
||||
mutation TriggerWebhookDryRun($objectId: ID!, $query: String!) {
|
||||
webhookDryRun(objectId: $objectId, query: $query) {
|
||||
payload
|
||||
errors {
|
||||
field
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type TriggerWebhookDryRunMutationFn = Apollo.MutationFunction<Types.TriggerWebhookDryRunMutation, Types.TriggerWebhookDryRunMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useTriggerWebhookDryRunMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useTriggerWebhookDryRunMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useTriggerWebhookDryRunMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [triggerWebhookDryRunMutation, { data, loading, error }] = useTriggerWebhookDryRunMutation({
|
||||
* variables: {
|
||||
* objectId: // value for 'objectId'
|
||||
* query: // value for 'query'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTriggerWebhookDryRunMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<Types.TriggerWebhookDryRunMutation, Types.TriggerWebhookDryRunMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useMutation<Types.TriggerWebhookDryRunMutation, Types.TriggerWebhookDryRunMutationVariables>(TriggerWebhookDryRunDocument, options);
|
||||
}
|
||||
export type TriggerWebhookDryRunMutationHookResult = ReturnType<typeof useTriggerWebhookDryRunMutation>;
|
||||
export type TriggerWebhookDryRunMutationResult = Apollo.MutationResult<Types.TriggerWebhookDryRunMutation>;
|
||||
export type TriggerWebhookDryRunMutationOptions = Apollo.BaseMutationOptions<Types.TriggerWebhookDryRunMutation, Types.TriggerWebhookDryRunMutationVariables>;
|
||||
export const CheckoutListDocument = gql`
|
||||
query CheckoutList($first: Int, $after: String, $last: Int, $before: String) {
|
||||
checkouts(before: $before, after: $after, first: $first, last: $last) {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
created
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasPreviousPage
|
||||
hasNextPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useCheckoutListQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useCheckoutListQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useCheckoutListQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useCheckoutListQuery({
|
||||
* variables: {
|
||||
* first: // value for 'first'
|
||||
* after: // value for 'after'
|
||||
* last: // value for 'last'
|
||||
* before: // value for 'before'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCheckoutListQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<Types.CheckoutListQuery, Types.CheckoutListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useQuery<Types.CheckoutListQuery, Types.CheckoutListQueryVariables>(CheckoutListDocument, options);
|
||||
}
|
||||
export function useCheckoutListLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.CheckoutListQuery, Types.CheckoutListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useLazyQuery<Types.CheckoutListQuery, Types.CheckoutListQueryVariables>(CheckoutListDocument, options);
|
||||
}
|
||||
export type CheckoutListQueryHookResult = ReturnType<typeof useCheckoutListQuery>;
|
||||
export type CheckoutListLazyQueryHookResult = ReturnType<typeof useCheckoutListLazyQuery>;
|
||||
export type CheckoutListQueryResult = Apollo.QueryResult<Types.CheckoutListQuery, Types.CheckoutListQueryVariables>;
|
||||
export const ChannelListDocument = gql`
|
||||
query ChannelList {
|
||||
channels {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useChannelListQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useChannelListQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useChannelListQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useChannelListQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useChannelListQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<Types.ChannelListQuery, Types.ChannelListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useQuery<Types.ChannelListQuery, Types.ChannelListQueryVariables>(ChannelListDocument, options);
|
||||
}
|
||||
export function useChannelListLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.ChannelListQuery, Types.ChannelListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useLazyQuery<Types.ChannelListQuery, Types.ChannelListQueryVariables>(ChannelListDocument, options);
|
||||
}
|
||||
export type ChannelListQueryHookResult = ReturnType<typeof useChannelListQuery>;
|
||||
export type ChannelListLazyQueryHookResult = ReturnType<typeof useChannelListLazyQuery>;
|
||||
export type ChannelListQueryResult = Apollo.QueryResult<Types.ChannelListQuery, Types.ChannelListQueryVariables>;
|
||||
export const CheckIfOrderExistsDocument = gql`
|
||||
query CheckIfOrderExists($id: ID!) {
|
||||
order(id: $id) {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -329,6 +329,12 @@ export type AttributeCreateInput = {
|
|||
storefrontSearchPosition?: InputMaybe<Scalars['Int']>;
|
||||
/** Whether the attribute can be displayed in the admin product list. */
|
||||
availableInGrid?: InputMaybe<Scalars['Boolean']>;
|
||||
/**
|
||||
* External ID of this attribute.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
/** An enumeration. */
|
||||
|
@ -459,6 +465,12 @@ export type AttributeUpdateInput = {
|
|||
storefrontSearchPosition?: InputMaybe<Scalars['Int']>;
|
||||
/** Whether the attribute can be displayed in the admin product list. */
|
||||
availableInGrid?: InputMaybe<Scalars['Boolean']>;
|
||||
/**
|
||||
* External ID of this product.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AttributeValueCreateInput = {
|
||||
|
@ -482,6 +494,12 @@ export type AttributeValueCreateInput = {
|
|||
fileUrl?: InputMaybe<Scalars['String']>;
|
||||
/** File content type. */
|
||||
contentType?: InputMaybe<Scalars['String']>;
|
||||
/**
|
||||
* External ID of this attribute value.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
/** Name of a value displayed in the interface. */
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
@ -494,8 +512,26 @@ export type AttributeValueFilterInput = {
|
|||
export type AttributeValueInput = {
|
||||
/** ID of the selected attribute. */
|
||||
id?: InputMaybe<Scalars['ID']>;
|
||||
/** The value or slug of an attribute to resolve. If the passed value is non-existent, it will be created. */
|
||||
/** The value or slug of an attribute to resolve. If the passed value is non-existent, it will be created. This field will be removed in Saleor 4.0. */
|
||||
values?: InputMaybe<Array<Scalars['String']>>;
|
||||
/**
|
||||
* Attribute value ID.
|
||||
*
|
||||
* Added in Saleor 3.9.
|
||||
*/
|
||||
dropdown?: InputMaybe<AttributeValueSelectableTypeInput>;
|
||||
/**
|
||||
* List of attribute value IDs.
|
||||
*
|
||||
* Added in Saleor 3.9.
|
||||
*/
|
||||
multiselect?: InputMaybe<Array<AttributeValueSelectableTypeInput>>;
|
||||
/**
|
||||
* Numeric value of an attribute.
|
||||
*
|
||||
* Added in Saleor 3.9.
|
||||
*/
|
||||
numeric?: InputMaybe<Scalars['String']>;
|
||||
/** URL of the file attribute. Every time, a new value is created. */
|
||||
file?: InputMaybe<Scalars['String']>;
|
||||
/** File content type. */
|
||||
|
@ -514,6 +550,18 @@ export type AttributeValueInput = {
|
|||
dateTime?: InputMaybe<Scalars['DateTime']>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents attribute value. If no ID provided, value will be resolved.
|
||||
*
|
||||
* Added in Saleor 3.9.
|
||||
*/
|
||||
export type AttributeValueSelectableTypeInput = {
|
||||
/** ID of an attribute value. */
|
||||
id?: InputMaybe<Scalars['ID']>;
|
||||
/** The value or slug of an attribute to resolve. If the passed value is non-existent, it will be created. */
|
||||
value?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AttributeValueTranslationInput = {
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
/**
|
||||
|
@ -547,6 +595,12 @@ export type AttributeValueUpdateInput = {
|
|||
fileUrl?: InputMaybe<Scalars['String']>;
|
||||
/** File content type. */
|
||||
contentType?: InputMaybe<Scalars['String']>;
|
||||
/**
|
||||
* External ID of this attribute value.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
/** Name of a value displayed in the interface. */
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
@ -1388,6 +1442,12 @@ export type CustomerInput = {
|
|||
note?: InputMaybe<Scalars['String']>;
|
||||
/** User language code. */
|
||||
languageCode?: InputMaybe<LanguageCodeEnum>;
|
||||
/**
|
||||
* External ID of the customer.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type DateRangeInput = {
|
||||
|
@ -1511,6 +1571,12 @@ export type DraftOrderCreateInput = {
|
|||
channelId?: InputMaybe<Scalars['ID']>;
|
||||
/** URL of a view where users should be redirected to see the order details. URL in RFC 1808 format. */
|
||||
redirectUrl?: InputMaybe<Scalars['String']>;
|
||||
/**
|
||||
* External ID of this order.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
/** Variant line input consisting of variant ID and quantity of products. */
|
||||
lines?: InputMaybe<Array<OrderLineCreateInput>>;
|
||||
};
|
||||
|
@ -1536,6 +1602,12 @@ export type DraftOrderInput = {
|
|||
channelId?: InputMaybe<Scalars['ID']>;
|
||||
/** URL of a view where users should be redirected to see the order details. URL in RFC 1808 format. */
|
||||
redirectUrl?: InputMaybe<Scalars['String']>;
|
||||
/**
|
||||
* External ID of this order.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export enum EventDeliveryAttemptSortField {
|
||||
|
@ -2785,6 +2857,18 @@ export enum MeasurementUnitsEnum {
|
|||
TONNE = 'TONNE'
|
||||
}
|
||||
|
||||
export enum MediaChoicesSortField {
|
||||
/** Sort media by ID. */
|
||||
ID = 'ID'
|
||||
}
|
||||
|
||||
export type MediaSortingInput = {
|
||||
/** Specifies the direction in which to sort products. */
|
||||
direction: OrderDirection;
|
||||
/** Sort media by the selected field. */
|
||||
field: MediaChoicesSortField;
|
||||
};
|
||||
|
||||
export type MenuCreateInput = {
|
||||
/** Name of the menu. */
|
||||
name: Scalars['String'];
|
||||
|
@ -3351,6 +3435,12 @@ export type OrderUpdateInput = {
|
|||
userEmail?: InputMaybe<Scalars['String']>;
|
||||
/** Shipping address of the customer. */
|
||||
shippingAddress?: InputMaybe<AddressInput>;
|
||||
/**
|
||||
* External ID of this order.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type OrderUpdateShippingInput = {
|
||||
|
@ -3883,6 +3973,12 @@ export type ProductCreateInput = {
|
|||
* Added in Saleor 3.8.
|
||||
*/
|
||||
privateMetadata?: InputMaybe<Array<MetadataInput>>;
|
||||
/**
|
||||
* External ID of this product.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
/** ID of the type that product belongs to. */
|
||||
productType: Scalars['ID'];
|
||||
};
|
||||
|
@ -4028,6 +4124,12 @@ export type ProductInput = {
|
|||
* Added in Saleor 3.8.
|
||||
*/
|
||||
privateMetadata?: InputMaybe<Array<MetadataInput>>;
|
||||
/**
|
||||
* External ID of this product.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type ProductMediaCreateInput = {
|
||||
|
@ -4244,6 +4346,12 @@ export type ProductVariantBulkCreateInput = {
|
|||
* Added in Saleor 3.8.
|
||||
*/
|
||||
privateMetadata?: InputMaybe<Array<MetadataInput>>;
|
||||
/**
|
||||
* External ID of this product variant.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
/** Stocks of a product available for sale. */
|
||||
stocks?: InputMaybe<Array<StockInput>>;
|
||||
/** List of prices assigned to channels. */
|
||||
|
@ -4306,6 +4414,12 @@ export type ProductVariantCreateInput = {
|
|||
* Added in Saleor 3.8.
|
||||
*/
|
||||
privateMetadata?: InputMaybe<Array<MetadataInput>>;
|
||||
/**
|
||||
* External ID of this product variant.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
/** Product ID of which type is the variant. */
|
||||
product: Scalars['ID'];
|
||||
/** Stocks of a product available for sale. */
|
||||
|
@ -4359,6 +4473,12 @@ export type ProductVariantInput = {
|
|||
* Added in Saleor 3.8.
|
||||
*/
|
||||
privateMetadata?: InputMaybe<Array<MetadataInput>>;
|
||||
/**
|
||||
* External ID of this product variant.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export enum ProductVariantSortField {
|
||||
|
@ -5142,6 +5262,12 @@ export type UserCreateInput = {
|
|||
note?: InputMaybe<Scalars['String']>;
|
||||
/** User language code. */
|
||||
languageCode?: InputMaybe<LanguageCodeEnum>;
|
||||
/**
|
||||
* External ID of the customer.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
/** URL of a view where users should be redirected to set the password. URL in RFC 1808 format. */
|
||||
redirectUrl?: InputMaybe<Scalars['String']>;
|
||||
/** Slug of a channel which will be used for notify user. Optional when only one channel exists. */
|
||||
|
@ -5320,6 +5446,12 @@ export type WarehouseCreateInput = {
|
|||
slug?: InputMaybe<Scalars['String']>;
|
||||
/** The email address of the warehouse. */
|
||||
email?: InputMaybe<Scalars['String']>;
|
||||
/**
|
||||
* External ID of the warehouse.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
/** Warehouse name. */
|
||||
name: Scalars['String'];
|
||||
/** Address of the warehouse. */
|
||||
|
@ -5368,6 +5500,12 @@ export type WarehouseUpdateInput = {
|
|||
slug?: InputMaybe<Scalars['String']>;
|
||||
/** The email address of the warehouse. */
|
||||
email?: InputMaybe<Scalars['String']>;
|
||||
/**
|
||||
* External ID of the warehouse.
|
||||
*
|
||||
* Added in Saleor 3.10.
|
||||
*/
|
||||
externalReference?: InputMaybe<Scalars['String']>;
|
||||
/** Warehouse name. */
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
/** Address of the warehouse. */
|
||||
|
@ -5425,13 +5563,22 @@ export type WebhookCreateInput = {
|
|||
query?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
/** An enumeration. */
|
||||
export enum WebhookDryRunErrorCode {
|
||||
GRAPHQL_ERROR = 'GRAPHQL_ERROR',
|
||||
NOT_FOUND = 'NOT_FOUND',
|
||||
INVALID_ID = 'INVALID_ID',
|
||||
MISSING_PERMISSION = 'MISSING_PERMISSION'
|
||||
}
|
||||
|
||||
/** An enumeration. */
|
||||
export enum WebhookErrorCode {
|
||||
GRAPHQL_ERROR = 'GRAPHQL_ERROR',
|
||||
INVALID = 'INVALID',
|
||||
NOT_FOUND = 'NOT_FOUND',
|
||||
REQUIRED = 'REQUIRED',
|
||||
UNIQUE = 'UNIQUE'
|
||||
UNIQUE = 'UNIQUE',
|
||||
DELETE_FAILED = 'DELETE_FAILED'
|
||||
}
|
||||
|
||||
/** Enum determining type of webhook. */
|
||||
|
@ -6200,6 +6347,15 @@ export enum WebhookSampleEventTypeEnum {
|
|||
OBSERVABILITY = 'OBSERVABILITY'
|
||||
}
|
||||
|
||||
/** An enumeration. */
|
||||
export enum WebhookTriggerErrorCode {
|
||||
GRAPHQL_ERROR = 'GRAPHQL_ERROR',
|
||||
NOT_FOUND = 'NOT_FOUND',
|
||||
INVALID_ID = 'INVALID_ID',
|
||||
MISSING_PERMISSION = 'MISSING_PERMISSION',
|
||||
MISSING_QUERY = 'MISSING_QUERY'
|
||||
}
|
||||
|
||||
export type WebhookUpdateInput = {
|
||||
/** The new name of the webhook. */
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
|
@ -6677,6 +6833,29 @@ export type AddressValidationRulesQueryVariables = Exact<{
|
|||
|
||||
export type AddressValidationRulesQuery = { __typename: 'Query', addressValidationRules: { __typename: 'AddressValidationData', allowedFields: Array<string>, countryAreaChoices: Array<{ __typename: 'ChoiceValue', raw: string | null, verbose: string | null }> } | null };
|
||||
|
||||
export type TriggerWebhookDryRunMutationVariables = Exact<{
|
||||
objectId: Scalars['ID'];
|
||||
query: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type TriggerWebhookDryRunMutation = { __typename: 'Mutation', webhookDryRun: { __typename: 'WebhookDryRun', payload: any | null, errors: Array<{ __typename: 'WebhookDryRunError', field: string | null, message: string | null }> } | null };
|
||||
|
||||
export type CheckoutListQueryVariables = Exact<{
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type CheckoutListQuery = { __typename: 'Query', checkouts: { __typename: 'CheckoutCountableConnection', edges: Array<{ __typename: 'CheckoutCountableEdge', cursor: string, node: { __typename: 'Checkout', id: string, created: any } }>, pageInfo: { __typename: 'PageInfo', hasPreviousPage: boolean, hasNextPage: boolean, startCursor: string | null, endCursor: string | null } } | null };
|
||||
|
||||
export type ChannelListQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ChannelListQuery = { __typename: 'Query', channels: Array<{ __typename: 'Channel', id: string, name: string }> | null };
|
||||
|
||||
export type CheckIfOrderExistsQueryVariables = Exact<{
|
||||
id: Scalars['ID'];
|
||||
}>;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export * from "./address";
|
||||
export * from "./apps";
|
||||
export * from "./pageTypes";
|
||||
export * from "./products";
|
||||
export * from "./warehouses";
|
||||
|
|
277
testUtils/mocks/products.ts
Normal file
277
testUtils/mocks/products.ts
Normal file
|
@ -0,0 +1,277 @@
|
|||
import { MockedResponse } from "@apollo/client/testing";
|
||||
import { productListQuery } from "@dashboard/products/queries";
|
||||
|
||||
export const productsMocks: MockedResponse[] = [
|
||||
{
|
||||
request: {
|
||||
query: productListQuery,
|
||||
variables: {
|
||||
first: 3,
|
||||
hasChannel: true,
|
||||
hasSelectedAttributes: true,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
products: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: "UHJvZHVjdDo3Mg==",
|
||||
name: "Apple Juice",
|
||||
thumbnail: {
|
||||
url: "https://wb-t-3-4-6.staging.saleor.cloud/media/thumbnails/products/saleordemoproduct_fd_juice_06_XO6p2Xu_thumbnail_256.png",
|
||||
__typename: "Image",
|
||||
},
|
||||
productType: {
|
||||
id: "UHJvZHVjdFR5cGU6OQ==",
|
||||
name: "Juice",
|
||||
hasVariants: true,
|
||||
__typename: "ProductType",
|
||||
},
|
||||
channelListings: [
|
||||
{
|
||||
isPublished: true,
|
||||
publicationDate: "2020-01-01",
|
||||
isAvailableForPurchase: true,
|
||||
availableForPurchase: "2020-08-31",
|
||||
visibleInListings: true,
|
||||
channel: {
|
||||
id: "Q2hhbm5lbDox",
|
||||
name: "Default channel",
|
||||
currencyCode: "USD",
|
||||
__typename: "Channel",
|
||||
},
|
||||
__typename: "ProductChannelListing",
|
||||
pricing: {
|
||||
priceRange: {
|
||||
start: {
|
||||
net: {
|
||||
amount: 5.0,
|
||||
currency: "USD",
|
||||
__typename: "Money",
|
||||
},
|
||||
__typename: "TaxedMoney",
|
||||
},
|
||||
stop: {
|
||||
net: {
|
||||
amount: 7.0,
|
||||
currency: "USD",
|
||||
__typename: "Money",
|
||||
},
|
||||
__typename: "TaxedMoney",
|
||||
},
|
||||
__typename: "TaxedMoneyRange",
|
||||
},
|
||||
__typename: "ProductPricingInfo",
|
||||
},
|
||||
},
|
||||
],
|
||||
__typename: "Product",
|
||||
updatedAt: "2021-03-10T12:31:34.521213+00:00",
|
||||
attributes: [
|
||||
{
|
||||
attribute: {
|
||||
id: "QXR0cmlidXRlOjE2",
|
||||
__typename: "Attribute",
|
||||
},
|
||||
values: [
|
||||
{
|
||||
id: "QXR0cmlidXRlVmFsdWU6Mw==",
|
||||
name: "Apple",
|
||||
slug: "apple",
|
||||
file: null,
|
||||
reference: null,
|
||||
boolean: null,
|
||||
date: null,
|
||||
dateTime: null,
|
||||
value: "",
|
||||
__typename: "AttributeValue",
|
||||
},
|
||||
],
|
||||
__typename: "SelectedAttribute",
|
||||
},
|
||||
],
|
||||
},
|
||||
__typename: "ProductCountableEdge",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: "UHJvZHVjdDo3NA==",
|
||||
name: "Banana Juice",
|
||||
thumbnail: {
|
||||
url: "https://wb-t-3-4-6.staging.saleor.cloud/media/thumbnails/products/saleordemoproduct_fd_juice_01_thumbnail_256.png",
|
||||
__typename: "Image",
|
||||
},
|
||||
productType: {
|
||||
id: "UHJvZHVjdFR5cGU6OQ==",
|
||||
name: "Juice",
|
||||
hasVariants: true,
|
||||
__typename: "ProductType",
|
||||
},
|
||||
channelListings: [
|
||||
{
|
||||
isPublished: true,
|
||||
publicationDate: "2020-01-01",
|
||||
isAvailableForPurchase: true,
|
||||
availableForPurchase: "2020-08-31",
|
||||
visibleInListings: true,
|
||||
channel: {
|
||||
id: "Q2hhbm5lbDox",
|
||||
name: "Default channel",
|
||||
currencyCode: "USD",
|
||||
__typename: "Channel",
|
||||
},
|
||||
__typename: "ProductChannelListing",
|
||||
pricing: {
|
||||
priceRange: {
|
||||
start: {
|
||||
net: {
|
||||
amount: 5.0,
|
||||
currency: "USD",
|
||||
__typename: "Money",
|
||||
},
|
||||
__typename: "TaxedMoney",
|
||||
},
|
||||
stop: {
|
||||
net: {
|
||||
amount: 7.0,
|
||||
currency: "USD",
|
||||
__typename: "Money",
|
||||
},
|
||||
__typename: "TaxedMoney",
|
||||
},
|
||||
__typename: "TaxedMoneyRange",
|
||||
},
|
||||
__typename: "ProductPricingInfo",
|
||||
},
|
||||
},
|
||||
],
|
||||
__typename: "Product",
|
||||
updatedAt: "2021-03-10T12:31:34.897799+00:00",
|
||||
attributes: [
|
||||
{
|
||||
attribute: {
|
||||
id: "QXR0cmlidXRlOjE2",
|
||||
__typename: "Attribute",
|
||||
},
|
||||
values: [
|
||||
{
|
||||
id: "QXR0cmlidXRlVmFsdWU6NTA=",
|
||||
name: "Banana",
|
||||
slug: "banana",
|
||||
file: null,
|
||||
reference: null,
|
||||
boolean: null,
|
||||
date: null,
|
||||
dateTime: null,
|
||||
value: "",
|
||||
__typename: "AttributeValue",
|
||||
},
|
||||
],
|
||||
__typename: "SelectedAttribute",
|
||||
},
|
||||
],
|
||||
},
|
||||
__typename: "ProductCountableEdge",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: "UHJvZHVjdDoxMjI=",
|
||||
name: "Bathroom Songs",
|
||||
thumbnail: {
|
||||
url: "https://wb-t-3-4-6.staging.saleor.cloud/media/thumbnails/products/saleor-digital-03_4_thumbnail_256.png",
|
||||
__typename: "Image",
|
||||
},
|
||||
productType: {
|
||||
id: "UHJvZHVjdFR5cGU6MTU=",
|
||||
name: "Audiobook",
|
||||
hasVariants: true,
|
||||
__typename: "ProductType",
|
||||
},
|
||||
channelListings: [
|
||||
{
|
||||
isPublished: true,
|
||||
publicationDate: "2020-01-01",
|
||||
isAvailableForPurchase: true,
|
||||
availableForPurchase: "2020-08-31",
|
||||
visibleInListings: false,
|
||||
channel: {
|
||||
id: "Q2hhbm5lbDox",
|
||||
name: "Default channel",
|
||||
currencyCode: "USD",
|
||||
__typename: "Channel",
|
||||
},
|
||||
__typename: "ProductChannelListing",
|
||||
pricing: {
|
||||
priceRange: {
|
||||
start: {
|
||||
net: {
|
||||
amount: 6.0,
|
||||
currency: "USD",
|
||||
__typename: "Money",
|
||||
},
|
||||
__typename: "TaxedMoney",
|
||||
},
|
||||
stop: {
|
||||
net: {
|
||||
amount: 6.0,
|
||||
currency: "USD",
|
||||
__typename: "Money",
|
||||
},
|
||||
__typename: "TaxedMoney",
|
||||
},
|
||||
__typename: "TaxedMoneyRange",
|
||||
},
|
||||
__typename: "ProductPricingInfo",
|
||||
},
|
||||
},
|
||||
],
|
||||
__typename: "Product",
|
||||
updatedAt: "2021-03-10T12:31:40.785454+00:00",
|
||||
attributes: [
|
||||
{
|
||||
attribute: {
|
||||
id: "QXR0cmlidXRlOjI2",
|
||||
__typename: "Attribute",
|
||||
},
|
||||
values: [
|
||||
{
|
||||
id: "QXR0cmlidXRlVmFsdWU6ODY=",
|
||||
name: "Digital Audio",
|
||||
slug: "digital-audio",
|
||||
file: null,
|
||||
reference: null,
|
||||
boolean: null,
|
||||
date: null,
|
||||
dateTime: null,
|
||||
value: "",
|
||||
__typename: "AttributeValue",
|
||||
},
|
||||
],
|
||||
__typename: "SelectedAttribute",
|
||||
},
|
||||
],
|
||||
},
|
||||
__typename: "ProductCountableEdge",
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
hasPreviousPage: false,
|
||||
hasNextPage: true,
|
||||
startCursor: "WyJhcHBsZS1qdWljZSJd",
|
||||
endCursor: "WyJiYXRocm9vbS1zb25ncyJd",
|
||||
__typename: "PageInfo",
|
||||
},
|
||||
totalCount: 41,
|
||||
__typename: "ProductCountableConnection",
|
||||
},
|
||||
},
|
||||
extensions: {
|
||||
cost: { requestedQueryCost: 13, maximumAvailable: 50000 },
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default productsMocks;
|
Loading…
Reference in a new issue