{"version":3,"file":"process.mjs","sources":["../../../../../../lib/webhooks/process.ts"],"sourcesContent":["import {StatusCode} from '../types';\nimport {\n  abstractConvertResponse,\n  AdapterResponse,\n  isOK,\n  NormalizedResponse,\n} from '../../runtime/http';\nimport {ConfigInterface} from '../base-types';\nimport * as ShopifyErrors from '../error';\nimport {logger} from '../logger';\n\nimport {\n  DeliveryMethod,\n  HttpWebhookHandlerWithCallback,\n  WebhookProcessParams,\n  WebhookRegistry,\n  WebhookType,\n  WebhookValidationErrorReason,\n  WebhookValidationInvalid,\n  WebhookValidationMissingHeaders,\n  WebhookValidationValid,\n} from './types';\nimport {validateFactory} from './validate';\n\ninterface HandlerCallResult {\n  statusCode: StatusCode;\n  errorMessage?: string;\n}\n\ninterface ErrorCallResult {\n  statusCode: StatusCode;\n  errorMessage: string;\n}\n\nconst STATUS_TEXT_LOOKUP: Record<string, string> = {\n  [StatusCode.Ok]: 'OK',\n  [StatusCode.BadRequest]: 'Bad Request',\n  [StatusCode.Unauthorized]: 'Unauthorized',\n  [StatusCode.NotFound]: 'Not Found',\n  [StatusCode.InternalServerError]: 'Internal Server Error',\n};\n\nexport function process(\n  config: ConfigInterface,\n  webhookRegistry: WebhookRegistry<HttpWebhookHandlerWithCallback>,\n) {\n  return async function process({\n    context,\n    rawBody,\n    ...adapterArgs\n  }: WebhookProcessParams): Promise<AdapterResponse> {\n    const response: NormalizedResponse = {\n      statusCode: StatusCode.Ok,\n      statusText: STATUS_TEXT_LOOKUP[StatusCode.Ok],\n      headers: {},\n    };\n\n    await logger(config).info('Receiving webhook request');\n\n    const webhookCheck = await validateFactory(config)({\n      rawBody,\n      ...adapterArgs,\n    });\n\n    let errorMessage = 'Unknown error while handling webhook';\n    if (webhookCheck.valid) {\n      const handlerResult = await callWebhookHandlers(\n        config,\n        webhookRegistry,\n        webhookCheck,\n        rawBody,\n        context,\n      );\n\n      response.statusCode = handlerResult.statusCode;\n      if (!isOK(response)) {\n        errorMessage = handlerResult.errorMessage || errorMessage;\n      }\n    } else {\n      const errorResult = await handleInvalidWebhook(config, webhookCheck);\n\n      response.statusCode = errorResult.statusCode;\n      response.statusText = STATUS_TEXT_LOOKUP[response.statusCode];\n      errorMessage = errorResult.errorMessage;\n    }\n\n    const returnResponse = await abstractConvertResponse(response, adapterArgs);\n    if (!isOK(response)) {\n      throw new ShopifyErrors.InvalidWebhookError({\n        message: errorMessage,\n        response: returnResponse,\n      });\n    }\n\n    return Promise.resolve(returnResponse);\n  };\n}\n\nasync function callWebhookHandlers(\n  config: ConfigInterface,\n  webhookRegistry: WebhookRegistry<HttpWebhookHandlerWithCallback>,\n  webhookCheck: WebhookValidationValid,\n  rawBody: string,\n  context: any,\n): Promise<HandlerCallResult> {\n  const log = logger(config);\n  const {hmac: _hmac, valid: _valid, ...loggingContext} = webhookCheck;\n\n  await log.debug(\n    'Webhook request is valid, looking for HTTP handlers to call',\n    loggingContext,\n  );\n\n  const handlers = webhookRegistry[webhookCheck.topic] || [];\n\n  const response: HandlerCallResult = {statusCode: StatusCode.Ok};\n\n  let found = false;\n  for (const handler of handlers) {\n    if (handler.deliveryMethod !== DeliveryMethod.Http) {\n      continue;\n    }\n    if (!handler.callback) {\n      response.statusCode = StatusCode.InternalServerError;\n      response.errorMessage =\n        \"Cannot call webhooks.process with a webhook handler that doesn't have a callback\";\n\n      throw new ShopifyErrors.MissingWebhookCallbackError({\n        message: response.errorMessage,\n        response,\n      });\n    }\n\n    found = true;\n\n    await log.debug('Found HTTP handler, triggering it', loggingContext);\n\n    // process() only handles programmatically-registered HTTP webhooks;\n    // events are registered via app TOML and don't use this code path.\n    if (webhookCheck.webhookType !== WebhookType.Webhooks) {\n      throw new ShopifyErrors.InvalidWebhookError({\n        message: 'process() only supports traditional webhooks, not events',\n        response,\n      });\n    }\n    const {webhookId, subTopic} = webhookCheck;\n\n    try {\n      await handler.callback(\n        webhookCheck.topic,\n        webhookCheck.domain,\n        rawBody,\n        webhookId,\n        webhookCheck.apiVersion,\n        ...(subTopic ? subTopic : ''),\n        context,\n      );\n    } catch (error) {\n      response.statusCode = StatusCode.InternalServerError;\n      response.errorMessage = error.message;\n    }\n  }\n\n  if (!found) {\n    await log.debug('No HTTP handlers found', loggingContext);\n\n    response.statusCode = StatusCode.NotFound;\n    response.errorMessage = `No HTTP webhooks registered for topic ${webhookCheck.topic}`;\n  }\n\n  return response;\n}\n\nasync function handleInvalidWebhook(\n  config: ConfigInterface,\n  webhookCheck: WebhookValidationInvalid,\n): Promise<ErrorCallResult> {\n  const response: ErrorCallResult = {\n    statusCode: StatusCode.InternalServerError,\n    errorMessage: 'Unknown error while handling webhook',\n  };\n\n  switch (webhookCheck.reason) {\n    case WebhookValidationErrorReason.MissingHeaders:\n      response.statusCode = StatusCode.BadRequest;\n      response.errorMessage = `Missing one or more of the required HTTP headers to process webhooks: [${(\n        webhookCheck as WebhookValidationMissingHeaders\n      ).missingHeaders.join(', ')}]`;\n      break;\n    case WebhookValidationErrorReason.MissingBody:\n      response.statusCode = StatusCode.BadRequest;\n      response.errorMessage = 'No body was received when processing webhook';\n      break;\n    case WebhookValidationErrorReason.MissingHmac:\n      response.statusCode = StatusCode.BadRequest;\n      response.errorMessage = `Missing HMAC header in request`;\n      break;\n    case WebhookValidationErrorReason.InvalidHmac:\n      response.statusCode = StatusCode.Unauthorized;\n      response.errorMessage = `Could not validate request HMAC`;\n      break;\n  }\n\n  await logger(config).debug(\n    `Webhook request is invalid, returning ${response.statusCode}: ${response.errorMessage}`,\n  );\n\n  return response;\n}\n"],"names":["ShopifyErrors.InvalidWebhookError","ShopifyErrors.MissingWebhookCallbackError"],"mappings":";;;;;;;AAkCA,MAAM,kBAAkB,GAA2B;AACjD,IAAA,CAAC,UAAU,CAAC,EAAE,GAAG,IAAI;AACrB,IAAA,CAAC,UAAU,CAAC,UAAU,GAAG,aAAa;AACtC,IAAA,CAAC,UAAU,CAAC,YAAY,GAAG,cAAc;AACzC,IAAA,CAAC,UAAU,CAAC,QAAQ,GAAG,WAAW;AAClC,IAAA,CAAC,UAAU,CAAC,mBAAmB,GAAG,uBAAuB;CAC1D;AAEK,SAAU,OAAO,CACrB,MAAuB,EACvB,eAAgE,EAAA;IAEhE,OAAO,eAAe,OAAO,CAAC,EAC5B,OAAO,EACP,OAAO,EACP,GAAG,WAAW,EACO,EAAA;AACrB,QAAA,MAAM,QAAQ,GAAuB;YACnC,UAAU,EAAE,UAAU,CAAC,EAAE;AACzB,YAAA,UAAU,EAAE,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;AAC7C,YAAA,OAAO,EAAE,EAAE;SACZ;QAED,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC;AAEtD,QAAA,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;YACjD,OAAO;AACP,YAAA,GAAG,WAAW;AACf,SAAA,CAAC;QAEF,IAAI,YAAY,GAAG,sCAAsC;AACzD,QAAA,IAAI,YAAY,CAAC,KAAK,EAAE;AACtB,YAAA,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAC7C,MAAM,EACN,eAAe,EACf,YAAY,EACZ,OAAO,EACP,OAAO,CACR;AAED,YAAA,QAAQ,CAAC,UAAU,GAAG,aAAa,CAAC,UAAU;AAC9C,YAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;AACnB,gBAAA,YAAY,GAAG,aAAa,CAAC,YAAY,IAAI,YAAY;YAC3D;QACF;aAAO;YACL,MAAM,WAAW,GAAG,MAAM,oBAAoB,CAAC,MAAM,EAAE,YAAY,CAAC;AAEpE,YAAA,QAAQ,CAAC,UAAU,GAAG,WAAW,CAAC,UAAU;YAC5C,QAAQ,CAAC,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC;AAC7D,YAAA,YAAY,GAAG,WAAW,CAAC,YAAY;QACzC;QAEA,MAAM,cAAc,GAAG,MAAM,uBAAuB,CAAC,QAAQ,EAAE,WAAW,CAAC;AAC3E,QAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;AACnB,YAAA,MAAM,IAAIA,mBAAiC,CAAC;AAC1C,gBAAA,OAAO,EAAE,YAAY;AACrB,gBAAA,QAAQ,EAAE,cAAc;AACzB,aAAA,CAAC;QACJ;AAEA,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC;AACxC,IAAA,CAAC;AACH;AAEA,eAAe,mBAAmB,CAChC,MAAuB,EACvB,eAAgE,EAChE,YAAoC,EACpC,OAAe,EACf,OAAY,EAAA;AAEZ,IAAA,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;AAC1B,IAAA,MAAM,EAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,cAAc,EAAC,GAAG,YAAY;IAEpE,MAAM,GAAG,CAAC,KAAK,CACb,6DAA6D,EAC7D,cAAc,CACf;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE;IAE1D,MAAM,QAAQ,GAAsB,EAAC,UAAU,EAAE,UAAU,CAAC,EAAE,EAAC;IAE/D,IAAI,KAAK,GAAG,KAAK;AACjB,IAAA,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE;QAC9B,IAAI,OAAO,CAAC,cAAc,KAAK,cAAc,CAAC,IAAI,EAAE;YAClD;QACF;AACA,QAAA,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;AACrB,YAAA,QAAQ,CAAC,UAAU,GAAG,UAAU,CAAC,mBAAmB;AACpD,YAAA,QAAQ,CAAC,YAAY;AACnB,gBAAA,kFAAkF;AAEpF,YAAA,MAAM,IAAIC,2BAAyC,CAAC;gBAClD,OAAO,EAAE,QAAQ,CAAC,YAAY;gBAC9B,QAAQ;AACT,aAAA,CAAC;QACJ;QAEA,KAAK,GAAG,IAAI;QAEZ,MAAM,GAAG,CAAC,KAAK,CAAC,mCAAmC,EAAE,cAAc,CAAC;;;QAIpE,IAAI,YAAY,CAAC,WAAW,KAAK,WAAW,CAAC,QAAQ,EAAE;AACrD,YAAA,MAAM,IAAID,mBAAiC,CAAC;AAC1C,gBAAA,OAAO,EAAE,0DAA0D;gBACnE,QAAQ;AACT,aAAA,CAAC;QACJ;AACA,QAAA,MAAM,EAAC,SAAS,EAAE,QAAQ,EAAC,GAAG,YAAY;AAE1C,QAAA,IAAI;AACF,YAAA,MAAM,OAAO,CAAC,QAAQ,CACpB,YAAY,CAAC,KAAK,EAClB,YAAY,CAAC,MAAM,EACnB,OAAO,EACP,SAAS,EACT,YAAY,CAAC,UAAU,EACvB,IAAI,QAAQ,GAAG,QAAQ,GAAG,EAAE,CAAC,EAC7B,OAAO,CACR;QACH;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,QAAQ,CAAC,UAAU,GAAG,UAAU,CAAC,mBAAmB;AACpD,YAAA,QAAQ,CAAC,YAAY,GAAG,KAAK,CAAC,OAAO;QACvC;IACF;IAEA,IAAI,CAAC,KAAK,EAAE;QACV,MAAM,GAAG,CAAC,KAAK,CAAC,wBAAwB,EAAE,cAAc,CAAC;AAEzD,QAAA,QAAQ,CAAC,UAAU,GAAG,UAAU,CAAC,QAAQ;QACzC,QAAQ,CAAC,YAAY,GAAG,CAAA,sCAAA,EAAyC,YAAY,CAAC,KAAK,EAAE;IACvF;AAEA,IAAA,OAAO,QAAQ;AACjB;AAEA,eAAe,oBAAoB,CACjC,MAAuB,EACvB,YAAsC,EAAA;AAEtC,IAAA,MAAM,QAAQ,GAAoB;QAChC,UAAU,EAAE,UAAU,CAAC,mBAAmB;AAC1C,QAAA,YAAY,EAAE,sCAAsC;KACrD;AAED,IAAA,QAAQ,YAAY,CAAC,MAAM;QACzB,KAAK,4BAA4B,CAAC,cAAc;AAC9C,YAAA,QAAQ,CAAC,UAAU,GAAG,UAAU,CAAC,UAAU;AAC3C,YAAA,QAAQ,CAAC,YAAY,GAAG,CAAA,uEAAA,EACtB,YACD,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAC9B;QACF,KAAK,4BAA4B,CAAC,WAAW;AAC3C,YAAA,QAAQ,CAAC,UAAU,GAAG,UAAU,CAAC,UAAU;AAC3C,YAAA,QAAQ,CAAC,YAAY,GAAG,8CAA8C;YACtE;QACF,KAAK,4BAA4B,CAAC,WAAW;AAC3C,YAAA,QAAQ,CAAC,UAAU,GAAG,UAAU,CAAC,UAAU;AAC3C,YAAA,QAAQ,CAAC,YAAY,GAAG,CAAA,8BAAA,CAAgC;YACxD;QACF,KAAK,4BAA4B,CAAC,WAAW;AAC3C,YAAA,QAAQ,CAAC,UAAU,GAAG,UAAU,CAAC,YAAY;AAC7C,YAAA,QAAQ,CAAC,YAAY,GAAG,CAAA,+BAAA,CAAiC;YACzD;;AAGJ,IAAA,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CACxB,CAAA,sCAAA,EAAyC,QAAQ,CAAC,UAAU,CAAA,EAAA,EAAK,QAAQ,CAAC,YAAY,CAAA,CAAE,CACzF;AAED,IAAA,OAAO,QAAQ;AACjB;;;;"}