Skip to content

Commit

Permalink
Vamc 15446 spike vamc police data page (#1753)
Browse files Browse the repository at this point in the history
choice between files getting libcurl and URLs getting node-fetch with ability to proxy (making room for using views from drupal via HTTP).
  • Loading branch information
eselkin authored Nov 1, 2023
1 parent 4a80a07 commit 7b9d00a
Show file tree
Hide file tree
Showing 16 changed files with 874 additions and 47 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
"cy-mobile-commands": "^0.3.0",
"cypress": "^10.8.0",
"cypress-axe": "^1.0.0",
"cypress-downloadfile": "^1.2.3",
"cypress-multi-reporters": "^1.5.0",
"cypress-plugin-tab": "^1.0.5",
"cypress-real-events": "^1.7.1",
Expand Down Expand Up @@ -210,6 +211,7 @@
"aws-sdk": "^2.1441.0",
"blob-polyfill": "^4.0.20200601",
"core-js": "^3.17.3",
"csvtojson": "^2.0.10",
"diff2html": "^3.4.11",
"dotenv": "^10.0.0",
"express": "^4.17.1",
Expand All @@ -229,6 +231,7 @@
"metalsmith-sitemap": "^1.0.0",
"moment": "^2.29.2",
"moment-timezone": "^0.5.33",
"node-libcurl": "^3.0.0",
"number-to-words": "^1.2.4",
"raven": "^2.6.4",
"react": "^16.13.1",
Expand Down
15 changes: 14 additions & 1 deletion src/platform/testing/e2e/cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const fs = require('fs-extra');
const path = require('path');
const table = require('table').table;
const { table } = require('table');
const { downloadFile } = require('cypress-downloadfile/lib/addPlugin');

const tableConfig = {
columns: {
Expand Down Expand Up @@ -76,5 +77,17 @@ module.exports = on => {

return dir;
},
downloadFile,
deleteFileOrDir(fileOrDirName) {
if (fs.existsSync(fileOrDirName)) {
fs.rmSync(fileOrDirName, {
recursive: true,
});
}
return null;
},
fileOrDirExists(fileOrDirName) {
return fs.existsSync(fileOrDirName);
},
});
};
9 changes: 9 additions & 0 deletions src/platform/testing/e2e/cypress/support/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,12 @@ import './hasCount';
import './keyboard';
import 'cy-mobile-commands';
import 'cypress-wait-until';
import 'cypress-downloadfile/lib/downloadFileCommand';

Cypress.Commands.add('deleteFileOrDir', fileOrDirName => {
return cy.task('deleteFileOrDir', fileOrDirName);
});

Cypress.Commands.add('fileOrDirExists', fileOrDirName => {
return cy.task('fileOrDirExists', fileOrDirName);
});
39 changes: 38 additions & 1 deletion src/site/stages/build/drupal/metalsmith-drupal.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ function pipeDrupalPagesIntoMetalsmith(contentData, files) {
const pages = contentData.data.nodeQuery.entities.filter(
e => e && Object.keys(e).length,
);

for (const page of pages) {
const {
entityUrl: { path: drupalUrl },
Expand Down Expand Up @@ -355,6 +354,44 @@ function getDrupalContent(buildOptions) {
let drupalData = null;
try {
drupalData = await loadDrupal(buildOptions);

// Add to drupal Sidebars at the bottom of the ABOUT VA/ABOUT LOVELL section the va-police link
// TODO: Remove if CMS goes forward with creating VA Police Pages
// const facilitySidebarQueries = Object.keys(drupalData.data).filter(k =>
// k.includes('FacilitySidebarQuery'),
// );
// FOR EACH FACILITY SIDEBAR QUERY, UNDER THE ABOUT XYZ SECTION ADD "VA Police" link
// for (const query of facilitySidebarQueries) {
// for (let i = 0; i < drupalData.data[query].links.length; i += 1) {
// const j = drupalData.data[query].links[i].links.findIndex(l => {
// return (
// l.label.toUpperCase().startsWith('ABOUT VA') ||
// l.label.toUpperCase().startsWith('ABOUT LOVELL')
// );
// });
// if (j !== -1) {
// console.log(
// `Adding VA Police to: ${drupalData.data[query].links[0].url.path}/va-police`,
// );
// drupalData.data[query].links[i].links[j].links.push({
// expanded: false,
// description: 'Police data',
// label: 'VA Police',
// links: [],
// url: {
// path: `${drupalData.data[query].links[0].url.path}/va-police`,
// },
// entity: {
// linkedEntity: {
// entityPublished: true,
// moderationState: 'published',
// },
// },
// });
// }
// }
// }

drupalData = convertDrupalFilesToLocal(drupalData, files);

await loadCachedDrupalFiles(buildOptions, files);
Expand Down
5 changes: 3 additions & 2 deletions src/site/stages/build/drupal/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function paginatePages(page, files, field, layout, ariaLabel, perPage) {
if (page[pageField]) {
const pagedEntities = _.chunk(page[pageField].entities, perPage);

for (let pageNum = 0; pageNum < pagedEntities.length; pageNum++) {
for (let pageNum = 0; pageNum < pagedEntities.length; pageNum += 1) {
let pagedPage = { ...page };

if (pageNum > 0) {
Expand Down Expand Up @@ -98,7 +98,7 @@ function paginatePages(page, files, field, layout, ariaLabel, perPage) {
start = pageNum;
}
}
for (let num = start; num < start + length; num++) {
for (let num = start; num < start + length; num += 1) {
innerPages.push({
href:
num === pageNum
Expand Down Expand Up @@ -356,6 +356,7 @@ function compilePage(page, contentData) {
const facilitySidebarNavItems = {
facilitySidebar: getFacilitySidebar(page, contentData),
};

const outreachSidebarNavItems = { outreachSidebar: outreachSidebarNav };
const alertItems = { alert: alertsItem };

Expand Down
17 changes: 16 additions & 1 deletion src/site/stages/build/drupal/static-data-files/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ const {
postProcess: postProcessVamcFacilitySupplementalStatus,
} = require('./vamcFacilitySupplementalStatus');

const {
query: queryVAPoliceData,
postProcess: postProcessVAPoliceData,
} = require('./vaPoliceData');

const DATA_FILE_PATH = 'data/cms';

/**
* {
* description: String used in build log,
* filename: File will be generated at `${DATA_FILE_PATH}/${filename}`,
* queryType: 'graphql' (default); aim to eventually support jsonapi
* query: String defining the query to be run,
* query: String|Object defining the query to be run,
* postProcess: Callback function to apply post-query processing on query result,
* }
*/
Expand All @@ -24,14 +29,24 @@ const DATA_FILES = [
description: 'VAMC EHR System',
filename: 'vamc-ehr.json',
query: queryVamcEhrSystem,
queryType: 'graphql',
postProcess: postProcessVamcEhrSystem,
},
{
description: 'VAMC Facility Supplemental Status',
filename: 'vamc-facility-supplemental-status.json',
query: queryVamcFacilitySupplementalStatus,
queryType: 'graphql',
postProcess: postProcessVamcFacilitySupplementalStatus,
},
{
description: 'VAMC Police Data',
filename: 'vamc-police.json',
queryType: 'curl',
// This looks like a highly complicated route to get the file data, but it is generalizeable to all CURL requests, not just file URLs
query: queryVAPoliceData,
postProcess: postProcessVAPoliceData,
},
];

module.exports = {
Expand Down
73 changes: 73 additions & 0 deletions src/site/stages/build/drupal/static-data-files/fetchApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const fetch = require('node-fetch');
const SocksProxyAgent = require('socks-proxy-agent');
const syswidecas = require('syswide-cas');
const { curly } = require('node-libcurl');

const { Response } = fetch;

// Uses fetch to make a request to a non-file URL or uses node-libcurl to make a request to a file URL
// Returns a node-fetch response in all cases
async function fetchWrapper(url, options) {
if (url.startsWith('file:')) {
const { data } = await curly.get(url);
return new Response(data, { status: 200, statusText: 'OK', url });
}
return fetch(url, options);
}

function encodeCredentials({ user, password }) {
const credentials = `${user}:${password}`;
return Buffer.from(credentials).toString('base64');
}

function getCurlClient(buildOptions, _clientOptionsArg = { verbose: true }) {
const buildArgs = {
user: buildOptions['drupal-user'],
password: buildOptions['drupal-password'],
maxParallelRequests: buildOptions['drupal-max-parallel-requests'],
};

Object.keys(buildArgs).forEach(key => {
if (!buildArgs[key]) delete buildArgs[key];
});

const { user, password } = buildArgs;

const encodedCredentials = encodeCredentials({ user, password });
const headers = {
Authorization: `Basic ${encodedCredentials}`,
'Content-Type': 'application/json',
};
const agent = new SocksProxyAgent('socks://127.0.0.1:2001');
return {
// We have to point to aws urls on Jenkins, so the only
// time we'll be using cms.va.gov addresses is locally,
// when we need a proxy

async proxyFetch(url, options = { headers }) {
const usingProxy =
/^https?:\/\/.*\.cms\.va\.gov\//.test(url) &&
!buildOptions['no-drupal-proxy'];

if (usingProxy) {
// addCAs() is here because VA uses self-signed certificates with a
// non-globally trusted Root Certificate Authority and we need to
// tell our code to trust it, otherwise we get self-signed certificate errors.
syswidecas.addCAs([
'certs/VA-Internal-S2-RCA1-v1.pem',
'certs/VA-Internal-S2-RCA2.pem',
]);
}

return fetchWrapper(
url,
// eslint-disable-next-line prefer-object-spread
Object.assign({}, options, {
agent: usingProxy ? agent : undefined,
}),
);
},
};
}

module.exports = getCurlClient;
Loading

0 comments on commit 7b9d00a

Please sign in to comment.