2023-04-29 09:30:48 +00:00
|
|
|
import { createProtectedHandler, NextProtectedApiHandler } from "@saleor/app-sdk/handlers/next";
|
|
|
|
import { saleorApp } from "../../../saleor-app";
|
2023-08-16 12:08:07 +00:00
|
|
|
import { FetchOwnWebhooksDocument, OwnWebhookFragment } from "../../../generated/graphql";
|
2023-04-29 09:30:48 +00:00
|
|
|
import { AlgoliaSearchProvider } from "../../lib/algolia/algoliaSearchProvider";
|
|
|
|
import { createSettingsManager } from "../../lib/metadata";
|
|
|
|
import {
|
|
|
|
IWebhookActivityTogglerService,
|
|
|
|
WebhookActivityTogglerService,
|
|
|
|
} from "../../domain/WebhookActivityToggler.service";
|
|
|
|
import { createLogger } from "../../lib/logger";
|
|
|
|
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
|
|
|
import { SearchProvider } from "../../lib/searchProvider";
|
2023-06-19 13:59:27 +00:00
|
|
|
import { createGraphQLClient } from "@saleor/apps-shared";
|
|
|
|
import { Client } from "urql";
|
2023-08-16 12:08:07 +00:00
|
|
|
import { isWebhookUpdateNeeded } from "../../lib/algolia/is-webhook-update-needed";
|
2023-04-29 09:30:48 +00:00
|
|
|
|
|
|
|
const logger = createLogger({
|
|
|
|
service: "webhooksStatusHandler",
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Simple dependency injection - factory injects all services, in tests everything can be configured without mocks
|
|
|
|
*/
|
|
|
|
type FactoryProps = {
|
2023-08-29 20:53:51 +00:00
|
|
|
settingsManagerFactory: (
|
|
|
|
client: Pick<Client, "query" | "mutation">,
|
|
|
|
appId: string,
|
|
|
|
) => SettingsManager;
|
|
|
|
webhookActivityTogglerFactory: (
|
|
|
|
appId: string,
|
|
|
|
client: Pick<Client, "query" | "mutation">,
|
|
|
|
) => IWebhookActivityTogglerService;
|
2023-04-29 09:30:48 +00:00
|
|
|
algoliaSearchProviderFactory: (appId: string, apiKey: string) => Pick<SearchProvider, "ping">;
|
2023-08-29 20:53:51 +00:00
|
|
|
graphqlClientFactory: (saleorApiUrl: string, token: string) => Pick<Client, "query" | "mutation">;
|
2023-04-29 09:30:48 +00:00
|
|
|
};
|
|
|
|
|
2023-08-16 12:08:07 +00:00
|
|
|
export type WebhooksStatusResponse = {
|
|
|
|
webhooks: OwnWebhookFragment[];
|
|
|
|
isUpdateNeeded: boolean;
|
|
|
|
};
|
|
|
|
|
2023-04-29 09:30:48 +00:00
|
|
|
export const webhooksStatusHandlerFactory =
|
|
|
|
({
|
|
|
|
settingsManagerFactory,
|
|
|
|
webhookActivityTogglerFactory,
|
|
|
|
algoliaSearchProviderFactory,
|
|
|
|
graphqlClientFactory,
|
2023-08-16 12:08:07 +00:00
|
|
|
}: FactoryProps): NextProtectedApiHandler<WebhooksStatusResponse> =>
|
2023-04-29 09:30:48 +00:00
|
|
|
async (req, res, { authData }) => {
|
|
|
|
/**
|
|
|
|
* Initialize services
|
|
|
|
*/
|
|
|
|
const client = graphqlClientFactory(authData.saleorApiUrl, authData.token);
|
|
|
|
const webhooksToggler = webhookActivityTogglerFactory(authData.appId, client);
|
2023-08-29 20:53:51 +00:00
|
|
|
const settingsManager = settingsManagerFactory(client, authData.appId);
|
2023-04-29 09:30:48 +00:00
|
|
|
|
|
|
|
const domain = new URL(authData.saleorApiUrl).host;
|
|
|
|
|
|
|
|
const [secretKey, appId] = await Promise.all([
|
|
|
|
settingsManager.get("secretKey", domain),
|
|
|
|
settingsManager.get("appId", domain),
|
|
|
|
]);
|
|
|
|
|
|
|
|
const settings = { secretKey, appId };
|
|
|
|
|
|
|
|
logger.debug(settings, "fetched settings");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If settings are incomplete, disable webhooks
|
|
|
|
*
|
|
|
|
* TODO Extract config operations to domain/
|
|
|
|
*/
|
|
|
|
if (!settings.appId || !settings.secretKey) {
|
|
|
|
logger.debug("Settings not set, will disable webhooks");
|
|
|
|
|
|
|
|
await webhooksToggler.disableOwnWebhooks();
|
|
|
|
} else {
|
|
|
|
/**
|
|
|
|
* Otherwise, if settings are set, check in Algolia if tokens are valid
|
|
|
|
*/
|
|
|
|
const algoliaService = algoliaSearchProviderFactory(settings.appId, settings.secretKey);
|
|
|
|
|
|
|
|
try {
|
|
|
|
logger.debug("Settings set, will ping Algolia");
|
|
|
|
|
|
|
|
await algoliaService.ping();
|
|
|
|
} catch (e) {
|
|
|
|
logger.debug("Algolia ping failed, will disable webhooks");
|
|
|
|
/**
|
|
|
|
* If credentials are invalid, also disable webhooks
|
|
|
|
*/
|
|
|
|
await webhooksToggler.disableOwnWebhooks();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
logger.debug("Settings and Algolia are correct, will fetch Webhooks from Saleor");
|
|
|
|
|
|
|
|
const webhooks = await client
|
|
|
|
.query(FetchOwnWebhooksDocument, { id: authData.appId })
|
|
|
|
.toPromise()
|
|
|
|
.then((r) => r.data?.app?.webhooks);
|
|
|
|
|
|
|
|
if (!webhooks) {
|
|
|
|
return res.status(500).end();
|
|
|
|
}
|
|
|
|
|
2023-08-16 12:08:07 +00:00
|
|
|
const isUpdateNeeded = isWebhookUpdateNeeded({
|
|
|
|
existingWebhookNames: webhooks.map((w) => w.name),
|
|
|
|
});
|
|
|
|
|
|
|
|
return res.status(200).json({
|
|
|
|
webhooks,
|
|
|
|
isUpdateNeeded,
|
|
|
|
});
|
2023-04-29 09:30:48 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
return res.status(500).end();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export default createProtectedHandler(
|
|
|
|
webhooksStatusHandlerFactory({
|
|
|
|
settingsManagerFactory: createSettingsManager,
|
|
|
|
webhookActivityTogglerFactory: function (appId, client) {
|
|
|
|
return new WebhookActivityTogglerService(appId, client);
|
|
|
|
},
|
|
|
|
algoliaSearchProviderFactory(appId, apiKey) {
|
2023-09-01 15:01:41 +00:00
|
|
|
return new AlgoliaSearchProvider({ appId, apiKey, enabledKeys: [] });
|
2023-04-29 09:30:48 +00:00
|
|
|
},
|
|
|
|
graphqlClientFactory(saleorApiUrl: string, token: string) {
|
2023-06-19 13:59:27 +00:00
|
|
|
return createGraphQLClient({ saleorApiUrl, token });
|
2023-04-29 09:30:48 +00:00
|
|
|
},
|
|
|
|
}),
|
|
|
|
saleorApp.apl,
|
2023-08-16 12:08:07 +00:00
|
|
|
[],
|
2023-04-29 09:30:48 +00:00
|
|
|
);
|