Skip to content

Commit

Permalink
Merge pull request #856 from CobyPear/DB-6705-fix-dkhc-next-drupal-de…
Browse files Browse the repository at this point in the history
…coupled-router-check

DB-6705: Fix Drupal decoupled-router validation
  • Loading branch information
backlineint authored Sep 6, 2023
2 parents 6d195cd + 15d7593 commit 2103033
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-boxes-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'create-pantheon-decoupled-kit': patch
---

Bump `@pantheon-systems/decoupled-kit-health-check`
6 changes: 6 additions & 0 deletions .changeset/mighty-keys-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@pantheon-systems/decoupled-kit-health-check': patch
---

[NextDruaplHealthCheck] Fixed potential false negatives when checking for
decoupled router
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { rest } from 'msw';
import { http } from 'msw';

const endpoint = 'https://drupal.test';
const umamiEndpoint = 'https://umami.drupal.test';
const invalidEndpoint = 'https://invalid.drupal.test';

const jsonapiIndexHandler = rest.get(
const jsonapiIndexHandler = http.get(
`${endpoint}/jsonapi`,
() => new Response(null, { status: 200 }),
);

const invalidJsonapiIndexHandler = rest.get(
const invalidJsonapiIndexHandler = http.get(
`${invalidEndpoint}/jsonapi`,
() => new Response(null, { status: 404 }),
);

const authHandler = rest.post(`${endpoint}/oauth/token`, ({ request }) => {
const authHandler = http.post(`${endpoint}/oauth/token`, ({ request }) => {
const url = new URL(request.url);
if (
url.searchParams.get('client_id') &&
Expand All @@ -30,42 +30,64 @@ const authHandler = rest.post(`${endpoint}/oauth/token`, ({ request }) => {
}
});

const decoupledRouterHandler = rest.get(
const decoupledRouterHandler = http.get(
`${endpoint}/router/translate-path`,
() => new Response(null, { status: 200 }),
() =>
new Response(
JSON.stringify({
message:
'unable to translate empty path. Please send a ?path query string parameter with your request.',
}),
{ status: 404 },
),
);

const umamiDecoupledRouterHandler = rest.get(
const umamiDecoupledRouterHandler = http.get(
`${umamiEndpoint}/router/translate-path`,
() => new Response(null, { status: 200 }),
() =>
new Response(
JSON.stringify({
message:
'Unable to translate empty path. Please send a ?path query string parameter with your request.',
}),
{ status: 404 },
),
);

const invalidDecoupledRouterHandler = rest.get(
const invalidDecoupledRouterHandler = http.get(
`${invalidEndpoint}/router/translate-path`,
() => new Response(null, { status: 404 }),
() =>
new Response(
'<html><body><h1>Some html to mock the 404 response without decoupled router</h1></body></html>',
{ status: 404 },
),
);

const defaultLanguageSettingsHandler = rest.get(
const defaultLanguageSettingsHandler = http.get(
`${endpoint}/jsonapi/configurable_language/configurable_language`,
() => new Response(null, { status: 404 }),
);
const invalidLanguageSettingsHandler = http.get(
`${invalidEndpoint}/jsonapi/configurable_language/configurable_language`,
() => new Response(null, { status: 404 }),
);

const umamiLanguageSettingsHandler = rest.get(
const umamiLanguageSettingsHandler = http.get(
`${umamiEndpoint}/jsonapi/configurable_language/configurable_language`,
() => new Response(null, { status: 200 }),
);

const menuItemHandler = rest.get(
const menuItemHandler = http.get(
`${endpoint}/jsonapi/menu_items/footer`,
() => new Response(null, { status: 200 }),
);

const invalidMenuItemHandler = rest.get(
const invalidMenuItemHandler = http.get(
`${invalidEndpoint}/jsonapi/menu_items/footer`,
() => new Response(null, { status: 404 }),
);

const previewHandler = rest.get(`${endpoint}/node/1/preview`, ({ request }) => {
const previewHandler = http.get(`${endpoint}/node/1/preview`, ({ request }) => {
if (request.headers.get('Authorization')?.endsWith('Bearer')) {
return new Response(null, { status: 401 });
} else {
Expand All @@ -76,6 +98,7 @@ const previewHandler = rest.get(`${endpoint}/node/1/preview`, ({ request }) => {
export const drupalRequestHandlers = [
jsonapiIndexHandler,
invalidJsonapiIndexHandler,
invalidLanguageSettingsHandler,
authHandler,
decoupledRouterHandler,
umamiDecoupledRouterHandler,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { rest } from 'msw';
import { http } from 'msw';

const endpoint = 'https://wordpress.test/wp/graphql';
const invalidEndpoint = 'https://invalid.wordpress.test/wp/graphql';

const validResponses = rest.get(`${endpoint}`, ({ request }) => {
const validResponses = http.get(`${endpoint}`, ({ request }) => {
const url = new URL(request.url);
const authQuery = /* graphql */ `query LatestPostsQuery {
posts(where: { status: PRIVATE }) {
Expand Down Expand Up @@ -177,13 +177,13 @@ const validResponses = rest.get(`${endpoint}`, ({ request }) => {
}
});

const invalidResponses = rest.get(`${invalidEndpoint}`, () => {
const invalidResponses = http.get(`${invalidEndpoint}`, () => {
return new Response(JSON.stringify({ errors: [{ message: 'Not found' }] }), {
status: 404,
});
});

const noWPGatsbyPlugin = rest.get(
const noWPGatsbyPlugin = http.get(
`https://wordpress.test.no-plugin/wp/graphql`,
({ request }) => {
const url = new URL(request.url);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,27 @@ describe('NextDrupalHealthCheck', () => {
const result = logSpy.mock.calls.map(([call]) => call);
expect(result).toMatchSnapshot();
});
it('should show a helpful error message if the decoupled router is not available', async ({
logSpy,
}) => {
process.env['PANTHEON_CMS_ENDPOINT'] = 'invalid.drupal.test';
const HC = new NextDrupalHealthCheck({ env: process.env });
vi.spyOn(HC, 'validateEndpoint').mockImplementationOnce(async () => {
console.log('Validating CMS endpoint...');
HC.log.success('PANTHEON_CMS_ENDPOINT is valid!');
return HC;
});
vi.spyOn(HC, 'validateMenu').mockImplementationOnce(async () => {
console.log('Validating Menu Item endpoint...');
HC.log.success('Menu Items endpoint is valid!');
return HC;
});
await HC.validateEndpoint()
.then((hc) => hc.validateMenu())
.then((hc) => hc.validateRouter())
.catch((err) => console.log(err.message));

const result = logSpy.mock.calls.map(([call]) => call);
expect(result).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ exports[`NextDrupalHealthCheck > should pass for a valid BACKEND_URL and invalid
"|__✅ BACKEND_URL is valid!",
"Validating Menu Item endpoint...",
"|__✅ Menu Items endpoint is valid!",
"Validating Decoupled Router module...",
"|__✅ Decoupled Router is valid!",
"Validating authentication...",
"|__💡 CLIENT_ID is required for preview but is not set.",
Expand All @@ -30,6 +31,7 @@ exports[`NextDrupalHealthCheck > should pass for a valid PANTHEON_CMS_ENDPOINT a
"|__✅ PANTHEON_CMS_ENDPOINT is valid!",
"Validating Menu Item endpoint...",
"|__✅ Menu Items endpoint is valid!",
"Validating Decoupled Router module...",
"|__✅ Decoupled Router is valid!",
"Validating authentication...",
"|__💡 CLIENT_ID is required for preview but is not set.",
Expand All @@ -51,6 +53,7 @@ exports[`NextDrupalHealthCheck > should pass for a valid backend and valid auth
"|__✅ PANTHEON_CMS_ENDPOINT is valid!",
"Validating Menu Item endpoint...",
"|__✅ Menu Items endpoint is valid!",
"Validating Decoupled Router module...",
"|__✅ Decoupled Router is valid!",
"Validating authentication...",
"|__✅ Auth is valid!",
Expand All @@ -69,6 +72,7 @@ exports[`NextDrupalHealthCheck > should pass for a valid backend and valid auth
"|__✅ PANTHEON_CMS_ENDPOINT is valid!",
"Validating Menu Item endpoint...",
"|__✅ Menu Items endpoint is valid!",
"Validating Decoupled Router module...",
"|__✅ Decoupled Router is valid!",
"Validating authentication...",
"|__✅ Auth is valid!",
Expand All @@ -89,3 +93,19 @@ exports[`NextDrupalHealthCheck > should show a helpful error message if the back
Check that invalid.drupal.test is valid and provides a 200 response",
]
`;

exports[`NextDrupalHealthCheck > should show a helpful error message if the decoupled router is not available 1`] = `
[
"No .env* file found, assuming production environment.",
"Checking for PANTHEON_CMS_ENDPOINT or BACKEND_URL...",
"|__✅ PANTHEON_CMS_ENDPOINT is set!",
"Validating CMS endpoint...",
"|__✅ PANTHEON_CMS_ENDPOINT is valid!",
"Validating Menu Item endpoint...",
"|__✅ Menu Items endpoint is valid!",
"Validating Decoupled Router module...",
"|__❌ Decoupled Router not detected for PANTHEON_CMS_ENDPOINT.
Check that invalid.drupal.test is valid and provides a 200 response.
Also ensure that the Decoupled Router module is enabled.",
]
`;
2 changes: 1 addition & 1 deletion packages/decoupled-kit-health-check/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@types/node": "^18.16.14",
"@vitest/coverage-v8": "^0.33.0",
"esbuild": "^0.18.11",
"msw": "next",
"msw": "0.0.0-fetch.rc-17",
"rimraf": "^5.0.1",
"vitest": "^0.33.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,22 +131,24 @@ export class NextDrupalHealthCheck extends DrupalHealthCheck {
});
}
async validateRouter() {
console.log('Validating Decoupled Router module...');
await this.checkForUmami();
const cmsEndpoint = this.getURL();
cmsEndpoint.pathname = '/router/translate-path';
cmsEndpoint.searchParams.set('format', '_json');
this.hasUmami
? cmsEndpoint.searchParams.set(
'path',
'articles/lets-hear-it-for-carrots',
)
: cmsEndpoint.searchParams.set('path', 'articles/example-article');

const isDecoupledRouterValid = await this.checkFor200(cmsEndpoint);
if (isDecoupledRouterValid) {
this.log.success('Decoupled Router is valid!');
return this;
try {
const res = await fetch(cmsEndpoint);
const { message } = (await res?.json()) as { message?: string };
// If request to /router/translate-path with no query param results in a 404 with a message, the router module is installed.
// a 404 with no message means the router module is not installed.
if (message) {
this.log.success('Decoupled Router is valid!');
return this;
}
} catch (error) {
void error;
}

throw new DecoupledRouterError({
endpoint: this.getURL().host,
endpointType: this.envVar,
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 2103033

Please sign in to comment.