All files orchestrate.js

96.08% Statements 49/51
91.3% Branches 21/23
100% Functions 4/4
96.08% Lines 49/51

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142    1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                         115x 112x   8x   112x 6x               109x                     131x 115x   16x                                       135x   135x 135x         135x         135x         135x 135x 135x   135x   135x                             135x   135x 109x     135x 135x 135x 135x 135x 135x 135x 135x   135x   135x   4x   131x   135x     1x  
'use strict';
 
const canonicalize = require( '../function-schemata/javascript/src/canonicalize.js' );
const normalize = require( '../function-schemata/javascript/src/normalize.js' );
const { makeMappedResultEnvelope, setMetadataValue } = require( '../function-schemata/javascript/src/utils.js' );
const { validatesAsFunctionCall } = require( '../function-schemata/javascript/src/schema.js' );
const { error, normalError } = require( '../function-schemata/javascript/src/error' );
const ErrorFormatter = require( '../function-schemata/javascript/src/errorFormatter' );
const { validate } = require( './validation.js' );
const { execute } = require( './execute.js' );
const { containsError, isError, makeWrappedResultEnvelope, returnOnFirstError } = require( './utils.js' );
const { Evaluator } = require( './Evaluator.js' );
const { Invariants } = require( './Invariants.js' );
const { ReferenceResolver } = require( './db.js' );
const { ZWrapper } = require( './ZWrapper' );
 
/**
 * Decides whether to validate a function. Returns the pair
 * <original ZObject, Unit> if validation succeeds; otherwise returns the pair
 * <Unit, Z5>.
 *
 * @param {Object} zobject
 * @param {boolean} doValidate whether to run validation; succeeds trivially if false
 * @param {Invariants} invariants for resolving Z9s
 * @return {Object} a Z22
 */
async function maybeValidate( zobject, doValidate, invariants ) {
	if ( doValidate ) {
		const errors = (
			await validate( zobject, invariants )
		).map( ( errorWrapper ) => errorWrapper.asJSON() );
 
		if ( errors.length > 0 ) {
			return makeMappedResultEnvelope(
				null,
				errors.length === 1 ?
					errors[ 0 ] :
					ErrorFormatter.createZErrorList( errors )
			);
		}
	}
	return makeMappedResultEnvelope( zobject, null );
}
 
/**
 * Returns the pair <original ZObject, Unit> if the input object is a Z7;
 * otherwise returns the pair <Unit, Z5>.
 *
 * @param {Object} zobject
 * @return {Object} a Z22 as described above
 */
async function Z7OrError( zobject ) {
	if ( ( await validatesAsFunctionCall( zobject ) ).isValid() ) {
		return makeMappedResultEnvelope( zobject, null );
	}
	return makeMappedResultEnvelope(
		null,
		normalError(
			[ error.wrong_content_type ],
			[ 'The provided object is not a function call' ]
		)
	);
}
 
/**
 * Main orchestration workflow. Executes an input Z7 and returns either the
 * results of function evaluation or the relevant error(s).
 *
 * Takes and returns JSON representation; not ZWrapper.
 *
 * @param {Object} input the input for a function call
 * @param {ImplementationSelector} implementationSelector
 * @return {Object} a Z22 containing the result of function evaluation or a Z5
 */
async function orchestrate( input, implementationSelector = null ) {
	const startTime = new Date();
 
	let zobject = input.zobject;
	Iif ( zobject === undefined ) {
		zobject = input;
	}
	let currentPair;
 
	Iif ( isError( zobject ) ) {
		currentPair = makeMappedResultEnvelope(
			null, zobject, /* canonicalize= */true
		);
	} else {
		currentPair = makeMappedResultEnvelope(
			zobject, null, /* canonicalize= */true
		);
	}
 
	const evaluator = new Evaluator( input.evaluatorUri || null );
	const resolver = new ReferenceResolver( input.wikiUri || null );
	const invariants = new Invariants( evaluator, resolver );
 
	const doValidate = typeof input.doValidate === 'boolean' ? input.doValidate : true;
 
	const callTuples = [
		[ normalize, [ /* generically= */true, /* withVoid= */ true ], 'normalize' ],
		// TODO (T296685): Dereference top-level object if it is a Z9?
		[ Z7OrError, [], 'Z7OrError' ],
		[ makeWrappedResultEnvelope, [], 'wrapAsZObject' ],
		[ maybeValidate, [ doValidate, invariants ], 'maybeValidate' ],
		[
			execute, [
				invariants, /* oldScope= */null, /* doValidate= */true,
				/* implementationSelector= */implementationSelector,
				/* resolveInternals= */true, /* topLevel= */true ],
			'execute'
		]
	];
 
	currentPair = await returnOnFirstError( currentPair, callTuples );
 
	if ( currentPair instanceof ZWrapper ) {
		currentPair = currentPair.asJSON();
	}
 
	const endTime = new Date();
	const duration = endTime.getTime() - startTime.getTime();
	const startTimeStr = startTime.toISOString();
	const endTimeStr = endTime.toISOString();
	const durationStr = duration + 'ms';
	currentPair = setMetadataValue( currentPair, { Z1K1: 'Z6', Z6K1: 'orchestrationStartTime' }, { Z1K1: 'Z6', Z6K1: startTimeStr } );
	currentPair = setMetadataValue( currentPair, { Z1K1: 'Z6', Z6K1: 'orchestrationEndTime' }, { Z1K1: 'Z6', Z6K1: endTimeStr } );
	currentPair = setMetadataValue( currentPair, { Z1K1: 'Z6', Z6K1: 'orchestrationDuration' }, { Z1K1: 'Z6', Z6K1: durationStr } );
 
	const canonicalized = await canonicalize( currentPair, /* withVoid= */ true );
 
	if ( containsError( canonicalized ) ) {
		// If canonicalization fails, return normalized form instead.
		console.log( 'Could not canonicalize; outputting in normal form.' );
	} else {
		currentPair = canonicalized.Z22K1;
	}
	return currentPair;
}
 
module.exports = orchestrate;