"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const internal_1 = require("../../internal");
const Storage_1 = require("../../Storage");
const testHelper_1 = require("../testHelper");
describe('Samples.LogicalManipulationUsingProjections', () => {
    const testsSubpath = 'Samples';
    const testName = 'TestLogicalManipulationUsingProjections';
    const sampleIt = process.env['SAMPLE_RUNTESTS'] === '1' ? it : it.skip;
    sampleIt('TestLogicalManipulationUsingProjections', (done) => __awaiter(void 0, void 0, void 0, function* () {
        jest.setTimeout(100000);
        testHelper_1.testHelper.deleteFilesFromActualOutput(testHelper_1.testHelper.getActualOutputFolderPath(testsSubpath, testName));
        const corpus = setupCdmCorpus();
        yield logicalManipulationUsingProjections(corpus);
        testHelper_1.testHelper.assertFolderFilesEquality(testHelper_1.testHelper.getExpectedOutputFolderPath(testsSubpath, testName), testHelper_1.testHelper.getActualOutputFolderPath(testsSubpath, testName), true);
        done();
    }));
    function setupCdmCorpus() {
        const corpus = new internal_1.CdmCorpusDefinition();
        corpus.storage.defaultNamespace = 'local';
        corpus.storage.mount('cdm', new Storage_1.LocalAdapter(testHelper_1.testHelper.sampleSchemaFolderPath));
        corpus.storage.mount('local', new Storage_1.LocalAdapter(testHelper_1.testHelper.getInputFolderPath(testsSubpath, testName)));
        corpus.storage.mount('output', new Storage_1.LocalAdapter(testHelper_1.testHelper.getActualOutputFolderPath(testsSubpath, testName)));
        return corpus;
    }
    /**
     * Applies the replaceAsForeignKey and addAttributeGroup operations to the entity attribute provided.
     * @param name entityAttr
     * @param name fkAttrName
     * @param name attrGroupName
     */
    function applyDefaultBehavior(entityAttr, fkAttrName, attrGroupName) {
        const ctx = entityAttr.ctx;
        const projection = new internal_1.CdmProjection(ctx);
        // Link for the Source property documentation.
        // https://docs.microsoft.com/en-us/common-data-model/sdk/convert-logical-entities-resolved-entities#source
        projection.source = entityAttr.entity;
        // Link for the RunSequentially property documentation.
        // https://docs.microsoft.com/en-us/common-data-model/sdk/convert-logical-entities-resolved-entities#run-sequentially
        projection.runSequentially = true;
        entityAttr.entity = new internal_1.CdmEntityReference(ctx, projection, false);
        if (fkAttrName) {
            const foreignKeyAttr = new internal_1.CdmTypeAttributeDefinition(ctx, fkAttrName);
            foreignKeyAttr.dataType = new internal_1.CdmDataTypeReference(ctx, 'entityId', true);
            // Link for the ReplaceAsForeignKey operation documentation.
            // https://docs.microsoft.com/en-us/common-data-model/sdk/projections/replaceasforeignkey
            const replaceAsFKOperation = new internal_1.CdmOperationReplaceAsForeignKey(ctx);
            replaceAsFKOperation.condition = 'referenceOnly';
            replaceAsFKOperation.reference = 'addressLine';
            replaceAsFKOperation.replaceWith = foreignKeyAttr;
            projection.operations.push(replaceAsFKOperation);
        }
        if (attrGroupName) {
            // Link for the AddAttributeGroup operation documentation.
            // https://docs.microsoft.com/en-us/common-data-model/sdk/projections/addattributegroup
            const addAttrGroupOperation = new internal_1.CdmOperationAddAttributeGroup(ctx);
            addAttrGroupOperation.condition = 'structured';
            addAttrGroupOperation.attributeGroupName = attrGroupName;
            projection.operations.push(addAttrGroupOperation);
        }
    }
    /**
     * Applies the arrayExpansion operation to the entity attribute provided.
     * It also takes care of applying a renameAttributes operation and optionally applying a addCountAttribute operation.
     * @param name entityAttr
     * @param name startOrdinal
     * @param name endOrdinal
     * @param name renameFormat
     * @param name countAttName
     */
    function applyArrayExpansion(entityAttr, startOrdinal, endOrdinal, renameFormat, countAttName) {
        const ctx = entityAttr.ctx;
        const projection = new internal_1.CdmProjection(ctx);
        projection.source = entityAttr.entity;
        projection.runSequentially = true;
        // Link for the Condition property documentation.
        // https://docs.microsoft.com/en-us/common-data-model/sdk/convert-logical-entities-resolved-entities#condition
        projection.condition = '!normalized';
        entityAttr.entity = new internal_1.CdmEntityReference(ctx, projection, false);
        // Link for the ArrayExpansion operation documentation.
        // https://docs.microsoft.com/en-us/common-data-model/sdk/projections/arrayexpansion
        const arrExpansionOperation = new internal_1.CdmOperationArrayExpansion(ctx);
        arrExpansionOperation.startOrdinal = startOrdinal;
        arrExpansionOperation.endOrdinal = endOrdinal;
        projection.operations.push(arrExpansionOperation);
        // Link for the RenameAttributes operation documentation.
        // https://docs.microsoft.com/en-us/common-data-model/sdk/projections/renameattributes
        const renameAttrsOperation = new internal_1.CdmOperationRenameAttributes(ctx);
        renameAttrsOperation.renameFormat = renameFormat;
        projection.operations.push(renameAttrsOperation);
        if (countAttName) {
            const countAttribute = new internal_1.CdmTypeAttributeDefinition(ctx, countAttName);
            countAttribute.dataType = new internal_1.CdmDataTypeReference(ctx, 'integer', true);
            // Link for the AddCountAttribute operation documentation.
            // https://docs.microsoft.com/en-us/common-data-model/sdk/projections/addcountattribute 
            const addCountAttrOperation = new internal_1.CdmOperationAddCountAttribute(ctx);
            addCountAttrOperation.countAttribute = countAttribute;
            projection.operations.push(addCountAttrOperation);
        }
    }
    function logicalManipulationUsingProjections(corpus) {
        return __awaiter(this, void 0, void 0, function* () {
            console.log('Create logical entity definition.');
            const logicalFolder = yield corpus.fetchObjectAsync('output:/');
            const logicalDoc = logicalFolder.documents.push('Person.cdm.json');
            logicalDoc.imports.push('local:/Address.cdm.json');
            const entity = logicalDoc.definitions.push('Person');
            // Add 'name' data typed attribute.
            const nameAttr = entity.attributes.push('name');
            nameAttr.dataType = new internal_1.CdmDataTypeReference(corpus.ctx, 'string', true);
            // Add 'age' data typed attribute.
            const ageAttr = entity.attributes.push('age');
            ageAttr.dataType = new internal_1.CdmDataTypeReference(corpus.ctx, 'string', true);
            // Add 'address' entity typed attribute.
            const entityAttr = new internal_1.CdmEntityAttributeDefinition(corpus.ctx, 'address');
            entityAttr.entity = new internal_1.CdmEntityReference(corpus.ctx, 'Address', true);
            applyArrayExpansion(entityAttr, 1, 3, '{m}{A}{o}', 'countAttribute');
            applyDefaultBehavior(entityAttr, 'addressFK', 'address');
            entity.attributes.push(entityAttr);
            // Add 'email' data typed attribute.
            const emailAttr = entity.attributes.push('email');
            emailAttr.dataType = new internal_1.CdmDataTypeReference(corpus.ctx, 'string', true);
            // Save the logical definition of Person.
            yield entity.inDocument.saveAsAsync('Person.cdm.json');
            console.log('Get \'resolved\' folder where the resolved entities will be saved.');
            const resolvedFolder = yield corpus.fetchObjectAsync('output:/');
            const resOpt = new internal_1.resolveOptions(entity);
            // To get more information about directives and their meaning refer to 
            // https://docs.microsoft.com/en-us/common-data-model/sdk/convert-logical-entities-resolved-entities#directives-guidance-and-the-resulting-resolved-shapes
            // We will start by resolving this entity with the 'normalized' direcitve. 
            // This directive will be used on this and the next two examples so we can analize the resolved entity
            // without the array expansion.
            console.log('Resolving logical entity with normalized directive.');
            resOpt.directives = new internal_1.AttributeResolutionDirectiveSet(new Set(['normalized']));
            const resNormalizedEntity = yield entity.createResolvedEntityAsync(`normalized_${entity.entityName}`, resOpt, resolvedFolder);
            yield resNormalizedEntity.inDocument.saveAsAsync(`${resNormalizedEntity.entityName}.cdm.json`);
            // Another common scenario is to resolve an entity using the 'referenceOnly' directive. 
            // This directives is used to replace the relationships with a foreign key.
            console.log('Resolving logical entity with referenceOnly directive.');
            resOpt.directives = new internal_1.AttributeResolutionDirectiveSet(new Set(['normalized', 'referenceOnly']));
            const resReferenceOnlyEntity = yield entity.createResolvedEntityAsync(`referenceOnly_${entity.entityName}`, resOpt, resolvedFolder);
            yield resReferenceOnlyEntity.inDocument.saveAsAsync(`${resReferenceOnlyEntity.entityName}.cdm.json`);
            // When dealing with structured data, like Json or parquet, it sometimes necessary to represent the idea that 
            // a property can hold a complex object. The shape of the complex object is defined by the source entity pointed by the 
            // entity attribute and we use the 'structured' directive to resolve the entity attribute as an attribute group.
            console.log('Resolving logical entity with structured directive.');
            resOpt.directives = new internal_1.AttributeResolutionDirectiveSet(new Set(['normalized', 'structured']));
            const resStructuredEntity = yield entity.createResolvedEntityAsync(`structured_${entity.entityName}`, resOpt, resolvedFolder);
            yield resStructuredEntity.inDocument.saveAsAsync(`${resStructuredEntity.entityName}.cdm.json`);
            // Now let us remove the 'normalized' directive so the array expansion operation can run.
            console.log('Resolving logical entity without directives (array expansion).');
            resOpt.directives = new internal_1.AttributeResolutionDirectiveSet(new Set([]));
            const resArrayEntity = yield entity.createResolvedEntityAsync(`array_expansion_${entity.entityName}`, resOpt, resolvedFolder);
            yield resArrayEntity.inDocument.saveAsAsync(`${resArrayEntity.entityName}.cdm.json`);
        });
    }
});
//# sourceMappingURL=LogicalManipulationUsingProjections.test.js.map