import {ValidationErrorReason, ValidationInvalid} from '../utils/types'; import {AdapterArgs} from '../../runtime/types'; import {Session} from '../session/session'; import {ShopifyEventsHeader, ShopifyHeader} from '../types'; export const WebhookType = { Webhooks: 'webhooks', Events: 'events', } as const; export type WebhookTypeValue = (typeof WebhookType)[keyof typeof WebhookType]; export const WEBHOOK_HEADER_NAMES = { [WebhookType.Webhooks]: { hmac: ShopifyHeader.Hmac, topic: ShopifyHeader.Topic, domain: ShopifyHeader.Domain, apiVersion: ShopifyHeader.ApiVersion, webhookId: ShopifyHeader.WebhookId, subTopic: ShopifyHeader.SubTopic, name: ShopifyHeader.Name, triggeredAt: ShopifyHeader.TriggeredAt, eventId: ShopifyHeader.EventId, }, [WebhookType.Events]: { hmac: ShopifyEventsHeader.Hmac, topic: ShopifyEventsHeader.Topic, domain: ShopifyEventsHeader.Domain, apiVersion: ShopifyEventsHeader.ApiVersion, eventId: ShopifyEventsHeader.EventId, handle: ShopifyEventsHeader.Handle, action: ShopifyEventsHeader.Action, resourceId: ShopifyEventsHeader.ResourceId, triggeredAt: ShopifyEventsHeader.TriggeredAt, }, } as const; export enum DeliveryMethod { Http = 'http', EventBridge = 'eventbridge', PubSub = 'pubsub', } export type WebhookHandlerFunction = ( topic: string, shop_domain: string, body: string, webhookId: string, apiVersion?: string, subTopic?: string, context?: any, ) => Promise; interface BaseWebhookHandler { id?: string; includeFields?: string[]; metafieldNamespaces?: string[]; subTopic?: string; context?: any; } export interface HttpWebhookHandler extends BaseWebhookHandler { deliveryMethod: DeliveryMethod.Http; callbackUrl: string; } export interface HttpWebhookHandlerWithCallback extends HttpWebhookHandler { callback: WebhookHandlerFunction; } export interface EventBridgeWebhookHandler extends BaseWebhookHandler { deliveryMethod: DeliveryMethod.EventBridge; arn: string; } export interface PubSubWebhookHandler extends BaseWebhookHandler { deliveryMethod: DeliveryMethod.PubSub; pubSubProject: string; pubSubTopic: string; } export type WebhookHandler = | HttpWebhookHandler | HttpWebhookHandlerWithCallback | EventBridgeWebhookHandler | PubSubWebhookHandler; // See https://shopify.dev/docs/api/admin-graphql/latest/enums/webhooksubscriptiontopic for available topics export type WebhookRegistry = Record; // eslint-disable-next-line no-warning-comments // TODO Rethink the wording for this enum - the operations we're doing are actually "subscribing" and "unsubscribing" // Consider changing the values when releasing v12.0.0 when it can be safely deprecated export enum WebhookOperation { Create = 'create', Update = 'update', Delete = 'delete', } export interface RegisterParams { session: Session; } export interface RegisterResult { success: boolean; deliveryMethod: DeliveryMethod; result: unknown; operation: WebhookOperation; } export type RegisterReturn = Record; interface WebhookHttpEndpoint { __typename: 'WebhookHttpEndpoint'; callbackUrl: string; } interface WebhookEventBridgeEndpoint { __typename: 'WebhookEventBridgeEndpoint'; arn: string; } interface WebhookPubSubEndpoint { __typename: 'WebhookPubSubEndpoint'; pubSubProject: string; pubSubTopic: string; } type WebhookEndpoint = | WebhookHttpEndpoint | WebhookEventBridgeEndpoint | WebhookPubSubEndpoint; export interface WebhookCheckResponseNode< T = { endpoint: WebhookEndpoint; }, > { node: { id: string; topic: string; includeFields: string[]; metafieldNamespaces: string[]; } & T; } export interface WebhookCheckResponse { webhookSubscriptions: { edges: T[]; pageInfo: { endCursor: string; hasNextPage: boolean; }; }; } export type AddHandlersParams = Record< string, WebhookHandler | WebhookHandler[] >; export interface WebhookProcessParams extends AdapterArgs { rawBody: string; context?: any; } export interface WebhookValidateParams extends WebhookProcessParams {} export const WebhookValidationErrorReason = { ...ValidationErrorReason, MissingHeaders: 'missing_headers', } as const; export type WebhookValidationErrorReasonType = (typeof WebhookValidationErrorReason)[keyof typeof WebhookValidationErrorReason]; interface BaseWebhookFields { apiVersion: string; domain: string; hmac: string; topic: string; triggeredAt?: string; } export interface WebhooksWebhookFields extends BaseWebhookFields { webhookType: typeof WebhookType.Webhooks; webhookId: string; subTopic?: string; name?: string; eventId?: string; } export interface EventsWebhookFields extends BaseWebhookFields { webhookType: typeof WebhookType.Events; eventId: string; handle?: string; action?: string; resourceId?: string; } export type WebhookFields = WebhooksWebhookFields | EventsWebhookFields; export interface WebhookValidationInvalid extends Omit< ValidationInvalid, 'reason' > { valid: false; reason: WebhookValidationErrorReasonType; } export interface WebhookValidationMissingHeaders extends WebhookValidationInvalid { reason: typeof WebhookValidationErrorReason.MissingHeaders; missingHeaders: string[]; } export type WebhookValidationValid = WebhookFields & {valid: true}; export type WebhookValidation = | WebhookValidationValid | WebhookValidationInvalid | WebhookValidationMissingHeaders;