2022-11-21 10:32:36 +00:00
|
|
|
import { APL, AplConfiguredResult, AplReadyResult, AuthData } from "./apl";
|
|
|
|
import { createAPLDebug } from "./apl-debug";
|
2023-01-11 15:55:10 +00:00
|
|
|
import { authDataFromObject } from "./auth-data-from-object";
|
2022-11-21 10:32:36 +00:00
|
|
|
|
2023-01-11 15:55:10 +00:00
|
|
|
const debug = createAPLDebug("SaleorCloudAPL");
|
2022-11-21 10:32:36 +00:00
|
|
|
|
2023-01-11 15:55:10 +00:00
|
|
|
export type SaleorCloudAPLConfig = {
|
2022-11-21 10:32:36 +00:00
|
|
|
resourceUrl: string;
|
2023-01-11 15:55:10 +00:00
|
|
|
token: string;
|
2022-11-21 10:32:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const validateResponseStatus = (response: Response) => {
|
2023-01-11 15:55:10 +00:00
|
|
|
if (!response.ok) {
|
2022-11-21 10:32:36 +00:00
|
|
|
debug("Response failed with status %s", response.status);
|
|
|
|
|
|
|
|
throw new Error(`Fetch returned with non 200 status code ${response.status}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-01-11 15:55:10 +00:00
|
|
|
const mapAuthDataToAPIBody = (authData: AuthData) => ({
|
|
|
|
saleor_app_id: authData.appId,
|
2023-01-12 12:39:49 +00:00
|
|
|
api_url: authData.saleorApiUrl,
|
2023-01-11 15:55:10 +00:00
|
|
|
jwks: authData.jwks,
|
|
|
|
domain: authData.domain,
|
|
|
|
token: authData.token,
|
|
|
|
});
|
|
|
|
|
2022-11-21 10:32:36 +00:00
|
|
|
/**
|
2023-01-11 15:55:10 +00:00
|
|
|
*
|
|
|
|
* Saleor Cloud APL - handle auth data management via REST API.
|
|
|
|
*
|
|
|
|
* Required configuration options:
|
|
|
|
* - `resourceUrl` URL to the REST API
|
|
|
|
* - `token` Authorization token assigned to your deployment
|
|
|
|
*
|
2022-11-21 10:32:36 +00:00
|
|
|
*/
|
2023-01-11 15:55:10 +00:00
|
|
|
export class SaleorCloudAPL implements APL {
|
2022-11-21 10:32:36 +00:00
|
|
|
private readonly resourceUrl: string;
|
|
|
|
|
2023-01-11 15:55:10 +00:00
|
|
|
private headers: Record<string, string>;
|
2022-11-21 10:32:36 +00:00
|
|
|
|
2023-01-11 15:55:10 +00:00
|
|
|
constructor(config: SaleorCloudAPLConfig) {
|
2022-11-21 10:32:36 +00:00
|
|
|
this.resourceUrl = config.resourceUrl;
|
2023-01-11 15:55:10 +00:00
|
|
|
this.headers = {
|
|
|
|
Authorization: `Bearer ${config.token}`,
|
|
|
|
};
|
2022-11-21 10:32:36 +00:00
|
|
|
}
|
|
|
|
|
2023-01-12 12:39:49 +00:00
|
|
|
private getUrlForDomain(saleorApiUrl: string) {
|
2023-01-11 15:55:10 +00:00
|
|
|
// API URL has to be base64 encoded
|
2023-01-12 12:39:49 +00:00
|
|
|
return `${this.resourceUrl}/${btoa(saleorApiUrl)}`;
|
2022-11-21 10:32:36 +00:00
|
|
|
}
|
|
|
|
|
2023-01-12 12:39:49 +00:00
|
|
|
async get(saleorApiUrl: string): Promise<AuthData | undefined> {
|
|
|
|
debug("Will fetch data from SaleorCloudAPL for saleorApiUrl %s", saleorApiUrl);
|
2022-11-21 10:32:36 +00:00
|
|
|
|
2023-01-12 12:39:49 +00:00
|
|
|
const response = await fetch(this.getUrlForDomain(saleorApiUrl), {
|
2022-11-21 10:32:36 +00:00
|
|
|
method: "GET",
|
|
|
|
headers: { "Content-Type": "application/json", ...this.headers },
|
|
|
|
}).catch((error) => {
|
|
|
|
debug("Failed to reach API call: %s", error?.message ?? "Unknown error");
|
|
|
|
throw new Error(`Attempt in fetch the data resulted with error: ${error}`);
|
|
|
|
});
|
|
|
|
|
|
|
|
validateResponseStatus(response);
|
|
|
|
|
|
|
|
const parsedResponse = (await response.json().catch((e) => {
|
|
|
|
debug("Failed to parse response: %s", e?.message ?? "Unknown error");
|
|
|
|
})) as unknown;
|
|
|
|
|
2023-01-11 15:55:10 +00:00
|
|
|
const authData = authDataFromObject(parsedResponse);
|
|
|
|
if (!authData) {
|
2023-01-12 12:39:49 +00:00
|
|
|
debug("No auth data for given saleorApiUrl");
|
2023-01-11 15:55:10 +00:00
|
|
|
return undefined;
|
2022-11-21 10:32:36 +00:00
|
|
|
}
|
|
|
|
|
2023-01-11 15:55:10 +00:00
|
|
|
return authData;
|
2022-11-21 10:32:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async set(authData: AuthData) {
|
2023-01-11 15:55:10 +00:00
|
|
|
debug("Saving data to SaleorCloudAPL for domain: %s", authData.domain);
|
2022-11-21 10:32:36 +00:00
|
|
|
|
|
|
|
const response = await fetch(this.resourceUrl, {
|
|
|
|
method: "POST",
|
|
|
|
headers: { "Content-Type": "application/json", ...this.headers },
|
2023-01-11 15:55:10 +00:00
|
|
|
body: JSON.stringify(mapAuthDataToAPIBody(authData)),
|
2022-11-21 10:32:36 +00:00
|
|
|
}).catch((e) => {
|
|
|
|
debug("Failed to reach API call: %s", e?.message ?? "Unknown error");
|
|
|
|
|
|
|
|
throw new Error(`Error during saving the data: ${e}`);
|
|
|
|
});
|
|
|
|
|
|
|
|
validateResponseStatus(response);
|
|
|
|
|
|
|
|
debug("Set command finished successfully for domain: %", authData.domain);
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2023-01-12 12:39:49 +00:00
|
|
|
async delete(saleorApiUrl: string) {
|
|
|
|
debug("Deleting data from SaleorCloud for saleorApiUrl: %s", saleorApiUrl);
|
2022-11-21 10:32:36 +00:00
|
|
|
|
|
|
|
try {
|
2023-01-12 12:39:49 +00:00
|
|
|
const response = await fetch(this.getUrlForDomain(saleorApiUrl), {
|
2022-11-21 10:32:36 +00:00
|
|
|
method: "DELETE",
|
|
|
|
headers: { "Content-Type": "application/json", ...this.headers },
|
|
|
|
});
|
|
|
|
|
|
|
|
debug(`Delete responded with ${response.status} code`);
|
|
|
|
} catch (error) {
|
|
|
|
debug("Error during deleting the data: %s", error);
|
|
|
|
|
2023-01-11 15:55:10 +00:00
|
|
|
throw new Error(`Error during deleting the data: ${error}`);
|
2022-11-21 10:32:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async getAll() {
|
2023-01-11 15:55:10 +00:00
|
|
|
debug("Get all data from SaleorCloud");
|
2022-11-21 10:32:36 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
const response = await fetch(this.resourceUrl, {
|
|
|
|
method: "GET",
|
|
|
|
headers: { "Content-Type": "application/json", ...this.headers },
|
|
|
|
});
|
|
|
|
|
|
|
|
debug(`Get all responded with ${response.status} code`);
|
|
|
|
|
|
|
|
return ((await response.json()) as AuthData[]) || [];
|
|
|
|
} catch (error) {
|
|
|
|
debug("Error during getting all the data:", error);
|
|
|
|
}
|
|
|
|
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
async isReady(): Promise<AplReadyResult> {
|
|
|
|
const configured = await this.isConfigured();
|
|
|
|
|
|
|
|
return configured
|
|
|
|
? {
|
|
|
|
ready: true,
|
|
|
|
}
|
|
|
|
: {
|
|
|
|
ready: false,
|
2023-01-11 15:55:10 +00:00
|
|
|
error: new Error("SaleorCloudAPL is not configured"),
|
2022-11-21 10:32:36 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
async isConfigured(): Promise<AplConfiguredResult> {
|
|
|
|
if (!this.resourceUrl) {
|
2023-01-11 15:55:10 +00:00
|
|
|
debug("Resource URL has not been specified.");
|
2022-11-21 10:32:36 +00:00
|
|
|
return {
|
|
|
|
configured: false,
|
2023-01-11 15:55:10 +00:00
|
|
|
error: new Error("SaleorCloudAPL required resourceUrl param"),
|
2022-11-21 10:32:36 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
configured: true,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|