Add Webhooks list search and fix events any event

This commit is contained in:
Krzysztof Bialoglowicz 2019-10-15 12:06:19 +02:00
parent 28bc919e47
commit fd39ab1b85
19 changed files with 218 additions and 103 deletions

View file

@ -3327,7 +3327,7 @@ type ProductVariantUpdatePrivateMeta {
type Query {
webhook(id: ID!): Webhook
webhooks(before: String, after: String, first: Int, last: Int): WebhookCountableConnection
webhooks(filter: WebhookFilterInput, before: String, after: String, first: Int, last: Int): WebhookCountableConnection
translations(kind: TranslatableKinds!, before: String, after: String, first: Int, last: Int): TranslatableItemConnection
shop: Shop
shippingZone(id: ID!): ShippingZone
@ -4319,7 +4319,7 @@ type WebhookEvent {
}
enum WebhookEventTypeEnum {
ALL_EVENTS
ANY_EVENTS
ORDER_CREATED
ORDER_FULLY_PAID
ORDER_UPDATED
@ -4328,6 +4328,11 @@ enum WebhookEventTypeEnum {
PRODUCT_CREATED
}
input WebhookFilterInput {
search: String
isActive: Boolean
}
type WebhookUpdate {
errors: [Error!]
webhook: Webhook

View file

@ -273,7 +273,7 @@ export enum VoucherTypeEnum {
}
export enum WebhookEventTypeEnum {
ALL_EVENTS = "ALL_EVENTS",
ANY_EVENTS = "ANY_EVENTS",
CUSTOMER_CREATED = "CUSTOMER_CREATED",
ORDER_CANCELLED = "ORDER_CANCELLED",
ORDER_CREATED = "ORDER_CREATED",
@ -794,6 +794,11 @@ export interface WebhookCreateInput {
secretKey?: string | null;
}
export interface WebhookFilterInput {
search?: string | null;
isActive?: boolean | null;
}
export interface WebhookUpdateInput {
name?: string | null;
targetUrl?: string | null;

View file

@ -32,7 +32,7 @@ const WebhookEvents: React.StatelessComponent<WebhookEventsProps> = ({
target: {
name: "events",
value: event.target.value
? WebhookEventTypeEnum.ALL_EVENTS
? WebhookEventTypeEnum.ANY_EVENTS
: data.events
}
} as any)

View file

@ -62,7 +62,7 @@ const WebhooksDetailsPage: React.StatelessComponent<
const intl = useIntl();
const initialForm: FormData = {
allEvents: !!maybe(() => webhook.events, []).find(
event => event.eventType === WebhookEventTypeEnum.ALL_EVENTS
event => event.eventType === WebhookEventTypeEnum.ANY_EVENTS
),
events: maybe(() => webhook.events, []).map(event => event.eventType),
id: maybe(() => webhook.id, null),

View file

@ -1,33 +1,39 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import AppHeader from "@saleor/components/AppHeader";
import Container from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader";
import SearchBar from "@saleor/components/SearchBar";
import { sectionNames } from "@saleor/intl";
import { PageListProps } from "@saleor/types";
import { PageListProps, SearchPageProps, TabPageProps } from "@saleor/types";
import { Webhooks_webhooks_edges_node } from "../../types/Webhooks";
import WebhooksList from "../WebhooksList/WebhooksList";
export interface WebhooksListPageProps extends PageListProps {
export interface WebhooksListPageProps
extends PageListProps,
SearchPageProps,
TabPageProps {
webhooks: Webhooks_webhooks_edges_node[];
onBack: () => void;
onRemove: (id: string) => void;
}
const WebhooksListPage: React.StatelessComponent<WebhooksListPageProps> = ({
disabled,
settings,
currentTab,
initialSearch,
onAdd,
onAll,
onBack,
onNextPage,
onPreviousPage,
onRowClick,
onRemove,
onUpdateListSettings,
pageInfo,
webhooks
onSearchChange,
onTabChange,
onTabDelete,
onTabSave,
tabs,
webhooks,
...listProps
}) => {
const intl = useIntl();
return (
@ -43,17 +49,26 @@ const WebhooksListPage: React.StatelessComponent<WebhooksListPageProps> = ({
/>
</Button>
</PageHeader>
<WebhooksList
disabled={disabled}
settings={settings}
webhooks={webhooks}
onNextPage={onNextPage}
onPreviousPage={onPreviousPage}
onRemove={onRemove}
onUpdateListSettings={onUpdateListSettings}
onRowClick={onRowClick}
pageInfo={pageInfo}
/>
<Card>
<SearchBar
allTabLabel={intl.formatMessage({
defaultMessage: "All Webhooks",
description: "tab name"
})}
currentTab={currentTab}
initialSearch={initialSearch}
searchPlaceholder={intl.formatMessage({
defaultMessage: "Search Webhooks"
})}
tabs={tabs}
onAll={onAll}
onSearchChange={onSearchChange}
onTabChange={onTabChange}
onTabDelete={onTabDelete}
onTabSave={onTabSave}
/>
<WebhooksList webhooks={webhooks} {...listProps} />
</Card>
</Container>
);
};

View file

@ -19,31 +19,25 @@ export const services: ServiceList_serviceAccounts_edges_node[] = [
export const webhookList: Webhooks_webhooks_edges_node[] = [
{
__typename: "Webhook",
events: [],
id: "Jzx123sEt==",
isActive: true,
name: "Webhook Test",
secretKey: "dsdasdasd_asdas",
serviceAccount: {
__typename: "ServiceAccount",
id: "Jzx123sEt==",
name: "Test Account"
},
targetUrl: "http://www.getsaleor.com"
},
{
__typename: "Webhook",
events: [],
id: "Jzx123sEt==",
isActive: true,
name: "Webhook Test 2",
secretKey: "zxczx_asdas",
serviceAccount: {
__typename: "ServiceAccount",
id: "Jzx1ss23sEt==",
name: "Test Account 2"
},
targetUrl: "http://www.getsaleor.com"
}
];
export const webhook: Webhook_webhook = {

View file

@ -38,7 +38,7 @@ const Component = () => {
const intl = useIntl();
return (
<>
<WindowTitle title={intl.formatMessage(sectionNames.plugins)} />
<WindowTitle title={intl.formatMessage(sectionNames.webhooks)} />
<Switch>
<Route exact path={webhooksListPath} component={WebhookList} />
<Route exact path={webhooksAddUrl} component={WebhookCreate} />

View file

@ -8,12 +8,7 @@ export const webhooksFragment = gql`
fragment WebhookFragment on Webhook {
id
name
events {
eventType
}
isActive
secretKey
targetUrl
serviceAccount {
id
name
@ -30,8 +25,20 @@ export const webhooksDetailsFragment = gql`
const webhooksList = gql`
${webhooksFragment}
query Webhooks($first: Int, $after: String, $last: Int, $before: String) {
webhooks(before: $before, after: $after, first: $first, last: $last) {
query Webhooks(
$first: Int
$after: String
$last: Int
$before: String
$filter: WebhookFilterInput
) {
webhooks(
first: $first
after: $after
before: $before
last: $last
filter: $filter
) {
edges {
node {
...WebhookFragment
@ -55,6 +62,11 @@ const webhooksDetails = gql`
query WebhookDetails($id: ID!) {
webhook(id: $id) {
...WebhookFragment
events {
eventType
}
secretKey
targetUrl
}
}
`;

View file

@ -2,7 +2,7 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { WebhookCreateInput, WebhookEventTypeEnum } from "./../../types/globalTypes";
import { WebhookCreateInput } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: WebhookCreate
@ -14,11 +14,6 @@ export interface WebhookCreate_webhookCreate_errors {
message: string | null;
}
export interface WebhookCreate_webhookCreate_webhook_events {
__typename: "WebhookEvent";
eventType: WebhookEventTypeEnum | null;
}
export interface WebhookCreate_webhookCreate_webhook_serviceAccount {
__typename: "ServiceAccount";
id: string;
@ -29,10 +24,7 @@ export interface WebhookCreate_webhookCreate_webhook {
__typename: "Webhook";
id: string;
name: string | null;
events: (WebhookCreate_webhookCreate_webhook_events | null)[] | null;
isActive: boolean;
secretKey: string | null;
targetUrl: string;
serviceAccount: WebhookCreate_webhookCreate_webhook_serviceAccount;
}

View file

@ -8,26 +8,26 @@ import { WebhookEventTypeEnum } from "./../../types/globalTypes";
// GraphQL query operation: WebhookDetails
// ====================================================
export interface WebhookDetails_webhook_events {
__typename: "WebhookEvent";
eventType: WebhookEventTypeEnum | null;
}
export interface WebhookDetails_webhook_serviceAccount {
__typename: "ServiceAccount";
id: string;
name: string | null;
}
export interface WebhookDetails_webhook_events {
__typename: "WebhookEvent";
eventType: WebhookEventTypeEnum | null;
}
export interface WebhookDetails_webhook {
__typename: "Webhook";
id: string;
name: string | null;
events: (WebhookDetails_webhook_events | null)[] | null;
isActive: boolean;
serviceAccount: WebhookDetails_webhook_serviceAccount;
events: (WebhookDetails_webhook_events | null)[] | null;
secretKey: string | null;
targetUrl: string;
serviceAccount: WebhookDetails_webhook_serviceAccount;
}
export interface WebhookDetails {

View file

@ -2,17 +2,10 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { WebhookEventTypeEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: WebhookFragment
// ====================================================
export interface WebhookFragment_events {
__typename: "WebhookEvent";
eventType: WebhookEventTypeEnum | null;
}
export interface WebhookFragment_serviceAccount {
__typename: "ServiceAccount";
id: string;
@ -23,9 +16,6 @@ export interface WebhookFragment {
__typename: "Webhook";
id: string;
name: string | null;
events: (WebhookFragment_events | null)[] | null;
isActive: boolean;
secretKey: string | null;
targetUrl: string;
serviceAccount: WebhookFragment_serviceAccount;
}

View file

@ -2,7 +2,7 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { WebhookUpdateInput, WebhookEventTypeEnum } from "./../../types/globalTypes";
import { WebhookUpdateInput } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: WebhookUpdate
@ -14,11 +14,6 @@ export interface WebhookUpdate_webhookUpdate_errors {
message: string | null;
}
export interface WebhookUpdate_webhookUpdate_webhook_events {
__typename: "WebhookEvent";
eventType: WebhookEventTypeEnum | null;
}
export interface WebhookUpdate_webhookUpdate_webhook_serviceAccount {
__typename: "ServiceAccount";
id: string;
@ -29,10 +24,7 @@ export interface WebhookUpdate_webhookUpdate_webhook {
__typename: "Webhook";
id: string;
name: string | null;
events: (WebhookUpdate_webhookUpdate_webhook_events | null)[] | null;
isActive: boolean;
secretKey: string | null;
targetUrl: string;
serviceAccount: WebhookUpdate_webhookUpdate_webhook_serviceAccount;
}

View file

@ -2,17 +2,12 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { WebhookEventTypeEnum } from "./../../types/globalTypes";
import { WebhookFilterInput } from "./../../types/globalTypes";
// ====================================================
// GraphQL query operation: Webhooks
// ====================================================
export interface Webhooks_webhooks_edges_node_events {
__typename: "WebhookEvent";
eventType: WebhookEventTypeEnum | null;
}
export interface Webhooks_webhooks_edges_node_serviceAccount {
__typename: "ServiceAccount";
id: string;
@ -23,10 +18,7 @@ export interface Webhooks_webhooks_edges_node {
__typename: "Webhook";
id: string;
name: string | null;
events: (Webhooks_webhooks_edges_node_events | null)[] | null;
isActive: boolean;
secretKey: string | null;
targetUrl: string;
serviceAccount: Webhooks_webhooks_edges_node_serviceAccount;
}
@ -58,4 +50,5 @@ export interface WebhooksVariables {
after?: string | null;
last?: number | null;
before?: string | null;
filter?: WebhookFilterInput | null;
}

View file

@ -2,17 +2,10 @@
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { WebhookEventTypeEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: WebhooksDetailsFragment
// ====================================================
export interface WebhooksDetailsFragment_events {
__typename: "WebhookEvent";
eventType: WebhookEventTypeEnum | null;
}
export interface WebhooksDetailsFragment_serviceAccount {
__typename: "ServiceAccount";
id: string;
@ -23,9 +16,6 @@ export interface WebhooksDetailsFragment {
__typename: "Webhook";
id: string;
name: string | null;
events: (WebhooksDetailsFragment_events | null)[] | null;
isActive: boolean;
secretKey: string | null;
targetUrl: string;
serviceAccount: WebhooksDetailsFragment_serviceAccount;
}

View file

@ -1,13 +1,26 @@
import { stringify as stringifyQs } from "qs";
import urlJoin from "url-join";
import { Dialog, Pagination, SingleAction } from "../types";
import {
ActiveTab,
Dialog,
Filters,
Pagination,
SingleAction,
TabActionDialog
} from "../types";
export const webhooksSection = "/webhooks/";
export const webhooksListPath = webhooksSection;
export type WebhookListUrlDialog = "remove";
export type WebhooksListUrlQueryParams = Dialog<WebhookListUrlDialog> &
export enum WebhookListUrlFiltersEnum {
query = "query"
}
export type WebhookListUrlFilters = Filters<WebhookListUrlFiltersEnum>;
export type WebhookListUrlDialog = "remove" | TabActionDialog;
export type WebhooksListUrlQueryParams = ActiveTab &
WebhookListUrlFilters &
Dialog<WebhookListUrlDialog> &
Pagination &
SingleAction;
export const webhooksListUrl = (params?: WebhooksListUrlQueryParams) =>

View file

@ -50,7 +50,7 @@ export const WebhooksCreate: React.StatelessComponent<
variables: {
input: {
events: data.allEvents
? [WebhookEventTypeEnum.ALL_EVENTS]
? [WebhookEventTypeEnum.ANY_EVENTS]
: data.events,
isActive: data.isActive,
name: data.name,

View file

@ -127,7 +127,7 @@ export const WebhooksDetails: React.StatelessComponent<
id,
input: {
events: data.allEvents
? [WebhookEventTypeEnum.ALL_EVENTS]
? [WebhookEventTypeEnum.ANY_EVENTS]
: data.events,
isActive: data.isActive,
name: data.name,

View file

@ -1,3 +1,7 @@
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData
} from "@saleor/components/SaveFilterTabDialog";
import { configurationMenuUrl } from "@saleor/configuration";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
@ -16,12 +20,21 @@ import { useIntl } from "react-intl";
import WebhooksListPage from "../components/WebhooksListPage/WebhooksListPage";
import { TypedWebhookDelete } from "../mutations";
import { TypedWebhooksListQuery } from "../queries";
import {
import { WebhookListUrlDialog,
WebhookListUrlFilters,
webhooksAddUrl,
webhooksListUrl,
WebhooksListUrlQueryParams,
webhooksUrl
} from "../urls";
import {
areFiltersApplied,
deleteFilterTab,
getActiveFilters,
getFilterTabs,
getFilterVariables,
saveFilterTab
} from "./filter";
interface WebhooksListProps {
params: WebhooksListUrlQueryParams;
@ -37,8 +50,23 @@ export const WebhooksList: React.StatelessComponent<WebhooksListProps> = ({
const { updateListSettings, settings } = useListSettings(
ListViews.WEBHOOK_LIST
);
const paginationState = createPaginationState(settings.rowNumber, params);
const tabs = getFilterTabs();
const currentTab =
params.activeTab === undefined
? areFiltersApplied(params)
? tabs.length + 1
: 0
: parseInt(params.activeTab, 0);
const changeFilterField = (filter: WebhookListUrlFilters) =>
navigate(
webhooksListUrl({
...getActiveFilters(params),
...filter,
activeTab: undefined
})
);
const closeModal = () =>
navigate(
webhooksListUrl({
@ -49,8 +77,45 @@ export const WebhooksList: React.StatelessComponent<WebhooksListProps> = ({
true
);
const openModal = (action: WebhookListUrlDialog, id?: string) =>
navigate(
webhooksListUrl({
...params,
action,
id
})
);
const handleTabChange = (tab: number) => {
navigate(
webhooksListUrl({
activeTab: tab.toString(),
...getFilterTabs()[tab - 1].data
})
);
};
const handleTabDelete = () => {
deleteFilterTab(currentTab);
navigate(webhooksListUrl());
};
const handleTabSave = (data: SaveFilterTabDialogFormData) => {
saveFilterTab(data.name, getActiveFilters(params));
handleTabChange(tabs.length + 1);
};
const paginationState = createPaginationState(settings.rowNumber, params);
const queryVariables = React.useMemo(
() => ({
...paginationState,
filter: getFilterVariables(params)
}),
[params]
);
return (
<TypedWebhooksListQuery displayLoader variables={paginationState}>
<TypedWebhooksListQuery displayLoader variables={queryVariables}>
{({ data, loading, refetch }) => {
const onWebhookDelete = (data: WebhookDelete) => {
if (data.webhookDelete.errors.length === 0) {
@ -95,6 +160,14 @@ export const WebhooksList: React.StatelessComponent<WebhooksListProps> = ({
return (
<>
<WebhooksListPage
currentTab={currentTab}
initialSearch={params.query || ""}
onSearchChange={query => changeFilterField({ query })}
onAll={() => navigate(webhooksListUrl())}
onTabChange={handleTabChange}
onTabDelete={() => openModal("delete-search")}
onTabSave={() => openModal("save-search")}
tabs={tabs.map(tab => tab.name)}
disabled={loading}
settings={settings}
webhooks={maybe(() =>
@ -122,6 +195,19 @@ export const WebhooksList: React.StatelessComponent<WebhooksListProps> = ({
onConfirm={handleRemoveConfirm}
open={params.action === "remove"}
/>
<SaveFilterTabDialog
open={params.action === "save-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleTabSave}
/>
<DeleteFilterTabDialog
open={params.action === "delete-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleTabDelete}
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
/>
</>
);
}}

View file

@ -0,0 +1,28 @@
import { WebhookFilterInput } from "@saleor/types/globalTypes";
import { createFilterTabUtils, createFilterUtils } from "../../utils/filters";
import {
WebhookListUrlFilters,
WebhookListUrlFiltersEnum,
WebhooksListUrlQueryParams
} from "../urls";
export const WEBHOOK_FILTERS_KEY = "webhookFilters";
export function getFilterVariables(
params: WebhookListUrlFilters
): WebhookFilterInput {
return {
search: params.query
};
}
export const {
deleteFilterTab,
getFilterTabs,
saveFilterTab
} = createFilterTabUtils<WebhookListUrlFilters>(WEBHOOK_FILTERS_KEY);
export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
WebhooksListUrlQueryParams,
WebhookListUrlFilters
>(WebhookListUrlFiltersEnum);