Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jk/cumulus 3806 2 #3811

Open
wants to merge 22 commits into
base: jk/CUMULUS-3806
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/api/endpoints/reconciliation-reports.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,12 @@ async function deleteReport(req, res) {
* Creates a new report
*
* @param {Object} req - express request object
* @param {RecReportParams} req.body
* @param {Object} res - express response object
* @returns {Promise<Object>} the promise of express response object
*/
async function createReport(req, res) {
/** @type NormalizedRecReportParams */
let validatedInput;
try {
validatedInput = normalizeEvent(req.body);
Expand Down
360 changes: 231 additions & 129 deletions packages/api/lambdas/create-reconciliation-report.js

Large diffs are not rendered by default.

24 changes: 8 additions & 16 deletions packages/api/lambdas/reports/orca-backup-reconciliation-report.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ const ORCASearchCatalogQueue = require('../../lib/ORCASearchCatalogQueue');
* @property {string} reason
*/

/** @typedef { import('@cumulus/db').PostgresGranuleRecord } PostgresGranuleRecord */
/**
* @typedef { import('@cumulus/db').PostgresGranuleRecord } PostgresGranuleRecord
* @typedef {import('../../lib/types').NormalizedRecReportParams } NormalizedRecReportParams
*/

/**
* @typedef {Object} GranuleReport
* @property {boolean} ok
Expand Down Expand Up @@ -98,8 +102,7 @@ const fileConflictTypes = {
/**
* Fetch orca configuration for all or specified collections
*
* @param {Object} recReportParams - input report params
* @param {String[]} recReportParams.collectionIds - array of collectionIds
* @param {NormalizedRecReportParams} recReportParams - input report params
* @returns {Promise<CollectionConfig>} - list of { collectionId, orca configuration }
*/
async function fetchCollectionsConfig(recReportParams) {
Expand Down Expand Up @@ -354,8 +357,7 @@ async function addGranuleToReport({
/**
* Compare the granule holdings in Cumulus with ORCA
*
* @param {Object} recReportParams - input report params
* @param {String[]} recReportParams.collectionIds - array of collectionIds
* @param {NormalizedRecReportParams} recReportParams - input report params
* @returns {Promise<Object>} an object with the okCount, onlyInCumulus, onlyInOrca
* and withConfilcts
*/
Expand Down Expand Up @@ -496,17 +498,7 @@ async function orcaReconciliationReportForGranules(recReportParams) {
/**
* Create an ORCA Backup Reconciliation report and save it to S3
*
* @param {Object} recReportParams - params
* @param {Object} recReportParams.collectionIds - array of collectionIds
* @param {Object} recReportParams.providers - array of providers
* @param {Object} recReportParams.granuleIds - array of granuleIds
* @param {Object} recReportParams.reportType - the report type
* @param {moment} recReportParams.createStartTime - when the report creation was begun
* @param {moment} recReportParams.endTimestamp - ending report datetime ISO Timestamp
* @param {string} recReportParams.reportKey - the s3 report key
* @param {string} recReportParams.stackName - the name of the CUMULUS stack
* @param {moment} recReportParams.startTimestamp - beginning report datetime ISO timestamp
* @param {string} recReportParams.systemBucket - the name of the CUMULUS system bucket
* @param {NormalizedRecReportParams} recReportParams - params
* @returns {Promise<void>} a Promise that resolves when the report has been
* uploaded to S3
*/
Expand Down
47 changes: 34 additions & 13 deletions packages/api/lib/reconciliationReport.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//@ts-check

'use strict';

const isEqual = require('lodash/isEqual');
Expand All @@ -9,6 +11,27 @@ const Logger = require('@cumulus/logger');

const log = new Logger({ sender: '@api/lambdas/create-reconciliation-report' });

/** @typedef {import('../lib/types').RecReportParams } RecReportParams */
/** @typedef {import('../lib/types').NormalizedRecReportParams } NormalizedRecReportParams */

/**
* @typedef {Object} ReportHeader
* @property {string | undefined} collectionId - The collection ID.
* @property {string | string[] | undefined} collectionIds - The collection IDs.
* @property {string | undefined} createEndTime - The end time of the report creation.
* @property {string} createStartTime - The start time of the report creation.
* @property {string | undefined} error - Any error that occurred.
* @property {string | undefined} granuleId - The granule ID.
* @property {string | string[] | undefined} granuleIds - The granule IDs.
* @property {string | string[] | undefined} provider - The provider.
* @property {string | string[] | undefined} providers - The providers.
* @property {string | undefined} location - The location.
* @property {string | undefined} reportEndTime - The end time of the report.
* @property {string | undefined} reportStartTime - The start time of the report.
* @property {string} reportType - The type of the report.
* @property {string} status - The status of the report.
*/

/**
* Extra search params to add to the cmrGranules searchConceptQueue
*
Expand All @@ -23,6 +46,7 @@ function cmrGranuleSearchParams(recReportParams) {
return [];
}

// TODO: remove
/**
* Prepare a list of collectionIds into an _id__in object
*
Expand All @@ -36,7 +60,7 @@ function searchParamsForCollectionIdArray(collectionIds) {

/**
* @param {string} dateable - any input valid for a JS Date contstructor.
* @returns {number} - primitive value of input date string or undefined, if
* @returns {number | undefined} - primitive value of input date string or undefined, if
* input string not convertable.
*/
function dateToValue(dateable) {
Expand All @@ -49,6 +73,7 @@ function dateStringToDateOrNull(dateable) {
return !Number.isNaN(date.valueOf()) ? date : undefined;
}

//TODO Verify this function is still needed
/**
*
* @param {Object} params - request params to convert to Elasticsearch params
Expand All @@ -74,9 +99,9 @@ function convertToESCollectionSearchParams(params) {
* @param {[Object]} params.collectionIds - List containing single Collection object
* multiple or no collections will result in a
* search object without a collection object
* @param {moment} params.endTimestamp - ending report datetime ISO Timestamp
* @param {moment} params.startTimestamp - beginning report datetime ISO timestamp
* @returns {[Object]} - array of objects of desired
* @param {string} params.endTimestamp - ending report datetime ISO Timestamp
* @param {string} params.startTimestamp - beginning report datetime ISO timestamp
* @returns {Object[]} - array of objects of desired
* parameters formatted for database collection
* search
*/
Expand Down Expand Up @@ -121,7 +146,7 @@ function convertToESGranuleSearchParams(params) {
/**
* Convert reconciliation report parameters to PostgreSQL database search params.
*
* @param {Object} params - request params to convert to database params
* @param {NormalizedRecReportParams} params - request params to convert to database params
* @returns {Object} object of desired parameters formated for database granule search
*/
function convertToDBGranuleSearchParams(params) {
Expand Down Expand Up @@ -152,7 +177,7 @@ function convertToDBGranuleSearchParams(params) {
* convert to es search parameters using createdAt for report time range
*
* @param {Object} params - request params to convert to Elasticsearch params
* @returns {Object} object of desired parameters formated for Elasticsearch.
* @returns {Object} object of desired parameters formatted for Elasticsearch.
*/
function convertToESGranuleSearchParamsWithCreatedAtRange(params) {
const searchParamsWithUpdatedAt = convertToESGranuleSearchParams(params);
Expand All @@ -167,7 +192,7 @@ function convertToESGranuleSearchParamsWithCreatedAtRange(params) {
/**
*
* @param {Object} params - request params to convert to orca params
* @returns {Object} object of desired parameters formated for orca
* @returns {Object} object of desired parameters formatted for orca
*/
function convertToOrcaGranuleSearchParams(params) {
const { collectionIds, granuleIds, providers, startTimestamp, endTimestamp } = params;
Expand All @@ -183,12 +208,8 @@ function convertToOrcaGranuleSearchParams(params) {
/**
* create initial report header
*
* @param {Object} recReportParams - params
* @param {Object} recReportParams.reportType - the report type
* @param {moment} recReportParams.createStartTime - when the report creation was begun
* @param {moment} recReportParams.endTimestamp - ending report datetime ISO Timestamp
* @param {moment} recReportParams.startTimestamp - beginning report datetime ISO timestamp
* @returns {Object} report header
* @param {NormalizedRecReportParams} recReportParams - params
* @returns {ReportHeader} report header
*/
function initialReportHeader(recReportParams) {
const {
Expand Down
66 changes: 36 additions & 30 deletions packages/api/lib/reconciliationReport/normalizeEvent.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
//@ts-check

'use strict';

/*eslint prefer-const: ["error", {"destructuring": "all"}]*/
const isString = require('lodash/isString');
const { removeNilProperties } = require('@cumulus/common/util');
const { InvalidArgument } = require('@cumulus/errors');

/**
* @typedef {import('../types').RecReportParams } RecReportParams
* @typedef {import('../types').NormalizedRecReportParams } NormalizedRecReportParams
*/

/**
* ensures input reportType can be handled by the lambda code.
*
* @param {string} reportType
* @returns {undefined} - if reportType is valid
* @returns {void} - if reportType is valid
* @throws {InvalidArgument} - otherwise
*/
function validateReportType(reportType) {
Expand All @@ -31,7 +38,7 @@ function validateReportType(reportType) {
/**
* Convert input to an ISO timestamp.
* @param {any} dateable - any type convertable to JS Date
* @returns {string} - date formated as ISO timestamp;
* @returns {string | undefined} - date formated as ISO timestamp;
*/
function isoTimestamp(dateable) {
if (dateable) {
Expand All @@ -45,26 +52,19 @@ function isoTimestamp(dateable) {
}

/**
* Transforms input granuleId into correct parameters for use in the
* Reconciliation Report lambda.
* @param {Array<string>|string} granuleId - list of granule Ids
* @param {Object} modifiedEvent - input event
* @returns {Object} updated input even with correct granuleId and granuleIds values.
* Normalizes the input into an array of granule IDs.
*
* @param {string|string[]|undefined} granuleId - The granule ID or an array of granule IDs.
* @returns {string[]|undefined} An array of granule IDs, or undefined if no granule ID is provided.
*/
function updateGranuleIds(granuleId, modifiedEvent) {
let returnEvent = { ...modifiedEvent };
if (granuleId) {
// transform input granuleId into an array on granuleIds
const granuleIds = isString(granuleId) ? [granuleId] : granuleId;
returnEvent = { ...modifiedEvent, granuleIds };
}
return returnEvent;
function generateGranuleIds(granuleId) {
return granuleId ? (isString(granuleId) ? [granuleId] : granuleId) : undefined;
}

/**
* Transforms input collectionId into correct parameters for use in the
* Reconciliation Report lambda.
* @param {Array<string>|string} collectionId - list of collection Ids
* @param {string[]|string | undefined} collectionId - list of collection Ids
* @param {Object} modifiedEvent - input event
* @returns {Object} updated input even with correct collectionId and collectionIds values.
*/
Expand All @@ -78,26 +78,32 @@ function updateCollectionIds(collectionId, modifiedEvent) {
return returnEvent;
}

function updateProviders(provider, modifiedEvent) {
let returnEvent = { ...modifiedEvent };
if (provider) {
// transform input provider into an array on providers
const providers = isString(provider) ? [provider] : provider;
returnEvent = { ...modifiedEvent, providers };
}
return returnEvent;
/**
* Normalizes the input provider into an array of providers.
*
* @param {string|string[]|undefined} provider - The provider or list of providers.
* @returns {string[]|undefined} An array of providers, or undefined if no provider is provided.
*/
function generateProviders(provider) {
return provider ? (isString(provider) ? [provider] : provider) : undefined;
}

/**
* Converts input parameters to normalized versions to pass on to the report
* functions. Ensures any input dates are formatted as ISO strings.
*
* @param {Object} event - input payload
* @returns {Object} - Object with normalized parameters
* @param {RecReportParams} event - input payload
* @returns {NormalizedRecReportParams} - Object with normalized parameters
*/
function normalizeEvent(event) {
const systemBucket = event.systemBucket || process.env.system_bucket;
if (!systemBucket) {
throw new InvalidArgument('systemBucket is required.');
}
const stackName = event.stackName || process.env.stackName;
if (!stackName) {
throw new InvalidArgument('stackName is required.');
}
const startTimestamp = isoTimestamp(event.startTimestamp);
const endTimestamp = isoTimestamp(event.endTimestamp);

Expand All @@ -120,16 +126,16 @@ function normalizeEvent(event) {
throw new InvalidArgument(`${reportType} reports cannot be launched with more than one input (granuleId, collectionId, or provider).`);
}
modifiedEvent = updateCollectionIds(collectionId, modifiedEvent);
modifiedEvent = updateGranuleIds(granuleId, modifiedEvent);
modifiedEvent = updateProviders(provider, modifiedEvent);

return removeNilProperties({
return (removeNilProperties({
...modifiedEvent,
systemBucket,
stackName,
startTimestamp,
endTimestamp,
reportType,
});
granuleIds: generateGranuleIds(granuleId),
providers: generateProviders(provider),
}));
}
exports.normalizeEvent = normalizeEvent;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const CRP = rewire('../../lambdas/create-reconciliation-report');
const linkingFilesToGranules = CRP.__get__('linkingFilesToGranules');
const isOneWayCollectionReport = CRP.__get__('isOneWayCollectionReport');
const isOneWayGranuleReport = CRP.__get__('isOneWayGranuleReport');
const shouldAggregateGranulesForCollections = CRP.__get__('shouldAggregateGranulesForCollections');

test(
'isOneWayCollectionReport returns true only when one or more specific parameters '
Expand Down Expand Up @@ -86,39 +85,6 @@ test(
}
);

test(
'shouldAggregateGranulesForCollections returns true only when one or more specific parameters '
+ ' are present on the reconciliation report object.',
(t) => {
const paramsThatShouldReturnTrue = ['updatedAt__to', 'updatedAt__from'];
const paramsThatShouldReturnFalse = [
'stackName',
'systemBucket',
'startTimestamp',
'anythingAtAll',
];

paramsThatShouldReturnTrue.map((p) =>
t.true(shouldAggregateGranulesForCollections({ [p]: randomId('value') })));

paramsThatShouldReturnFalse.map((p) =>
t.false(shouldAggregateGranulesForCollections({ [p]: randomId('value') })));

const allTrueKeys = paramsThatShouldReturnTrue.reduce(
(accum, current) => ({ ...accum, [current]: randomId('value') }),
{}
);
t.true(shouldAggregateGranulesForCollections(allTrueKeys));

const allFalseKeys = paramsThatShouldReturnFalse.reduce(
(accum, current) => ({ ...accum, [current]: randomId('value') }),
{}
);
t.false(shouldAggregateGranulesForCollections(allFalseKeys));
t.true(shouldAggregateGranulesForCollections({ ...allTrueKeys, ...allFalseKeys }));
}
);

test('linkingFilesToGranules return values', (t) => {
const reportTypesToReturnFalse = ['Granule Inventory', 'Internal', 'Inventory'];
const reportTypesToReturnTrue = ['Granule Not Found'];
Expand Down
Loading