@red-hat-developer-hub/e2e-test-utils 1.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +6 -0
- package/dist/deployment/keycloak/config/keycloak-values.yaml +96 -0
- package/dist/deployment/keycloak/constants.d.ts +29 -0
- package/dist/deployment/keycloak/constants.d.ts.map +1 -0
- package/dist/deployment/keycloak/constants.js +75 -0
- package/dist/deployment/keycloak/deployment.d.ts +89 -0
- package/dist/deployment/keycloak/deployment.d.ts.map +1 -0
- package/dist/deployment/keycloak/deployment.js +435 -0
- package/dist/deployment/keycloak/index.d.ts +2 -0
- package/dist/deployment/keycloak/index.d.ts.map +1 -0
- package/dist/deployment/keycloak/index.js +1 -0
- package/dist/deployment/keycloak/types.d.ts +59 -0
- package/dist/deployment/keycloak/types.d.ts.map +1 -0
- package/dist/deployment/keycloak/types.js +1 -0
- package/dist/deployment/rhdh/config/auth/guest/app-config.yaml +5 -0
- package/dist/deployment/rhdh/config/auth/keycloak/app-config.yaml +19 -0
- package/dist/deployment/rhdh/config/auth/keycloak/dynamic-plugins.yaml +3 -0
- package/dist/deployment/rhdh/config/auth/keycloak/secrets.yaml +12 -0
- package/dist/deployment/rhdh/config/common/app-config-rhdh.yaml +6 -0
- package/dist/deployment/rhdh/config/common/dynamic-plugins.yaml +3 -0
- package/dist/deployment/rhdh/config/common/rhdh-secrets.yaml +7 -0
- package/dist/deployment/rhdh/config/helm/value_file.yaml +7 -0
- package/dist/deployment/rhdh/config/operator/subscription.yaml +21 -0
- package/dist/deployment/rhdh/constants.d.ts +19 -0
- package/dist/deployment/rhdh/constants.d.ts.map +1 -0
- package/dist/deployment/rhdh/constants.js +27 -0
- package/dist/deployment/rhdh/deployment.d.ts +40 -0
- package/dist/deployment/rhdh/deployment.d.ts.map +1 -0
- package/dist/deployment/rhdh/deployment.js +303 -0
- package/dist/deployment/rhdh/index.d.ts +2 -0
- package/dist/deployment/rhdh/index.d.ts.map +1 -0
- package/dist/deployment/rhdh/index.js +1 -0
- package/dist/deployment/rhdh/types.d.ts +31 -0
- package/dist/deployment/rhdh/types.d.ts.map +1 -0
- package/dist/deployment/rhdh/types.js +1 -0
- package/dist/eslint/base.config.d.ts +10 -0
- package/dist/eslint/base.config.d.ts.map +1 -0
- package/dist/eslint/base.config.js +213 -0
- package/dist/playwright/base-config.d.ts +14 -0
- package/dist/playwright/base-config.d.ts.map +1 -0
- package/dist/playwright/base-config.js +47 -0
- package/dist/playwright/fixtures/test.d.ts +13 -0
- package/dist/playwright/fixtures/test.d.ts.map +1 -0
- package/dist/playwright/fixtures/test.js +48 -0
- package/dist/playwright/global-setup.d.ts +6 -0
- package/dist/playwright/global-setup.d.ts.map +1 -0
- package/dist/playwright/global-setup.js +67 -0
- package/dist/playwright/helpers/accessibility.d.ts +13 -0
- package/dist/playwright/helpers/accessibility.d.ts.map +1 -0
- package/dist/playwright/helpers/accessibility.js +24 -0
- package/dist/playwright/helpers/api-endpoints.d.ts +11 -0
- package/dist/playwright/helpers/api-endpoints.d.ts.map +1 -0
- package/dist/playwright/helpers/api-endpoints.js +15 -0
- package/dist/playwright/helpers/api-helper.d.ts +77 -0
- package/dist/playwright/helpers/api-helper.d.ts.map +1 -0
- package/dist/playwright/helpers/api-helper.js +285 -0
- package/dist/playwright/helpers/common.d.ts +31 -0
- package/dist/playwright/helpers/common.d.ts.map +1 -0
- package/dist/playwright/helpers/common.js +342 -0
- package/dist/playwright/helpers/index.d.ts +5 -0
- package/dist/playwright/helpers/index.d.ts.map +1 -0
- package/dist/playwright/helpers/index.js +4 -0
- package/dist/playwright/helpers/navbar.d.ts +2 -0
- package/dist/playwright/helpers/navbar.d.ts.map +1 -0
- package/dist/playwright/helpers/navbar.js +1 -0
- package/dist/playwright/helpers/ui-helper.d.ts +106 -0
- package/dist/playwright/helpers/ui-helper.d.ts.map +1 -0
- package/dist/playwright/helpers/ui-helper.js +439 -0
- package/dist/playwright/page-objects/global-obj.d.ts +25 -0
- package/dist/playwright/page-objects/global-obj.d.ts.map +1 -0
- package/dist/playwright/page-objects/global-obj.js +24 -0
- package/dist/playwright/page-objects/page-obj.d.ts +41 -0
- package/dist/playwright/page-objects/page-obj.d.ts.map +1 -0
- package/dist/playwright/page-objects/page-obj.js +40 -0
- package/dist/playwright/pages/catalog-import.d.ts +31 -0
- package/dist/playwright/pages/catalog-import.d.ts.map +1 -0
- package/dist/playwright/pages/catalog-import.js +65 -0
- package/dist/playwright/pages/catalog.d.ts +14 -0
- package/dist/playwright/pages/catalog.d.ts.map +1 -0
- package/dist/playwright/pages/catalog.js +37 -0
- package/dist/playwright/pages/extensions.d.ts +38 -0
- package/dist/playwright/pages/extensions.d.ts.map +1 -0
- package/dist/playwright/pages/extensions.js +110 -0
- package/dist/playwright/pages/home-page.d.ts +10 -0
- package/dist/playwright/pages/home-page.d.ts.map +1 -0
- package/dist/playwright/pages/home-page.js +46 -0
- package/dist/playwright/pages/index.d.ts +6 -0
- package/dist/playwright/pages/index.d.ts.map +1 -0
- package/dist/playwright/pages/index.js +5 -0
- package/dist/playwright/pages/notifications.d.ts +24 -0
- package/dist/playwright/pages/notifications.d.ts.map +1 -0
- package/dist/playwright/pages/notifications.js +112 -0
- package/dist/utils/bash.d.ts +3 -0
- package/dist/utils/bash.d.ts.map +1 -0
- package/dist/utils/bash.js +4 -0
- package/dist/utils/common.d.ts +3 -0
- package/dist/utils/common.d.ts.map +1 -0
- package/dist/utils/common.js +8 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/kubernetes-client.d.ts +97 -0
- package/dist/utils/kubernetes-client.d.ts.map +1 -0
- package/dist/utils/kubernetes-client.js +558 -0
- package/dist/utils/merge-yamls.d.ts +50 -0
- package/dist/utils/merge-yamls.d.ts.map +1 -0
- package/dist/utils/merge-yamls.js +96 -0
- package/dist/utils/plugin-metadata.d.ts +116 -0
- package/dist/utils/plugin-metadata.d.ts.map +1 -0
- package/dist/utils/plugin-metadata.js +330 -0
- package/package.json +108 -0
- package/tsconfig.base.json +16 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineConfig as baseDefineConfig, } from "@playwright/test";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
/**
|
|
4
|
+
* Base Playwright configuration that can be extended by workspace-specific configs.
|
|
5
|
+
* Provides sensible defaults for RHDH plugin e2e testing.
|
|
6
|
+
*/
|
|
7
|
+
export const baseConfig = {
|
|
8
|
+
testDir: "./tests",
|
|
9
|
+
forbidOnly: !!process.env.CI,
|
|
10
|
+
retries: Number(process.env.PLAYWRIGHT_RETRIES ?? 0),
|
|
11
|
+
workers: process.env.PLAYWRIGHT_WORKERS || "50%",
|
|
12
|
+
outputDir: "node_modules/.cache/e2e-test-results",
|
|
13
|
+
timeout: 90_000,
|
|
14
|
+
reporter: [
|
|
15
|
+
["list"],
|
|
16
|
+
["html", { outputFolder: "playwright-report", open: "on-failure" }],
|
|
17
|
+
["json", { outputFile: "playwright-report/results.json" }],
|
|
18
|
+
],
|
|
19
|
+
use: {
|
|
20
|
+
ignoreHTTPSErrors: true,
|
|
21
|
+
trace: "retain-on-failure",
|
|
22
|
+
screenshot: "only-on-failure",
|
|
23
|
+
viewport: { width: 1920, height: 1080 },
|
|
24
|
+
video: {
|
|
25
|
+
mode: "retain-on-failure",
|
|
26
|
+
size: { width: 1280, height: 720 },
|
|
27
|
+
},
|
|
28
|
+
actionTimeout: 10_000,
|
|
29
|
+
navigationTimeout: 50_000,
|
|
30
|
+
},
|
|
31
|
+
expect: {
|
|
32
|
+
timeout: 10_000,
|
|
33
|
+
},
|
|
34
|
+
globalSetup: resolve(import.meta.dirname, "../playwright/global-setup.js"),
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Defines a workspace-specific config by merging with base config.
|
|
38
|
+
* Only allows overriding the projects configuration.
|
|
39
|
+
* @param overrides - Object containing projects to override
|
|
40
|
+
* @returns Merged Playwright configuration
|
|
41
|
+
*/
|
|
42
|
+
export function defineConfig(overrides = {}) {
|
|
43
|
+
return baseDefineConfig({
|
|
44
|
+
...baseConfig,
|
|
45
|
+
projects: overrides.projects,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RHDHDeployment } from "../../deployment/rhdh/index.js";
|
|
2
|
+
import { LoginHelper, UIhelper } from "../helpers/index.js";
|
|
3
|
+
type RHDHDeploymentTestFixtures = {
|
|
4
|
+
rhdh: RHDHDeployment;
|
|
5
|
+
uiHelper: UIhelper;
|
|
6
|
+
loginHelper: LoginHelper;
|
|
7
|
+
};
|
|
8
|
+
type RHDHDeploymentWorkerFixtures = {
|
|
9
|
+
rhdhDeploymentWorker: RHDHDeployment;
|
|
10
|
+
};
|
|
11
|
+
export * from "@playwright/test";
|
|
12
|
+
export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions & RHDHDeploymentTestFixtures, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions & RHDHDeploymentWorkerFixtures>;
|
|
13
|
+
//# sourceMappingURL=test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../src/playwright/fixtures/test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,KAAK,0BAA0B,GAAG;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC1B,CAAC;AAEF,KAAK,4BAA4B,GAAG;IAClC,oBAAoB,EAAE,cAAc,CAAC;CACtC,CAAC;AAEF,cAAc,kBAAkB,CAAC;AAEjC,eAAO,MAAM,IAAI,oSAkDf,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { RHDHDeployment } from "../../deployment/rhdh/index.js";
|
|
2
|
+
import { test as base } from "@playwright/test";
|
|
3
|
+
import { LoginHelper, UIhelper } from "../helpers/index.js";
|
|
4
|
+
export * from "@playwright/test";
|
|
5
|
+
export const test = base.extend({
|
|
6
|
+
rhdhDeploymentWorker: [
|
|
7
|
+
// eslint-disable-next-line no-empty-pattern
|
|
8
|
+
async ({}, use, workerInfo) => {
|
|
9
|
+
console.log(`Deploying rhdh for plugin ${workerInfo.project.name} in namespace ${workerInfo.project.name}`);
|
|
10
|
+
const rhdhDeployment = new RHDHDeployment(workerInfo.project.name);
|
|
11
|
+
try {
|
|
12
|
+
await rhdhDeployment.configure();
|
|
13
|
+
await use(rhdhDeployment);
|
|
14
|
+
}
|
|
15
|
+
finally {
|
|
16
|
+
if (process.env.CI) {
|
|
17
|
+
console.log(`Deleting namespace ${workerInfo.project.name}`);
|
|
18
|
+
await rhdhDeployment.teardown();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{ scope: "worker", auto: true },
|
|
23
|
+
],
|
|
24
|
+
rhdh: [
|
|
25
|
+
async ({ rhdhDeploymentWorker }, use) => {
|
|
26
|
+
await use(rhdhDeploymentWorker);
|
|
27
|
+
},
|
|
28
|
+
{ auto: true, scope: "test" },
|
|
29
|
+
],
|
|
30
|
+
uiHelper: [
|
|
31
|
+
async ({ page }, use) => {
|
|
32
|
+
await use(new UIhelper(page));
|
|
33
|
+
},
|
|
34
|
+
{ scope: "test" },
|
|
35
|
+
],
|
|
36
|
+
loginHelper: [
|
|
37
|
+
async ({ page }, use) => {
|
|
38
|
+
await use(new LoginHelper(page));
|
|
39
|
+
},
|
|
40
|
+
{ scope: "test" },
|
|
41
|
+
],
|
|
42
|
+
baseURL: [
|
|
43
|
+
async ({ rhdhDeploymentWorker }, use) => {
|
|
44
|
+
await use(rhdhDeploymentWorker.rhdhUrl);
|
|
45
|
+
},
|
|
46
|
+
{ scope: "test" },
|
|
47
|
+
],
|
|
48
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-setup.d.ts","sourceRoot":"","sources":["../../src/playwright/global-setup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2EH,wBAA8B,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAMzD"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global setup for Playwright tests.
|
|
3
|
+
* This file runs once before all tests.
|
|
4
|
+
*/
|
|
5
|
+
import { KubernetesClientHelper } from "../utils/kubernetes-client.js";
|
|
6
|
+
import { $ } from "../utils/bash.js";
|
|
7
|
+
import { KeycloakHelper } from "../deployment/keycloak/index.js";
|
|
8
|
+
import { DEFAULT_KEYCLOAK_CONFIG, DEFAULT_RHDH_CLIENT, DEFAULT_USERS, } from "../deployment/keycloak/constants.js";
|
|
9
|
+
const REQUIRED_BINARIES = ["oc", "kubectl", "helm"];
|
|
10
|
+
async function checkRequiredBinaries() {
|
|
11
|
+
const missingBinaries = [];
|
|
12
|
+
for (const binary of REQUIRED_BINARIES) {
|
|
13
|
+
try {
|
|
14
|
+
await $ `command -v ${binary} > /dev/null 2>&1`;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
missingBinaries.push(binary);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (missingBinaries.length > 0) {
|
|
21
|
+
throw new Error(`ERROR: Missing required binaries: ${missingBinaries.join(", ")}. Please install them before running tests.`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function setClusterRouterBaseEnv() {
|
|
25
|
+
const k8sClient = new KubernetesClientHelper();
|
|
26
|
+
process.env.K8S_CLUSTER_ROUTER_BASE =
|
|
27
|
+
await k8sClient.getClusterIngressDomain();
|
|
28
|
+
console.log(`Cluster router base: ${process.env.K8S_CLUSTER_ROUTER_BASE}`);
|
|
29
|
+
}
|
|
30
|
+
async function deployKeycloak() {
|
|
31
|
+
if (process.env.SKIP_KEYCLOAK_DEPLOYMENT === "true") {
|
|
32
|
+
console.log("Skipping Keycloak deployment");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
console.log("Set SKIP_KEYCLOAK_DEPLOYMENT=true if test doesn't require keycloak/oidc as auth provider");
|
|
36
|
+
const keycloak = new KeycloakHelper({ namespace: "rhdh-keycloak" });
|
|
37
|
+
// Check if Keycloak is already running
|
|
38
|
+
if (await keycloak.isRunning()) {
|
|
39
|
+
console.log("Keycloak is already running, skipping deployment");
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
await keycloak.deploy();
|
|
43
|
+
await keycloak.configureForRHDH();
|
|
44
|
+
}
|
|
45
|
+
// Set environment variables for RHDH integration
|
|
46
|
+
const realm = DEFAULT_KEYCLOAK_CONFIG.realm;
|
|
47
|
+
process.env.KEYCLOAK_CLIENT_SECRET = DEFAULT_RHDH_CLIENT.clientSecret;
|
|
48
|
+
process.env.KEYCLOAK_CLIENT_ID = DEFAULT_RHDH_CLIENT.clientId;
|
|
49
|
+
process.env.KEYCLOAK_REALM = realm;
|
|
50
|
+
process.env.KEYCLOAK_LOGIN_REALM = realm;
|
|
51
|
+
process.env.KEYCLOAK_METADATA_URL = `${keycloak.keycloakUrl}/realms/${realm}`;
|
|
52
|
+
process.env.KEYCLOAK_BASE_URL = keycloak.keycloakUrl;
|
|
53
|
+
console.table({
|
|
54
|
+
keycloakURL: keycloak.keycloakUrl,
|
|
55
|
+
adminUser: keycloak.deploymentConfig.adminUser,
|
|
56
|
+
adminPassword: keycloak.deploymentConfig.adminPassword,
|
|
57
|
+
testUsername: DEFAULT_USERS[0].username,
|
|
58
|
+
testPassword: DEFAULT_USERS[0].password,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export default async function globalSetup() {
|
|
62
|
+
console.log("Running global setup...");
|
|
63
|
+
await checkRequiredBinaries();
|
|
64
|
+
await setClusterRouterBaseEnv();
|
|
65
|
+
await deployKeycloak();
|
|
66
|
+
console.log("Global setup completed successfully");
|
|
67
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Page } from "@playwright/test";
|
|
2
|
+
export interface AccessibilityTestOptions {
|
|
3
|
+
/** Custom name for the attached results file. Defaults to "accessibility-scan-results.violations.json" */
|
|
4
|
+
attachName?: string;
|
|
5
|
+
/** Whether to assert that there are no violations. Defaults to true */
|
|
6
|
+
assertNoViolations?: boolean;
|
|
7
|
+
/** WCAG tags to test against. Defaults to ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] */
|
|
8
|
+
wcagTags?: string[];
|
|
9
|
+
/** Rules to disable during the scan. Defaults to ["color-contrast"] */
|
|
10
|
+
disabledRules?: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function runAccessibilityTests(page: Page, options?: AccessibilityTestOptions): Promise<import("axe-core").AxeResults>;
|
|
13
|
+
//# sourceMappingURL=accessibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/accessibility.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C,MAAM,WAAW,wBAAwB;IACvC,0GAA0G;IAC1G,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AASD,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,wBAA6B,0CAuBvC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import AxeBuilder from "@axe-core/playwright";
|
|
2
|
+
import { expect, test } from "@playwright/test";
|
|
3
|
+
const DEFAULT_OPTIONS = {
|
|
4
|
+
attachName: "accessibility-scan-results.violations.json",
|
|
5
|
+
assertNoViolations: true,
|
|
6
|
+
wcagTags: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"],
|
|
7
|
+
disabledRules: ["color-contrast"],
|
|
8
|
+
};
|
|
9
|
+
export async function runAccessibilityTests(page, options = {}) {
|
|
10
|
+
const config = { ...DEFAULT_OPTIONS, ...options };
|
|
11
|
+
const testInfo = test.info();
|
|
12
|
+
const accessibilityScanResults = await new AxeBuilder({ page })
|
|
13
|
+
.withTags(config.wcagTags)
|
|
14
|
+
.disableRules(config.disabledRules)
|
|
15
|
+
.analyze();
|
|
16
|
+
await testInfo.attach(config.attachName, {
|
|
17
|
+
body: JSON.stringify(accessibilityScanResults.violations, null, 2),
|
|
18
|
+
contentType: "application/json",
|
|
19
|
+
});
|
|
20
|
+
if (config.assertNoViolations) {
|
|
21
|
+
expect(accessibilityScanResults.violations, `Found ${accessibilityScanResults.violations.length} accessibility violation(s)`).toHaveLength(0);
|
|
22
|
+
}
|
|
23
|
+
return accessibilityScanResults;
|
|
24
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const GITHUB_API_ENDPOINTS: {
|
|
2
|
+
pull: (owner: string, repo: string, state: "open" | "closed" | "all") => string;
|
|
3
|
+
issues: (state: string) => string;
|
|
4
|
+
workflowRuns: string;
|
|
5
|
+
deleteRepo: (owner: string, repo: string) => string;
|
|
6
|
+
mergePR: (owner: string, repoName: string, pullNumber: number) => string;
|
|
7
|
+
createRepo: (owner: string) => string;
|
|
8
|
+
pullFiles: (owner: string, repoName: string, pr: number) => string;
|
|
9
|
+
contents: (owner: string, repoName: string) => string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=api-endpoints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-endpoints.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/api-endpoints.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,oBAAoB;kBACjB,MAAM,QAAQ,MAAM,SAAS,MAAM,GAAG,QAAQ,GAAG,KAAK;oBAGpD,MAAM;;wBAVG,MAAM,QAAQ,MAAM;qBAiB5B,MAAM,YAAY,MAAM,cAAc,MAAM;wBAGzC,MAAM;uBAEP,MAAM,YAAY,MAAM,MAAM,MAAM;sBAGrC,MAAM,YAAY,MAAM;CAE3C,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const baseApiUrl = "https://api.github.com";
|
|
2
|
+
const perPage = 100;
|
|
3
|
+
const getRepoUrl = (owner, repo) => `${baseApiUrl}/repos/${owner}/${repo}`;
|
|
4
|
+
const getOrgUrl = (owner) => `${baseApiUrl}/orgs/${owner}`;
|
|
5
|
+
const backstageShowcaseAPI = getRepoUrl("janus-idp", "backstage-showcase");
|
|
6
|
+
export const GITHUB_API_ENDPOINTS = {
|
|
7
|
+
pull: (owner, repo, state) => `${getRepoUrl(owner, repo)}/pulls?per_page=${perPage}&state=${state}`,
|
|
8
|
+
issues: (state) => `${backstageShowcaseAPI}/issues?per_page=${perPage}&sort=updated&state=${state}`,
|
|
9
|
+
workflowRuns: `${backstageShowcaseAPI}/actions/runs?per_page=${perPage}`,
|
|
10
|
+
deleteRepo: getRepoUrl,
|
|
11
|
+
mergePR: (owner, repoName, pullNumber) => `${getRepoUrl(owner, repoName)}/pulls/${pullNumber}/merge`,
|
|
12
|
+
createRepo: (owner) => `${getOrgUrl(owner)}/repos`,
|
|
13
|
+
pullFiles: (owner, repoName, pr) => `${getRepoUrl(owner, repoName)}/pulls/${pr}/files`,
|
|
14
|
+
contents: (owner, repoName) => `${getRepoUrl(owner, repoName)}/contents`,
|
|
15
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { APIResponse } from "@playwright/test";
|
|
2
|
+
export declare class APIHelper {
|
|
3
|
+
private static githubAPIVersion;
|
|
4
|
+
private staticToken;
|
|
5
|
+
private baseUrl;
|
|
6
|
+
useStaticToken: boolean;
|
|
7
|
+
static githubRequest(method: string, url: string, body?: string | object): Promise<APIResponse>;
|
|
8
|
+
static getGithubPaginatedRequest(url: string, pageNo?: number, response?: unknown[]): Promise<unknown[]>;
|
|
9
|
+
static createGitHubRepo(owner: string, repoName: string): Promise<void>;
|
|
10
|
+
static createGitHubRepoWithFile(owner: string, repoName: string, filename: string, fileContent: string): Promise<void>;
|
|
11
|
+
static createFileInRepo(owner: string, repoName: string, filePath: string, content: string, commitMessage: string, branch?: string): Promise<void>;
|
|
12
|
+
static initCommit(owner: string, repo: string, branch?: string): Promise<void>;
|
|
13
|
+
static deleteGitHubRepo(owner: string, repoName: string): Promise<void>;
|
|
14
|
+
static mergeGitHubPR(owner: string, repoName: string, pullNumber: number): Promise<void>;
|
|
15
|
+
static getGitHubPRs(owner: string, repoName: string, state: "open" | "closed" | "all", paginated?: boolean): Promise<any>;
|
|
16
|
+
static getfileContentFromPR(owner: string, repoName: string, pr: number, filename: string): Promise<string>;
|
|
17
|
+
getGuestToken(): Promise<string>;
|
|
18
|
+
getGuestAuthHeader(): Promise<{
|
|
19
|
+
[key: string]: string;
|
|
20
|
+
}>;
|
|
21
|
+
setStaticToken(token: string): Promise<void>;
|
|
22
|
+
setBaseUrl(url: string): Promise<void>;
|
|
23
|
+
static apiRequestWithStaticToken(method: string, url: string, staticToken: string, body?: string | object): Promise<APIResponse>;
|
|
24
|
+
getAllCatalogUsersFromAPI(): Promise<any>;
|
|
25
|
+
getAllCatalogLocationsFromAPI(): Promise<any>;
|
|
26
|
+
getAllCatalogGroupsFromAPI(): Promise<any>;
|
|
27
|
+
getGroupEntityFromAPI(group: string): Promise<any>;
|
|
28
|
+
getCatalogUserFromAPI(user: string): Promise<any>;
|
|
29
|
+
deleteUserEntityFromAPI(user: string): Promise<(() => string) | undefined>;
|
|
30
|
+
getCatalogGroupFromAPI(group: string): Promise<any>;
|
|
31
|
+
deleteGroupEntityFromAPI(group: string): Promise<() => string>;
|
|
32
|
+
scheduleEntityRefreshFromAPI(entity: string, kind: string, token: string): Promise<number>;
|
|
33
|
+
/**
|
|
34
|
+
* Fetches the UID of an entity by its name from the Backstage catalog.
|
|
35
|
+
*
|
|
36
|
+
* @param name - The name of the entity (e.g., 'hello-world-2').
|
|
37
|
+
* @returns The UID string if found, otherwise undefined.
|
|
38
|
+
*/
|
|
39
|
+
static getEntityUidByName(name: string): Promise<string | undefined>;
|
|
40
|
+
/**
|
|
41
|
+
* Deletes a location from the Backstage catalog by its UID.
|
|
42
|
+
*
|
|
43
|
+
* @param uid - The UID of the location to delete.
|
|
44
|
+
* @returns The status code of the delete operation.
|
|
45
|
+
*/
|
|
46
|
+
static deleteLocationByUid(uid: string): Promise<number>;
|
|
47
|
+
/**
|
|
48
|
+
* Fetches the UID of a Template entity by its name and namespace from the Backstage catalog.
|
|
49
|
+
*
|
|
50
|
+
* @param name - The name of the template entity (e.g., 'hello-world-2').
|
|
51
|
+
* @param namespace - The namespace of the template entity (default: 'default').
|
|
52
|
+
* @returns The UID string if found, otherwise undefined.
|
|
53
|
+
*/
|
|
54
|
+
static getTemplateEntityUidByName(name: string, namespace?: string): Promise<string | undefined>;
|
|
55
|
+
/**
|
|
56
|
+
* Deletes an entity location from the Backstage catalog by its ID.
|
|
57
|
+
*
|
|
58
|
+
* @param id - The ID of the entity to delete.
|
|
59
|
+
* @returns The status code of the delete operation.
|
|
60
|
+
*/
|
|
61
|
+
static deleteEntityLocationById(id: string): Promise<number>;
|
|
62
|
+
/**
|
|
63
|
+
* Registers a new location in the Backstage catalog.
|
|
64
|
+
*
|
|
65
|
+
* @param target - The target URL of the location to register.
|
|
66
|
+
* @returns The status code of the registration operation.
|
|
67
|
+
*/
|
|
68
|
+
static registerLocation(target: string): Promise<number>;
|
|
69
|
+
/**
|
|
70
|
+
* Fetches the ID of a location from the Backstage catalog by its target URL.
|
|
71
|
+
*
|
|
72
|
+
* @param target - The target URL of the location to search for.
|
|
73
|
+
* @returns The ID string if found, otherwise undefined.
|
|
74
|
+
*/
|
|
75
|
+
static getLocationIdByTarget(target: string): Promise<string | undefined>;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=api-helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-helper.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/api-helper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { request, expect } from "@playwright/test";
|
|
2
|
+
import { GITHUB_API_ENDPOINTS } from "./api-endpoints.js";
|
|
3
|
+
export class APIHelper {
|
|
4
|
+
static githubAPIVersion = "2022-11-28";
|
|
5
|
+
staticToken = "";
|
|
6
|
+
baseUrl = "";
|
|
7
|
+
useStaticToken = false;
|
|
8
|
+
static async githubRequest(method, url, body) {
|
|
9
|
+
const context = await request.newContext();
|
|
10
|
+
const options = {
|
|
11
|
+
method: method,
|
|
12
|
+
headers: {
|
|
13
|
+
Accept: "application/vnd.github+json",
|
|
14
|
+
Authorization: `Bearer ${process.env.GH_RHDH_QE_USER_TOKEN}`,
|
|
15
|
+
"X-GitHub-Api-Version": this.githubAPIVersion,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
if (body) {
|
|
19
|
+
options.data = body;
|
|
20
|
+
}
|
|
21
|
+
const response = await context.fetch(url, options);
|
|
22
|
+
return response;
|
|
23
|
+
}
|
|
24
|
+
static async getGithubPaginatedRequest(url, pageNo = 1, response = []) {
|
|
25
|
+
const fullUrl = `${url}&page=${pageNo}`;
|
|
26
|
+
const result = await this.githubRequest("GET", fullUrl);
|
|
27
|
+
const body = await result.json();
|
|
28
|
+
if (!Array.isArray(body)) {
|
|
29
|
+
throw new Error(`Expected array but got ${typeof body}: ${JSON.stringify(body)}`);
|
|
30
|
+
}
|
|
31
|
+
if (body.length === 0) {
|
|
32
|
+
return response;
|
|
33
|
+
}
|
|
34
|
+
response = [...response, ...body];
|
|
35
|
+
return await this.getGithubPaginatedRequest(url, pageNo + 1, response);
|
|
36
|
+
}
|
|
37
|
+
static async createGitHubRepo(owner, repoName) {
|
|
38
|
+
const response = await APIHelper.githubRequest("POST", GITHUB_API_ENDPOINTS.createRepo(owner), {
|
|
39
|
+
name: repoName,
|
|
40
|
+
private: false,
|
|
41
|
+
});
|
|
42
|
+
expect(response.status() === 201 || response.ok()).toBeTruthy();
|
|
43
|
+
}
|
|
44
|
+
static async createGitHubRepoWithFile(owner, repoName, filename, fileContent) {
|
|
45
|
+
// Create the repository
|
|
46
|
+
await APIHelper.createGitHubRepo(owner, repoName);
|
|
47
|
+
// Add the specified file
|
|
48
|
+
await APIHelper.createFileInRepo(owner, repoName, filename, fileContent, `Add ${filename} file`);
|
|
49
|
+
}
|
|
50
|
+
static async createFileInRepo(owner, repoName, filePath, content, commitMessage, branch = "main") {
|
|
51
|
+
const encodedContent = Buffer.from(content).toString("base64");
|
|
52
|
+
const response = await APIHelper.githubRequest("PUT", `${GITHUB_API_ENDPOINTS.contents(owner, repoName)}/${filePath}`, {
|
|
53
|
+
message: commitMessage,
|
|
54
|
+
content: encodedContent,
|
|
55
|
+
branch: branch,
|
|
56
|
+
});
|
|
57
|
+
expect(response.status() === 201 || response.ok()).toBeTruthy();
|
|
58
|
+
}
|
|
59
|
+
static async initCommit(owner, repo, branch = "main") {
|
|
60
|
+
const content = Buffer.from("This is the initial commit for the repository.").toString("base64");
|
|
61
|
+
const response = await APIHelper.githubRequest("PUT", `${GITHUB_API_ENDPOINTS.contents(owner, repo)}/initial-commit.md`, {
|
|
62
|
+
message: "Initial commit",
|
|
63
|
+
content: content,
|
|
64
|
+
branch: branch,
|
|
65
|
+
});
|
|
66
|
+
expect(response.status() === 201 || response.ok()).toBeTruthy();
|
|
67
|
+
}
|
|
68
|
+
static async deleteGitHubRepo(owner, repoName) {
|
|
69
|
+
await APIHelper.githubRequest("DELETE", GITHUB_API_ENDPOINTS.deleteRepo(owner, repoName));
|
|
70
|
+
}
|
|
71
|
+
static async mergeGitHubPR(owner, repoName, pullNumber) {
|
|
72
|
+
await APIHelper.githubRequest("PUT", GITHUB_API_ENDPOINTS.mergePR(owner, repoName, pullNumber));
|
|
73
|
+
}
|
|
74
|
+
static async getGitHubPRs(owner, repoName, state, paginated = false) {
|
|
75
|
+
const url = GITHUB_API_ENDPOINTS.pull(owner, repoName, state);
|
|
76
|
+
if (paginated) {
|
|
77
|
+
return await APIHelper.getGithubPaginatedRequest(url);
|
|
78
|
+
}
|
|
79
|
+
const response = await APIHelper.githubRequest("GET", url);
|
|
80
|
+
return response.json();
|
|
81
|
+
}
|
|
82
|
+
static async getfileContentFromPR(owner, repoName, pr, filename) {
|
|
83
|
+
const response = await APIHelper.githubRequest("GET", GITHUB_API_ENDPOINTS.pullFiles(owner, repoName, pr));
|
|
84
|
+
const fileRawUrl = (await response.json()).find((file) => file.filename === filename).raw_url;
|
|
85
|
+
const rawFileContent = await (await APIHelper.githubRequest("GET", fileRawUrl)).text();
|
|
86
|
+
return rawFileContent;
|
|
87
|
+
}
|
|
88
|
+
async getGuestToken() {
|
|
89
|
+
const context = await request.newContext();
|
|
90
|
+
const response = await context.post("/api/auth/guest/refresh");
|
|
91
|
+
expect(response.status()).toBe(200);
|
|
92
|
+
const data = await response.json();
|
|
93
|
+
return data.backstageIdentity.token;
|
|
94
|
+
}
|
|
95
|
+
async getGuestAuthHeader() {
|
|
96
|
+
const token = await this.getGuestToken();
|
|
97
|
+
const headers = {
|
|
98
|
+
Authorization: `Bearer ${token}`,
|
|
99
|
+
};
|
|
100
|
+
return headers;
|
|
101
|
+
}
|
|
102
|
+
async setStaticToken(token) {
|
|
103
|
+
this.useStaticToken = true;
|
|
104
|
+
this.staticToken = "Bearer " + token;
|
|
105
|
+
}
|
|
106
|
+
async setBaseUrl(url) {
|
|
107
|
+
this.baseUrl = url;
|
|
108
|
+
}
|
|
109
|
+
static async apiRequestWithStaticToken(method, url, staticToken, body) {
|
|
110
|
+
const context = await request.newContext();
|
|
111
|
+
const options = {
|
|
112
|
+
method: method,
|
|
113
|
+
headers: {
|
|
114
|
+
Accept: "application/json",
|
|
115
|
+
Authorization: `${staticToken}`,
|
|
116
|
+
},
|
|
117
|
+
...(body && { data: body }),
|
|
118
|
+
};
|
|
119
|
+
const response = await context.fetch(url, options);
|
|
120
|
+
return response;
|
|
121
|
+
}
|
|
122
|
+
async getAllCatalogUsersFromAPI() {
|
|
123
|
+
const url = `${this.baseUrl}/api/catalog/entities/by-query?orderField=metadata.name%2Casc&filter=kind%3Duser`;
|
|
124
|
+
const token = this.useStaticToken ? this.staticToken : "";
|
|
125
|
+
const response = await APIHelper.apiRequestWithStaticToken("GET", url, token);
|
|
126
|
+
return response.json();
|
|
127
|
+
}
|
|
128
|
+
async getAllCatalogLocationsFromAPI() {
|
|
129
|
+
const url = `${this.baseUrl}/api/catalog/entities/by-query?orderField=metadata.name%2Casc&filter=kind%3Dlocation`;
|
|
130
|
+
const token = this.useStaticToken ? this.staticToken : "";
|
|
131
|
+
const response = await APIHelper.apiRequestWithStaticToken("GET", url, token);
|
|
132
|
+
return response.json();
|
|
133
|
+
}
|
|
134
|
+
async getAllCatalogGroupsFromAPI() {
|
|
135
|
+
const url = `${this.baseUrl}/api/catalog/entities/by-query?orderField=metadata.name%2Casc&filter=kind%3Dgroup`;
|
|
136
|
+
const token = this.useStaticToken ? this.staticToken : "";
|
|
137
|
+
const response = await APIHelper.apiRequestWithStaticToken("GET", url, token);
|
|
138
|
+
return response.json();
|
|
139
|
+
}
|
|
140
|
+
async getGroupEntityFromAPI(group) {
|
|
141
|
+
const url = `${this.baseUrl}/api/catalog/entities/by-name/group/default/${group}`;
|
|
142
|
+
const token = this.useStaticToken ? this.staticToken : "";
|
|
143
|
+
const response = await APIHelper.apiRequestWithStaticToken("GET", url, token);
|
|
144
|
+
return response.json();
|
|
145
|
+
}
|
|
146
|
+
async getCatalogUserFromAPI(user) {
|
|
147
|
+
const url = `${this.baseUrl}/api/catalog/entities/by-name/user/default/${user}`;
|
|
148
|
+
const token = this.useStaticToken ? this.staticToken : "";
|
|
149
|
+
const response = await APIHelper.apiRequestWithStaticToken("GET", url, token);
|
|
150
|
+
return response.json();
|
|
151
|
+
}
|
|
152
|
+
async deleteUserEntityFromAPI(user) {
|
|
153
|
+
const r = await this.getCatalogUserFromAPI(user);
|
|
154
|
+
if (!r.metadata || !r.metadata.uid) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const url = `${this.baseUrl}/api/catalog/entities/by-uid/${r.metadata.uid}`;
|
|
158
|
+
const token = this.useStaticToken ? this.staticToken : "";
|
|
159
|
+
const response = await APIHelper.apiRequestWithStaticToken("DELETE", url, token);
|
|
160
|
+
return response.statusText;
|
|
161
|
+
}
|
|
162
|
+
async getCatalogGroupFromAPI(group) {
|
|
163
|
+
const url = `${this.baseUrl}/api/catalog/entities/by-name/group/default/${group}`;
|
|
164
|
+
const token = this.useStaticToken ? this.staticToken : "";
|
|
165
|
+
const response = await APIHelper.apiRequestWithStaticToken("GET", url, token);
|
|
166
|
+
return response.json();
|
|
167
|
+
}
|
|
168
|
+
async deleteGroupEntityFromAPI(group) {
|
|
169
|
+
const r = await this.getCatalogGroupFromAPI(group);
|
|
170
|
+
const url = `${this.baseUrl}/api/catalog/entities/by-uid/${r.metadata.uid}`;
|
|
171
|
+
const token = this.useStaticToken ? this.staticToken : "";
|
|
172
|
+
const response = await APIHelper.apiRequestWithStaticToken("DELETE", url, token);
|
|
173
|
+
return response.statusText;
|
|
174
|
+
}
|
|
175
|
+
async scheduleEntityRefreshFromAPI(entity, kind, token) {
|
|
176
|
+
const url = `${this.baseUrl}/api/catalog/refresh`;
|
|
177
|
+
const reqBody = { entityRef: `${kind}:default/${entity}` };
|
|
178
|
+
const responseRefresh = await APIHelper.apiRequestWithStaticToken("POST", url, token, reqBody);
|
|
179
|
+
return responseRefresh.status();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Fetches the UID of an entity by its name from the Backstage catalog.
|
|
183
|
+
*
|
|
184
|
+
* @param name - The name of the entity (e.g., 'hello-world-2').
|
|
185
|
+
* @returns The UID string if found, otherwise undefined.
|
|
186
|
+
*/
|
|
187
|
+
static async getEntityUidByName(name) {
|
|
188
|
+
const baseUrl = process.env.BASE_URL;
|
|
189
|
+
const url = `${baseUrl}/api/catalog/entities/by-name/template/default/${name}`;
|
|
190
|
+
const context = await request.newContext();
|
|
191
|
+
const response = await context.get(url);
|
|
192
|
+
if (response.status() !== 200) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
const data = await response.json();
|
|
196
|
+
return data?.metadata?.uid;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Deletes a location from the Backstage catalog by its UID.
|
|
200
|
+
*
|
|
201
|
+
* @param uid - The UID of the location to delete.
|
|
202
|
+
* @returns The status code of the delete operation.
|
|
203
|
+
*/
|
|
204
|
+
static async deleteLocationByUid(uid) {
|
|
205
|
+
const baseUrl = process.env.BASE_URL;
|
|
206
|
+
const url = `${baseUrl}/api/catalog/locations/${uid}`;
|
|
207
|
+
const context = await request.newContext();
|
|
208
|
+
const response = await context.delete(url);
|
|
209
|
+
return response.status();
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Fetches the UID of a Template entity by its name and namespace from the Backstage catalog.
|
|
213
|
+
*
|
|
214
|
+
* @param name - The name of the template entity (e.g., 'hello-world-2').
|
|
215
|
+
* @param namespace - The namespace of the template entity (default: 'default').
|
|
216
|
+
* @returns The UID string if found, otherwise undefined.
|
|
217
|
+
*/
|
|
218
|
+
static async getTemplateEntityUidByName(name, namespace = "default") {
|
|
219
|
+
const baseUrl = process.env.BASE_URL;
|
|
220
|
+
const url = `${baseUrl}/api/catalog/locations/by-entity/template/${namespace}/${name}`;
|
|
221
|
+
const context = await request.newContext();
|
|
222
|
+
const response = await context.get(url);
|
|
223
|
+
if (response.status() === 200) {
|
|
224
|
+
const data = await response.json();
|
|
225
|
+
return data?.metadata?.uid;
|
|
226
|
+
}
|
|
227
|
+
if (response.status() === 404) {
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Deletes an entity location from the Backstage catalog by its ID.
|
|
234
|
+
*
|
|
235
|
+
* @param id - The ID of the entity to delete.
|
|
236
|
+
* @returns The status code of the delete operation.
|
|
237
|
+
*/
|
|
238
|
+
static async deleteEntityLocationById(id) {
|
|
239
|
+
const baseUrl = process.env.BASE_URL;
|
|
240
|
+
const url = `${baseUrl}/api/catalog/locations/${id}`;
|
|
241
|
+
const context = await request.newContext();
|
|
242
|
+
const response = await context.delete(url);
|
|
243
|
+
return response.status();
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Registers a new location in the Backstage catalog.
|
|
247
|
+
*
|
|
248
|
+
* @param target - The target URL of the location to register.
|
|
249
|
+
* @returns The status code of the registration operation.
|
|
250
|
+
*/
|
|
251
|
+
static async registerLocation(target) {
|
|
252
|
+
const baseUrl = process.env.BASE_URL;
|
|
253
|
+
const url = `${baseUrl}/api/catalog/locations`;
|
|
254
|
+
const context = await request.newContext();
|
|
255
|
+
const response = await context.post(url, {
|
|
256
|
+
data: {
|
|
257
|
+
type: "url",
|
|
258
|
+
target,
|
|
259
|
+
},
|
|
260
|
+
headers: {
|
|
261
|
+
"Content-Type": "application/json",
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
return response.status();
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Fetches the ID of a location from the Backstage catalog by its target URL.
|
|
268
|
+
*
|
|
269
|
+
* @param target - The target URL of the location to search for.
|
|
270
|
+
* @returns The ID string if found, otherwise undefined.
|
|
271
|
+
*/
|
|
272
|
+
static async getLocationIdByTarget(target) {
|
|
273
|
+
const baseUrl = process.env.BASE_URL;
|
|
274
|
+
const url = `${baseUrl}/api/catalog/locations`;
|
|
275
|
+
const context = await request.newContext();
|
|
276
|
+
const response = await context.get(url);
|
|
277
|
+
if (response.status() !== 200) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
const data = await response.json();
|
|
281
|
+
// data is expected to be an array of objects with a 'data' property
|
|
282
|
+
const location = (Array.isArray(data) ? data : []).find((entry) => entry?.data?.target === target);
|
|
283
|
+
return location?.data?.id;
|
|
284
|
+
}
|
|
285
|
+
}
|