Allow subscription query for sync events (#3099)

Allow subscription query for sync events
This commit is contained in:
Bartłomiej Wiaduch 2023-02-23 10:03:41 +01:00 committed by GitHub
parent 9f54a7840c
commit 0fef41b04f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 250 additions and 157 deletions

View file

@ -21,6 +21,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Add redirect to GraphiQL from product & order details pages - #2940 by @zaiste - Add redirect to GraphiQL from product & order details pages - #2940 by @zaiste
- Extract permissions for subscription query - #3155 by @zaiste - Extract permissions for subscription query - #3155 by @zaiste
- Add custom request headers to webhook form - #3107 by @2can - Add custom request headers to webhook form - #3107 by @2can
- Allow subscription query for sync events - #3099 by @2can
## 3.4 ## 3.4

View file

@ -1618,10 +1618,6 @@
"context": "sale status", "context": "sale status",
"string": "Active" "string": "Active"
}, },
"ApNw0L": {
"context": "Dry run objects unavailable",
"string": "The following objects are currently not available for dry run:"
},
"AqHafs": { "AqHafs": {
"context": "provided email input placeholder", "context": "provided email input placeholder",
"string": "Provided email address" "string": "Provided email address"
@ -1731,6 +1727,10 @@
"context": "button label", "context": "button label",
"string": "Settings" "string": "Settings"
}, },
"BYTvv/": {
"context": "Dry run events unavailable",
"string": "The following events from provided query are currently not available for dry run:"
},
"BZ7BkQ": { "BZ7BkQ": {
"context": "capture payment, button", "context": "capture payment, button",
"string": "Capture" "string": "Capture"
@ -4187,6 +4187,10 @@
"context": "pricing card title", "context": "pricing card title",
"string": "Pricing" "string": "Pricing"
}, },
"TnnDjx": {
"context": "Dry run sync events alert",
"string": "Dry run currently is not available for synchronous events"
},
"TnyLrZ": { "TnyLrZ": {
"context": "PageTypeDeleteWarningDialog with items multiple description", "context": "PageTypeDeleteWarningDialog with items multiple description",
"string": "You are about to delete multiple page types. Some of them are assigned to pages. Deleting those page types will also delete those pages" "string": "You are about to delete multiple page types. Some of them are assigned to pages. Deleting those page types will also delete those pages"

View file

@ -1,5 +1,5 @@
import { MockedProvider, MockedResponse } from "@apollo/client/testing"; import { MockedProvider, MockedResponse } from "@apollo/client/testing";
import { WebhookEventTypeAsyncEnum } from "@dashboard/graphql"; import { WebhookEventTypeSyncEnum } from "@dashboard/graphql";
import { ThemeProvider } from "@saleor/macaw-ui"; import { ThemeProvider } from "@saleor/macaw-ui";
import productsMocks from "@test/mocks/products"; import productsMocks from "@test/mocks/products";
import { render, screen } from "@testing-library/react"; import { render, screen } from "@testing-library/react";
@ -23,8 +23,8 @@ describe("DryRun", () => {
query: "", query: "",
showDialog: true, showDialog: true,
setShowDialog: jest.fn(), setShowDialog: jest.fn(),
asyncEvents: [] as WebhookEventTypeAsyncEnum[],
setResult: jest.fn(), setResult: jest.fn(),
syncEvents: [] as WebhookEventTypeSyncEnum[],
}; };
// Act // Act

View file

@ -2,7 +2,7 @@ import Grid from "@dashboard/components/Grid";
import { useStyles } from "@dashboard/custom-apps/components/WebhookEvents/styles"; import { useStyles } from "@dashboard/custom-apps/components/WebhookEvents/styles";
import { import {
useTriggerWebhookDryRunMutation, useTriggerWebhookDryRunMutation,
WebhookEventTypeAsyncEnum, WebhookEventTypeSyncEnum,
} from "@dashboard/graphql"; } from "@dashboard/graphql";
import { import {
capitalize, capitalize,
@ -26,30 +26,33 @@ import React, { Dispatch, SetStateAction, useState } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import DryRunItemsList from "../DryRunItemsList/DryRunItemsList"; import DryRunItemsList from "../DryRunItemsList/DryRunItemsList";
import { DocumentMap } from "../DryRunItemsList/utils";
import { messages } from "./messages"; import { messages } from "./messages";
import { getObjects } from "./utils"; import { getUnavailableObjects } from "./utils";
interface DryRunProps { interface DryRunProps {
query: string; query: string;
showDialog: boolean; showDialog: boolean;
setShowDialog: Dispatch<SetStateAction<boolean>>; setShowDialog: Dispatch<SetStateAction<boolean>>;
asyncEvents: WebhookEventTypeAsyncEnum[];
setResult: Dispatch<SetStateAction<string>>; setResult: Dispatch<SetStateAction<string>>;
syncEvents: WebhookEventTypeSyncEnum[];
} }
export const DryRun: React.FC<DryRunProps> = ({ const DryRun: React.FC<DryRunProps> = ({
setResult, setResult,
showDialog, showDialog,
setShowDialog, setShowDialog,
query, query,
syncEvents,
}: DryRunProps) => { }: DryRunProps) => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles(); const classes = useStyles();
const [objectId, setObjectId] = useState<string | null>(null); const [objectId, setObjectId] = useState<string | null>(null);
const [triggerWebhookDryRun] = useTriggerWebhookDryRunMutation(); const [triggerWebhookDryRun] = useTriggerWebhookDryRunMutation();
const availableObjects = getObjects(query); const availableObjects = Object.keys(DocumentMap).map(object =>
const unavailableObjects = getObjects(query, false); capitalize(object.split("_").join(" ").toLowerCase()),
);
const unavailableObjects = getUnavailableObjects(query);
const [object, setObject] = useState<string | null>(null); const [object, setObject] = useState<string | null>(null);
const dryRun = async () => { const dryRun = async () => {
@ -71,6 +74,23 @@ export const DryRun: React.FC<DryRunProps> = ({
return <></>; return <></>;
} }
if (syncEvents.length > 0) {
return (
<Dialog open={showDialog} fullWidth maxWidth="md" data-test-id="dry-run">
<DialogHeader onClose={closeDialog}>
{intl.formatMessage(messages.header)}
</DialogHeader>
<DialogContent style={{ overflow: "scroll" }}>
<Alert variant="error" close={false}>
<Typography>
{intl.formatMessage(messages.unavailableSyncEvents)}
</Typography>
</Alert>
</DialogContent>
</Dialog>
);
}
return ( return (
<Dialog open={showDialog} fullWidth maxWidth="md" data-test-id="dry-run"> <Dialog open={showDialog} fullWidth maxWidth="md" data-test-id="dry-run">
<DialogHeader onClose={closeDialog}> <DialogHeader onClose={closeDialog}>
@ -84,8 +104,8 @@ export const DryRun: React.FC<DryRunProps> = ({
{!!unavailableObjects.length && ( {!!unavailableObjects.length && (
<Alert variant="warning" close={false}> <Alert variant="warning" close={false}>
<Typography> <Typography>
{intl.formatMessage(messages.unavailableObjects)} {intl.formatMessage(messages.unavailableEvents)}
&nbsp; <br />
<strong>{unavailableObjects.join(", ")}</strong> <strong>{unavailableObjects.join(", ")}</strong>
</Typography> </Typography>
</Alert> </Alert>
@ -158,7 +178,7 @@ export const DryRun: React.FC<DryRunProps> = ({
color="primary" color="primary"
variant="primary" variant="primary"
onClick={dryRun} onClick={dryRun}
disabled={!availableObjects.length} disabled={!object}
> >
{intl.formatMessage(messages.run)} {intl.formatMessage(messages.run)}
</Button> </Button>

View file

@ -6,16 +6,21 @@ export const messages = defineMessages({
defaultMessage: "Dry run", defaultMessage: "Dry run",
description: "Dry run dialog header", description: "Dry run dialog header",
}, },
unavailableSyncEvents: {
id: "TnnDjx",
defaultMessage: "Dry run currently is not available for synchronous events",
description: "Dry run sync events alert",
},
selectObject: { selectObject: {
id: "+RffqY", id: "+RffqY",
defaultMessage: "Select object type to perform dry run on provided query", defaultMessage: "Select object type to perform dry run on provided query",
description: "Dry run dialog object title", description: "Dry run dialog object title",
}, },
unavailableObjects: { unavailableEvents: {
id: "ApNw0L", id: "BYTvv/",
defaultMessage: defaultMessage:
"The following objects are currently not available for dry run:", "The following events from provided query are currently not available for dry run:",
description: "Dry run objects unavailable", description: "Dry run events unavailable",
}, },
objects: { objects: {
id: "uccjUM", id: "uccjUM",

View file

@ -0,0 +1,24 @@
import { getUnavailableObjects } from "./utils";
describe("getUnavailableObjects", () => {
it("should return unavailable for dry run events from provided query", () => {
const query = `
subscription {
event {
... on ProductUpdated {
__typename
}
... on ProductDeleted {
__typename
}
... on AddressUpdated {
__typename
}
}
}`;
const events = getUnavailableObjects(query);
expect(events).toEqual(["AddressUpdated"]);
});
});

View file

@ -1,8 +1,8 @@
import { AsyncWebhookTypes } from "@dashboard/custom-apps/components/WebhookEvents"; import { getWebhookTypes } from "@dashboard/custom-apps/components/WebhookEvents/utils";
import { WebhookEventTypeAsyncEnum } from "@dashboard/graphql";
import { InlineFragmentNode, ObjectFieldNode, parse, visit } from "graphql"; import { InlineFragmentNode, ObjectFieldNode, parse, visit } from "graphql";
import uniq from "lodash/uniq";
import { ExcludedDocumentMap } from "../DryRunItemsList/utils"; import { DocumentMap, ExcludedDocumentMap } from "../DryRunItemsList/utils";
const getEventsFromQuery = (query: string) => { const getEventsFromQuery = (query: string) => {
if (query.length === 0) { if (query.length === 0) {
@ -32,28 +32,32 @@ const getEventsFromQuery = (query: string) => {
} }
}; };
export const getObjects = (query: string, available = true) => { export const getUnavailableObjects = (query: string) => {
const queryEvents = getEventsFromQuery(query); const queryEvents = getEventsFromQuery(query);
return uniq( return queryEvents.reduce((acc, event) => {
queryEvents.map(event => { const formattedEvent = event
const object = event.split(/(?=[A-Z])/).slice(0, -1); .split(/(?=[A-Z])/)
if ( .join("_")
Object.keys(AsyncWebhookTypes) .toUpperCase();
.filter(object => if (checkEventPresence(formattedEvent)) {
available acc.push(event);
? !Object.keys(ExcludedDocumentMap).includes(object.toUpperCase()) }
: Object.keys(ExcludedDocumentMap).includes(object.toUpperCase()),
)
.includes(object.join("_").toUpperCase())
) {
return object.join(" ");
}
return event return acc;
.split(/(?=[A-Z])/) }, []);
.slice(0, -2) };
.join(" ");
}), const checkEventPresence = (event: string) => {
).filter(object => object.length > 0); const webhookTypes = getWebhookTypes(Object.keys(WebhookEventTypeAsyncEnum));
const availableObjects = Object.keys(DocumentMap);
const excludedObjects = Object.keys(webhookTypes).filter(
object => !availableObjects.includes(object),
);
Object.keys(ExcludedDocumentMap).forEach(
object => !excludedObjects.includes(object) && excludedObjects.push(object),
);
return excludedObjects.some(object => event.startsWith(object));
}; };

View file

@ -18,17 +18,21 @@ import { useIntl } from "react-intl";
import Avatar from "../TableCellAvatar/Avatar"; import Avatar from "../TableCellAvatar/Avatar";
import { messages } from "./messages"; import { messages } from "./messages";
import { DocumentMap, TData, TVariables } from "./utils"; import { DocumentMap, TData, TVariables } from "./utils";
interface DryRunItemsListProps {
export interface DryRunItemsListProps {
objectId: string; objectId: string;
setObjectId: React.Dispatch<any>; setObjectId: React.Dispatch<any>;
object: string; object: string;
} }
const DryRunItemsList = (props: DryRunItemsListProps) => { const DryRunItemsList: React.FC<DryRunItemsListProps> = ({
object,
objectId,
setObjectId,
}: DryRunItemsListProps) => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles(); const classes = useStyles();
const { checkbox } = useListWidths(); const { checkbox } = useListWidths();
const { object, objectId, setObjectId } = props;
const objectDocument = DocumentMap[object]; const objectDocument = DocumentMap[object];
const objectCollection = const objectCollection =
objectDocument.collection ?? camelCase(`${object.toLowerCase()}s`); objectDocument.collection ?? camelCase(`${object.toLowerCase()}s`);

View file

@ -1,4 +1,4 @@
import { WebhookEventTypeAsyncEnum } from "@dashboard/graphql"; import { WebhookFormData } from "@dashboard/custom-apps/components/WebhookDetailsPage";
import { import {
CopyIcon, CopyIcon,
GraphiQLProvider, GraphiQLProvider,
@ -78,7 +78,7 @@ export function GraphiQL({
visiblePlugin, visiblePlugin,
defaultHeaders, defaultHeaders,
...props ...props
}: GraphiQLProps & { asyncEvents: WebhookEventTypeAsyncEnum[] }) { }: GraphiQLProps & { data: WebhookFormData }) {
// Ensure props are correct // Ensure props are correct
if (typeof fetcher !== "function") { if (typeof fetcher !== "function") {
throw new TypeError( throw new TypeError(
@ -130,7 +130,7 @@ export function GraphiQL({
setShowDialog={setShowDialog} setShowDialog={setShowDialog}
query={query} query={query}
setResult={setResult} setResult={setResult}
asyncEvents={props.asyncEvents} syncEvents={props.data.syncEvents}
/> />
</GraphiQLProvider> </GraphiQLProvider>
); );

View file

@ -96,17 +96,18 @@ const WebhookDetailsPage: React.FC<WebhookDetailsPageProps> = ({
return ( return (
<Form confirmLeave initial={initialForm} onSubmit={handleSubmit}> <Form confirmLeave initial={initialForm} onSubmit={handleSubmit}>
{({ data, submit, change }) => { {({ data, submit, change }) => {
const handleSyncEventsSelect = createSyncEventsSelectHandler( const handleSyncEventsSelect = createSyncEventsSelectHandler({
change, change,
data.syncEvents, data,
setQuery,
);
const handleAsyncEventsSelect = createAsyncEventsSelectHandler(
change,
data.asyncEvents,
query, query,
setQuery, setQuery,
); });
const handleAsyncEventsSelect = createAsyncEventsSelectHandler({
change,
data,
query,
setQuery,
});
return ( return (
<DetailedContent useSingleColumn> <DetailedContent useSingleColumn>
@ -127,6 +128,7 @@ const WebhookDetailsPage: React.FC<WebhookDetailsPageProps> = ({
<FormSpacer /> <FormSpacer />
<WebhookEvents <WebhookEvents
data={data} data={data}
setQuery={setQuery}
onSyncEventChange={handleSyncEventsSelect} onSyncEventChange={handleSyncEventsSelect}
onAsyncEventChange={handleAsyncEventsSelect} onAsyncEventChange={handleAsyncEventsSelect}
/> />

View file

@ -19,23 +19,26 @@ import {
Pill, Pill,
useListWidths, useListWidths,
} from "@saleor/macaw-ui"; } from "@saleor/macaw-ui";
import React, { useState } from "react"; import React, { Dispatch, SetStateAction, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { messages } from "./messages"; import { messages } from "./messages";
import { useStyles } from "./styles"; import { useStyles } from "./styles";
import { EventTypes, getEventName } from "./utils";
interface WebhookEventsProps { interface WebhookEventsProps {
data: { data: {
syncEvents: WebhookEventTypeSyncEnum[]; syncEvents: WebhookEventTypeSyncEnum[];
asyncEvents: WebhookEventTypeAsyncEnum[]; asyncEvents: WebhookEventTypeAsyncEnum[];
}; };
setQuery: Dispatch<SetStateAction<string>>;
onSyncEventChange: (event: ChangeEvent) => void; onSyncEventChange: (event: ChangeEvent) => void;
onAsyncEventChange: (event: ChangeEvent) => void; onAsyncEventChange: (event: ChangeEvent) => void;
} }
const WebhookEvents: React.FC<WebhookEventsProps> = ({ const WebhookEvents: React.FC<WebhookEventsProps> = ({
data, data,
setQuery,
onSyncEventChange, onSyncEventChange,
onAsyncEventChange, onAsyncEventChange,
}) => { }) => {
@ -56,14 +59,19 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
const handleTabChange = value => { const handleTabChange = value => {
setObject(null); setObject(null);
setQuery("");
setTab(value); setTab(value);
}; };
const countEvents = object => { const countEvents = object => {
const selected = tab === "sync" ? data.syncEvents : data.asyncEvents; const selected = tab === "sync" ? data.syncEvents : data.asyncEvents;
const objectEvents = EventTypes[tab][object].map( const objectEvents = EventTypes[tab][object].map(event => {
event => `${object}_${event}`, if (event === object) {
); return object;
}
return `${object}_${event}`;
});
return objectEvents.filter((event: never) => selected.includes(event)) return objectEvents.filter((event: never) => selected.includes(event))
.length; .length;
@ -177,62 +185,3 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
}; };
WebhookEvents.displayName = "WebhookEvents"; WebhookEvents.displayName = "WebhookEvents";
export default WebhookEvents; export default WebhookEvents;
type Actions = string[];
export const AsyncWebhookTypes: Record<string, Actions> = {
ADDRESS: ["CREATED", "UPDATED", "DELETED"],
APP: ["INSTALLED", "UPDATED", "DELETED"],
ATTRIBUTE: ["CREATED", "UPDATED", "DELETED"],
CATEGORY: ["CREATED", "UPDATED", "DELETED"],
CHANNEL: ["CREATED", "UPDATED", "DELETED"],
GIFT_CARD: ["CREATED", "UPDATED", "DELETED", "STATUS_CHANGED"],
CHECKOUT: ["CREATED", "UPDATED", "DELETED"],
COLLECTION: ["CREATED", "UPDATED", "DELETED"],
CUSTOMER: ["CREATED", "UPDATED", "DELETED"],
FULFILLMENT: ["CREATED"],
INVOICE: ["DELETED", "REQUESTED", "SENT"],
MENU: ["CREATED", "UPDATED", "DELETED"],
ORDER: [
"CANCELLED",
"CONFIRMED",
"CREATED",
"FULFILLED",
"FULLY_PAID",
"UPDATED",
],
PAGE: ["CREATED", "UPDATED", "DELETED"],
PRODUCT: ["CREATED", "UPDATED", "DELETED"],
PRODUCT_VARIANT: ["CREATED", "UPDATED", "DELETED"],
SALE: ["CREATED", "UPDATED", "DELETED", "TOGGLE"],
SHIPPING_PRICE: ["CREATED", "UPDATED", "DELETED"],
SHIPPING_ZONE: ["CREATED", "UPDATED", "DELETED"],
STAFF: ["CREATED", "UPDATED", "DELETED"],
TRANSLATION: ["ACTION_REQUEST", "CREATED", "UPDATED"],
VOUCHER: ["CREATED", "UPDATED", "DELETED"],
WAREHOUSE: ["CREATED", "UPDATED", "DELETED"],
};
const SyncWebhookTypes: Record<string, Actions> = {
PAYMENT: [
"AUTHORIZE",
"CAPTURE",
"CONFIRM",
"LIST_GATEWAYS",
"PROCESS",
"REFUND",
"VOID",
],
CHECKOUT: ["CALCULATE_TAXES", "FILTER_SHIPPING_METHODS"],
ORDER: ["CALCULATE_TAXES", "FILTER_SHIPPING_METHODS"],
SHIPPING: ["LIST_METHODS_FOR_CHECKOUT"],
};
const EventTypes = {
async: AsyncWebhookTypes,
sync: SyncWebhookTypes,
};
const getEventName = (object: string, event: string) =>
[object, event].join("_").toUpperCase() as WebhookEventTypeSyncEnum;

View file

@ -0,0 +1,23 @@
import { getWebhookTypes } from "./utils";
const TestKeys = [
"DRAFT_ORDER_CREATED",
"DRAFT_ORDER_UPDATED",
"PRODUCT_CREATED",
"PRODUCT_UPDATED",
"PRODUCT_VARIANT_UPDATED",
];
describe("getWebhookTypes", () => {
it("should map array of enum keys to objects with events ", () => {
const TestWebhookTypes = getWebhookTypes(TestKeys);
expect(Object.keys(TestWebhookTypes)).toEqual(["DRAFT_ORDER", "PRODUCT"]);
expect(TestWebhookTypes.DRAFT_ORDER).toEqual(["CREATED", "UPDATED"]);
expect(TestWebhookTypes.PRODUCT).toEqual([
"CREATED",
"UPDATED",
"VARIANT_UPDATED",
]);
});
});

View file

@ -0,0 +1,46 @@
import {
WebhookEventTypeAsyncEnum,
WebhookEventTypeSyncEnum,
} from "@dashboard/graphql";
type Actions = string[];
export const getWebhookTypes = (webhookEvents: string[]) => {
const multiWords = ["DRAFT_ORDER", "GIFT_CARD", "ANY_EVENTS"];
return webhookEvents.reduce<Record<string, Actions>>((acc, key) => {
const keywords = key.split("_");
const multiKeyword = keywords.slice(0, 2).join("_");
const [keyword, sliceSize] = multiWords.includes(multiKeyword)
? [multiKeyword, 2]
: [keywords[0], 1];
const event = keywords.slice(sliceSize).join("_");
const events = acc[keyword] || [];
events.push(!!event.length ? event : multiKeyword);
acc[keyword] = events;
return acc;
}, {});
};
export const AsyncWebhookTypes: Record<string, Actions> = getWebhookTypes(
Object.keys(WebhookEventTypeAsyncEnum),
);
const SyncWebhookTypes: Record<string, Actions> = getWebhookTypes(
Object.keys(WebhookEventTypeSyncEnum),
);
export const EventTypes = {
async: AsyncWebhookTypes,
sync: SyncWebhookTypes,
};
export const getEventName = (object: string, event: string) => {
if (object === event) {
return object.toUpperCase() as WebhookEventTypeSyncEnum;
}
return [object, event].join("_").toUpperCase() as WebhookEventTypeSyncEnum;
};

View file

@ -4,7 +4,6 @@ import CardTitle from "@dashboard/components/CardTitle";
import { useExplorerPlugin } from "@graphiql/plugin-explorer"; import { useExplorerPlugin } from "@graphiql/plugin-explorer";
import { createGraphiQLFetcher } from "@graphiql/toolkit"; import { createGraphiQLFetcher } from "@graphiql/toolkit";
import { Card, CardContent } from "@material-ui/core"; import { Card, CardContent } from "@material-ui/core";
import clsx from "clsx";
import React from "react"; import React from "react";
import { defineMessages, useIntl } from "react-intl"; import { defineMessages, useIntl } from "react-intl";
@ -45,13 +44,8 @@ const WebhookSubscriptionQuery: React.FC<WebhookSubscriptionQueryProps> = ({
const classes = useStyles(); const classes = useStyles();
return ( return (
<Card <Card className={classes.card}>
className={clsx(classes.card, data.syncEvents.length && classes.disabled)} <CardTitle title={intl.formatMessage(messages.title)} />
>
<CardTitle
title={intl.formatMessage(messages.title)}
className={classes.cardTitle}
/>
<CardContent className={classes.cardContent}> <CardContent className={classes.cardContent}>
<GraphiQL <GraphiQL
data-test-id="graphiql-webhook" data-test-id="graphiql-webhook"
@ -62,7 +56,7 @@ const WebhookSubscriptionQuery: React.FC<WebhookSubscriptionQueryProps> = ({
onEditQuery={setQuery} onEditQuery={setQuery}
plugins={[explorerPlugin]} plugins={[explorerPlugin]}
isHeadersEditorEnabled={false} isHeadersEditorEnabled={false}
asyncEvents={data.asyncEvents} data={data}
/> />
</CardContent> </CardContent>
</Card> </Card>

View file

@ -12,28 +12,36 @@ import {
print, print,
visit, visit,
} from "graphql"; } from "graphql";
import isEmpty from "lodash/isEmpty";
import React, { Dispatch, SetStateAction } from "react";
import { WebhookFormData } from "./components/WebhookDetailsPage";
import { filterSelectedAsyncEvents } from "./utils"; import { filterSelectedAsyncEvents } from "./utils";
interface CreateSyncEventsSelectHandler {
change: (event: ChangeEvent, cb?: () => void) => void;
data: WebhookFormData;
query: string;
setQuery: Dispatch<SetStateAction<string>>;
}
export const createSyncEventsSelectHandler = export const createSyncEventsSelectHandler =
( ({ change, data, query, setQuery }: CreateSyncEventsSelectHandler) =>
change: (event: ChangeEvent, cb?: () => void) => void,
syncEvents: WebhookEventTypeSyncEnum[],
setQuery: React.Dispatch<React.SetStateAction<string>>,
) =>
(event: ChangeEvent) => { (event: ChangeEvent) => {
const { syncEvents, asyncEvents } = data;
const events = toggle(event.target.value, syncEvents, (a, b) => a === b); const events = toggle(event.target.value, syncEvents, (a, b) => a === b);
// Clear query
setQuery("");
// Clear asyncEvents // Clear asyncEvents
change({ if (!isEmpty(asyncEvents)) {
target: { setQuery("");
name: "asyncEvents",
value: [], change({
}, target: {
}); name: "asyncEvents",
value: [],
},
});
}
change({ change({
target: { target: {
@ -41,26 +49,35 @@ export const createSyncEventsSelectHandler =
value: events, value: events,
}, },
}); });
handleQuery(events, query, setQuery);
}; };
interface CreateAsyncEventsSelectHandler {
change: (event: ChangeEvent, cb?: () => void) => void;
data: WebhookFormData;
query: string;
setQuery: Dispatch<SetStateAction<string>>;
}
export const createAsyncEventsSelectHandler = export const createAsyncEventsSelectHandler =
( ({ change, data, query, setQuery }: CreateAsyncEventsSelectHandler) =>
change: (event: ChangeEvent, cb?: () => void) => void,
asyncEvents: WebhookEventTypeAsyncEnum[],
query: string,
setQuery: React.Dispatch<React.SetStateAction<string>>,
) =>
(event: ChangeEvent) => { (event: ChangeEvent) => {
const { syncEvents, asyncEvents } = data;
const events = toggle(event.target.value, asyncEvents, (a, b) => a === b); const events = toggle(event.target.value, asyncEvents, (a, b) => a === b);
const filteredEvents = filterSelectedAsyncEvents(events); const filteredEvents = filterSelectedAsyncEvents(events);
// Clear syncEvents // Clear syncEvents
change({ if (!isEmpty(syncEvents)) {
target: { setQuery("");
name: "syncEvents",
value: [], change({
}, target: {
}); name: "syncEvents",
value: [],
},
});
}
change({ change({
target: { target: {
@ -73,7 +90,7 @@ export const createAsyncEventsSelectHandler =
}; };
const handleQuery = ( const handleQuery = (
events: WebhookEventTypeAsyncEnum[], events: WebhookEventTypeAsyncEnum[] | WebhookEventTypeSyncEnum[],
query: string, query: string,
setQuery: React.Dispatch<React.SetStateAction<string>>, setQuery: React.Dispatch<React.SetStateAction<string>>,
) => { ) => {