'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var ordercloudJavascriptSdk = require('ordercloud-javascript-sdk');
var _ = require('lodash');
var chalk = require('chalk');
var emoji = require('node-emoji');
var axios = require('axios');
var yaml = require('js-yaml');
var portalJavascriptSdk = require('@ordercloud/portal-javascript-sdk');
var Bottleneck = require('bottleneck');
var jwt_decode = require('jwt-decode');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
var emoji__default = /*#__PURE__*/_interopDefaultLegacy(emoji);
var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
var yaml__default = /*#__PURE__*/_interopDefaultLegacy(yaml);
var Bottleneck__default = /*#__PURE__*/_interopDefaultLegacy(Bottleneck);
var jwt_decode__default = /*#__PURE__*/_interopDefaultLegacy(jwt_decode);

/*! *****************************************************************************
Copyright (c) Microsoft Corporation.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */

function __awaiter(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());
    });
}

function __generator(thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
}

function __spreadArray(to, from) {
    for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
        to[j] = from[i];
    return to;
}

var templates = [
	{
		name: "Simple-B2C",
		dataUrl: "https://raw.githubusercontent.com/ordercloud-api/ordercloud-seed/main/seeds/Simple-B2C.yml",
		portalActive: true,
		shortDescription: "something something",
		longDescription: "something something something something"
	},
	{
		name: "Vercel-B2C",
		dataUrl: "https://raw.githubusercontent.com/ordercloud-api/ordercloud-seed/main/seeds/Vercel-B2C.yml",
		portalActive: false,
		shortDescription: "something something",
		longDescription: "something something something something"
	}
];

var Random = /** @class */ (function () {
    function Random() {
    }
    Random.generateWebhookSecret = function () {
        return this.generate(20);
    };
    Random.generateClientSecret = function () {
        return this.generate(60);
    };
    Random.generateOrgID = function () {
        return this.generate(16);
    };
    Random.generate = function (length) {
        var retVal = "";
        for (var i = 0, n = this.charset.length; i < length; ++i) {
            retVal += this.charset.charAt(Math.floor(Math.random() * n));
        }
        return retVal;
    };
    Random.charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    return Random;
}());

var flatten = ___default['default'].flatten;
var OrderCloudBulk = /** @class */ (function () {
    function OrderCloudBulk(limiter) {
        var _this = this;
        this.retryWaitScheduleInMS = [1000, 5000];
        this.limiter = limiter;
        this.limiter.on("failed", function (error, jobInfo) {
            if (jobInfo.retryCount < _this.retryWaitScheduleInMS.length) {
                // returning a number does a retry in that many milliseconds
                // https://www.npmjs.com/package/bottleneck#retries
                var wait = _this.retryWaitScheduleInMS[jobInfo.retryCount];
                console.log("Job " + jobInfo.options.id + " failed. Will retry after " + wait + " ms.");
                return wait;
            }
            _this.throwError(error);
        });
    }
    OrderCloudBulk.prototype.throwError = function (error) {
        console.log(chalk__default['default'].red("\nERROR: Unexpected error from OrderCloud. This is likely a seeding tool bug. Please create a github issue with your seed file included! https://github.com/ordercloud-api/ordercloud-seed/issues.\n"));
        console.log("Request method:", chalk__default['default'].green(error.response.config.method.toUpperCase()));
        console.log("Request url:", chalk__default['default'].green(error.response.config.url));
        console.log("Request data:", chalk__default['default'].green(error.response.config.data));
        console.log("\nResponse status:", error.status);
        console.log("Response data:", error.errors.Errors[0]);
    };
    OrderCloudBulk.prototype.ListAll = function (resource) {
        var routeParams = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            routeParams[_i - 1] = arguments[_i];
        }
        return __awaiter(this, void 0, void 0, function () {
            var listFunc;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        listFunc = resource.sdkObject[resource.listMethodName];
                        return [4 /*yield*/, this.ListAllWithFunction.apply(this, __spreadArray([resource.name, listFunc], routeParams))];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    OrderCloudBulk.prototype.ListAllWithFunction = function (resourceName, listFunc) {
        var routeParams = [];
        for (var _i = 2; _i < arguments.length; _i++) {
            routeParams[_i - 2] = arguments[_i];
        }
        return __awaiter(this, void 0, void 0, function () {
            var queryParams, page1, additionalPages, results;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        queryParams = { page: 1, pageSize: 100, depth: 'all' };
                        return [4 /*yield*/, listFunc.apply(void 0, __spreadArray(__spreadArray([], routeParams), [queryParams]))];
                    case 1:
                        page1 = _a.sent();
                        additionalPages = _.range(2, Math.max((page1 === null || page1 === void 0 ? void 0 : page1.Meta.TotalPages) + 1, 2));
                        return [4 /*yield*/, this.Run(resourceName, additionalPages, function (page) { return listFunc.apply(void 0, __spreadArray(__spreadArray([], routeParams), [{ page: page, pageSize: 100 }])); })
                            // combine and flatten items
                        ];
                    case 2:
                        results = _a.sent();
                        // combine and flatten items
                        return [2 /*return*/, flatten(__spreadArray([page1], results).map(function (r) { return r.Items; }))];
                }
            });
        });
    };
    OrderCloudBulk.prototype.CreateAll = function (resource, records) {
        return __awaiter(this, void 0, void 0, function () {
            var createFunc;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        createFunc = resource.sdkObject[resource.createMethodName];
                        return [4 /*yield*/, this.Run(resource.name, records, function (record) {
                                if (resource.parentRefField) {
                                    return createFunc(record[resource.parentRefField], record);
                                }
                                else {
                                    return createFunc(record);
                                }
                            })];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    OrderCloudBulk.prototype.Run = function (resourceName, records, func) {
        return __awaiter(this, void 0, void 0, function () {
            var tasks, _loop_1, this_1, task, i;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (___default['default'].isNil(records)) {
                            return [2 /*return*/, []];
                        }
                        tasks = [];
                        _loop_1 = function (i) {
                            var record = records[i];
                            task = this_1.limiter.schedule({ id: resourceName + "-" + i + "-" + Random.generate(6) }, function () { return func(record); });
                            tasks.push(task);
                        };
                        this_1 = this;
                        for (i = 0; i < records.length; i++) {
                            _loop_1(i);
                        }
                        return [4 /*yield*/, Promise.all(tasks)];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    return OrderCloudBulk;
}());

function defaultLogger(message, messageType) {
    if (messageType === void 0) { messageType = MessageType.Progress; }
    if (messageType == MessageType.Success) {
        console.log(emoji__default['default'].get('heavy_check_mark'), chalk__default['default'].green(' SUCCESS -', message));
    }
    if (messageType == MessageType.Error) {
        console.log(emoji__default['default'].get('x'), chalk__default['default'].red('ERROR -', message));
    }
    if (messageType == MessageType.Progress) {
        console.log(emoji__default['default'].get('clock1'), 'PROGRESS -', message);
    }
}
var MessageType;
(function (MessageType) {
    MessageType[MessageType["Error"] = 0] = "Error";
    MessageType[MessageType["Progress"] = 1] = "Progress";
    MessageType[MessageType["Success"] = 2] = "Success";
})(MessageType || (MessageType = {}));

// strings coorespond to the name of the YAML property for that resource
var OCResourceEnum;
(function (OCResourceEnum) {
    OCResourceEnum["SecurityProfiles"] = "SecurityProfiles";
    OCResourceEnum["ImpersonationConfigs"] = "ImpersonationConfigs";
    OCResourceEnum["OpenIdConnects"] = "OpenIdConnects";
    OCResourceEnum["AdminUsers"] = "AdminUsers";
    OCResourceEnum["AdminUserGroups"] = "AdminUserGroups";
    OCResourceEnum["AdminAddresses"] = "AdminAddresses";
    OCResourceEnum["MessageSenders"] = "MessageSenders";
    OCResourceEnum["ApiClients"] = "ApiClients";
    OCResourceEnum["Incrementors"] = "Incrementors";
    OCResourceEnum["IntegrationEvents"] = "IntegrationEvents";
    OCResourceEnum["Webhooks"] = "Webhooks";
    OCResourceEnum["XpIndices"] = "XpIndices";
    OCResourceEnum["Buyers"] = "Buyers";
    OCResourceEnum["Users"] = "Users";
    OCResourceEnum["UserGroups"] = "UserGroups";
    OCResourceEnum["Addresses"] = "Addresses";
    OCResourceEnum["CostCenters"] = "CostCenters";
    OCResourceEnum["CreditCards"] = "CreditCards";
    OCResourceEnum["SpendingAccounts"] = "SpendingAccounts";
    OCResourceEnum["ApprovalRules"] = "ApprovalRules";
    OCResourceEnum["Suppliers"] = "Suppliers";
    OCResourceEnum["SupplierUsers"] = "SupplierUsers";
    OCResourceEnum["SupplierUserGroups"] = "SupplierUserGroups";
    OCResourceEnum["SupplierAddresses"] = "SupplierAddresses";
    OCResourceEnum["Catalogs"] = "Catalogs";
    OCResourceEnum["Categories"] = "Categories";
    OCResourceEnum["Products"] = "Products";
    OCResourceEnum["PriceSchedules"] = "PriceSchedules";
    OCResourceEnum["Specs"] = "Specs";
    OCResourceEnum["SpecOptions"] = "SpecOptions";
    OCResourceEnum["ProductFacets"] = "ProductFacets";
    OCResourceEnum["Promotions"] = "Promotions";
    OCResourceEnum["SecurityProfileAssignments"] = "SecurityProfileAssignments";
    OCResourceEnum["AdminUserGroupAssignments"] = "AdminUserGroupAssignments";
    OCResourceEnum["ApiClientAssignments"] = "ApiClientAssignments";
    OCResourceEnum["UserGroupAssignments"] = "UserGroupAssignments";
    OCResourceEnum["AddressAssignments"] = "AddressAssignments";
    OCResourceEnum["CostCenterAssignments"] = "CostCenterAssignments";
    OCResourceEnum["CreditCardAssignments"] = "CreditCardAssignments";
    OCResourceEnum["SpendingAccountAssignments"] = "SpendingAccountAssignments";
    OCResourceEnum["SupplierUserGroupsAssignments"] = "SupplierUserGroupsAssignments";
    OCResourceEnum["ProductAssignments"] = "ProductAssignments";
    OCResourceEnum["CatalogAssignments"] = "CatalogAssignments";
    OCResourceEnum["ProductCatalogAssignment"] = "ProductCatalogAssignment";
    OCResourceEnum["CategoryAssignments"] = "CategoryAssignments";
    OCResourceEnum["CategoryProductAssignments"] = "CategoryProductAssignments";
    OCResourceEnum["SpecProductAssignments"] = "SpecProductAssignments";
    OCResourceEnum["PromotionAssignment"] = "PromotionAssignment";
})(OCResourceEnum || (OCResourceEnum = {}));

var ImpersonationConfigValidationFunc = function (record, validator) {
    var impersonationBuyerID = record["ImpersonationBuyerID"];
    var impersonationGroupID = record["ImpersonationGroupID"];
    var impersonationUserID = record["ImpersonationUserID"];
    var hasGroupID = !___default['default'].isNil(impersonationGroupID);
    var hasUserID = !___default['default'].isNil(impersonationUserID);
    if (___default['default'].isNil(impersonationBuyerID)) {
        if (hasGroupID && !validator.idCache.has(OCResourceEnum.AdminUserGroups, impersonationGroupID)) {
            validator.addError("Invalid reference ImpersonationConfigs.ImpersonationGroupID: no AdminUserGroup found with ID \"" + impersonationGroupID + "\".");
        }
        if (hasUserID && !validator.idCache.has(OCResourceEnum.AdminUsers, impersonationUserID)) {
            validator.addError("Invalid reference ImpersonationConfigs.impersonationUserID: no AdminUser found with ID \"" + impersonationUserID + "\".");
        }
    }
    else {
        if (hasGroupID && !validator.idCache.has(OCResourceEnum.UserGroups, impersonationBuyerID + "/" + impersonationGroupID)) {
            validator.addError("Invalid reference ImpersonationConfigs.ImpersonationGroupID: no UserGroup found with ID \"" + impersonationGroupID + "\" and BuyerID \"" + impersonationBuyerID + "\".");
        }
        if (hasUserID && !validator.idCache.has(OCResourceEnum.Users, impersonationBuyerID + "/" + impersonationUserID)) {
            validator.addError("Invalid reference ImpersonationConfigs.impersonationUserID: no User found with ID \"" + impersonationUserID + "\" and BuyerID \"" + impersonationBuyerID + "\".");
        }
    }
};
var ApiClientValidationFunc = function (record, validator) {
    var defaultContextUsername = record["DefaultContextUserName"];
    if (!___default['default'].isNil(defaultContextUsername) && !validator.usernameCache.has(defaultContextUsername)) {
        validator.addError("Invalid reference ApiClients.DefaultContextUserName: no User, SupplierUser or AdminUser found with Username \"" + defaultContextUsername + "\".");
    }
};
var WebhookValidationFunc = function (record, validator) {
    var _a;
    var apiClientIDs = (_a = record["ApiClientIDs"]) !== null && _a !== void 0 ? _a : [];
    var invalidIDs = apiClientIDs.filter(function (id) { return !validator.idCache.has(OCResourceEnum.ApiClients, id); });
    if (invalidIDs.length !== 0) {
        validator.addError("Invalid reference Webhooks.ApiClientIDs: could not find ApiClients with IDs " + invalidIDs.join(", ") + ".");
    }
};
var SecurityProfileAssignmentValidationFunc = function (record, validator) {
    var buyerID = record["BuyerID"];
    var supplierID = record["SupplierID"];
    var userID = record["UserID"];
    var groupID = record["UserGroupID"];
    var hasBuyerID = !___default['default'].isNil(buyerID);
    var hasSupplierID = !___default['default'].isNil(supplierID);
    var hasUserID = !___default['default'].isNil(userID);
    var hasGroupID = !___default['default'].isNil(groupID);
    if (hasBuyerID && hasSupplierID) {
        return validator.addError("SecurityProfileAssignment error: cannot include both a BuyerID and a SupplierID");
    }
    else if (hasSupplierID) {
        if (hasUserID && !validator.idCache.has(OCResourceEnum.SupplierUsers, supplierID + "/" + userID)) {
            validator.addError("Invalid reference SecurityProfileAssignment.UserID: no SupplierUser found with ID \"" + userID + "\" and SupplierID \"" + supplierID + "\".");
        }
        if (hasGroupID && !validator.idCache.has(OCResourceEnum.SupplierUserGroups, supplierID + "/" + groupID)) {
            validator.addError("Invalid reference SecurityProfileAssignment.UserGroupID: no SupplierUserGroups found with ID \"" + groupID + "\" and SupplierID \"" + supplierID + "\".");
        }
    }
    else if (hasBuyerID) {
        if (hasUserID && !validator.idCache.has(OCResourceEnum.Users, buyerID + "/" + userID)) {
            validator.addError("Invalid reference SecurityProfileAssignment.UserID: no User found with ID \"" + userID + "\" and BuyerID \"" + buyerID + "\".");
        }
        if (hasGroupID && !validator.idCache.has(OCResourceEnum.UserGroups, buyerID + "/" + groupID)) {
            validator.addError("Invalid reference SecurityProfileAssignment.UserGroupID: no UserGroup found with ID \"" + groupID + "\" and BuyerID \"" + buyerID + "\".");
        }
    }
    else {
        if (hasUserID && !validator.idCache.has(OCResourceEnum.AdminUsers, userID)) {
            validator.addError("Invalid reference SecurityProfileAssignment.UserID: no AdminUser found with ID \"" + userID + "\".");
        }
        if (hasGroupID && !validator.idCache.has(OCResourceEnum.AdminUserGroups, groupID)) {
            validator.addError("Invalid reference SecurityProfileAssignment.UserGroupID: no AdminUserGroup found with ID \"" + groupID + "\".");
        }
    }
};

var Directory = [
    {
        name: OCResourceEnum.SecurityProfiles,
        modelName: 'SecurityProfile',
        sdkObject: ordercloudJavascriptSdk.SecurityProfiles,
        createPriority: 2,
        path: "/securityprofiles"
    },
    {
        name: OCResourceEnum.ImpersonationConfigs,
        modelName: 'ImpersonationConfig',
        sdkObject: ordercloudJavascriptSdk.ImpersonationConfigs,
        createPriority: 6,
        path: "/impersonationconfig",
        customValidationFunc: ImpersonationConfigValidationFunc,
        foreignKeys: {
            ClientID: { foreignResource: OCResourceEnum.ApiClients },
            SecurityProfileID: { foreignResource: OCResourceEnum.SecurityProfiles },
            BuyerID: { foreignResource: OCResourceEnum.Buyers },
            GroupID: {
                foreignResource: OCResourceEnum.UserGroups,
                foreignParentRefField: "BuyerID"
            },
            UserID: {
                foreignResource: OCResourceEnum.Users,
                foreignParentRefField: "BuyerID"
            },
            ImpersonationBuyerID: { foreignResource: OCResourceEnum.Buyers },
        }
    },
    {
        name: OCResourceEnum.OpenIdConnects,
        modelName: 'OpenIdConnect',
        sdkObject: ordercloudJavascriptSdk.OpenIdConnects,
        createPriority: 6,
        path: "/openidconnects",
        redactFields: ["ConnectClientSecret"],
        foreignKeys: {
            OrderCloudApiClientID: { foreignResource: OCResourceEnum.ApiClients },
            IntegrationEventID: { foreignResource: OCResourceEnum.IntegrationEvents }
        }
    },
    {
        name: OCResourceEnum.AdminUsers,
        modelName: 'User',
        sdkObject: ordercloudJavascriptSdk.AdminUsers,
        path: "/adminusers",
        createPriority: 2
    },
    {
        name: OCResourceEnum.AdminUserGroups,
        modelName: 'UserGroup',
        sdkObject: ordercloudJavascriptSdk.AdminUserGroups,
        path: "/usergroups",
        createPriority: 2
    },
    {
        name: OCResourceEnum.AdminAddresses,
        modelName: 'Address',
        sdkObject: ordercloudJavascriptSdk.AdminAddresses,
        path: "/addresses",
        createPriority: 2
    },
    {
        name: OCResourceEnum.MessageSenders,
        modelName: 'MessageSender',
        sdkObject: ordercloudJavascriptSdk.MessageSenders,
        path: "/messagesenders",
        createPriority: 2,
        redactFields: ["SharedKey"],
    },
    {
        name: OCResourceEnum.ApiClients,
        modelName: 'ApiClient',
        sdkObject: ordercloudJavascriptSdk.ApiClients,
        createPriority: 5,
        path: "/apiclients",
        redactFields: ["ClientSecret"],
        foreignKeys: {
            IntegrationEventID: { foreignResource: OCResourceEnum.IntegrationEvents }
        },
        downloadTransformFunc: function (x) {
            x.ID = x.ID.toLowerCase(); // funky platform thing with API CLient ID casing
            return x;
        },
        customValidationFunc: ApiClientValidationFunc,
    },
    {
        name: OCResourceEnum.Incrementors,
        modelName: 'Incrementor',
        sdkObject: ordercloudJavascriptSdk.Incrementors,
        path: "/incrementors",
        createPriority: 1,
    },
    {
        name: OCResourceEnum.Webhooks,
        modelName: 'Webhook',
        sdkObject: ordercloudJavascriptSdk.Webhooks,
        createPriority: 6,
        path: "/webhooks",
        redactFields: ["HashKey"],
        // for .ApiClientIDs
        customValidationFunc: WebhookValidationFunc
    },
    {
        name: OCResourceEnum.IntegrationEvents,
        modelName: 'IntegrationEvent',
        sdkObject: ordercloudJavascriptSdk.IntegrationEvents,
        path: "/integrationEvents",
        createPriority: 2,
        redactFields: ["HashKey"],
    },
    {
        name: OCResourceEnum.XpIndices,
        modelName: "XpIndex",
        sdkObject: ordercloudJavascriptSdk.XpIndices,
        path: "/xpindices",
        createPriority: 1,
        createMethodName: "Put"
    },
    {
        name: OCResourceEnum.Buyers,
        modelName: "Buyer",
        sdkObject: ordercloudJavascriptSdk.Buyers,
        createPriority: 3,
        path: "/buyers",
        foreignKeys: {
            DefaultCatalogID: { foreignResource: OCResourceEnum.Catalogs }
        },
        children: [OCResourceEnum.Users, OCResourceEnum.UserGroups, OCResourceEnum.Addresses, OCResourceEnum.CostCenters, OCResourceEnum.CreditCards, OCResourceEnum.SpendingAccounts, OCResourceEnum.ApprovalRules, OCResourceEnum.UserGroupAssignments, OCResourceEnum.SpendingAccountAssignments, OCResourceEnum.AddressAssignments, OCResourceEnum.CostCenterAssignments, OCResourceEnum.CreditCardAssignments],
    },
    {
        name: OCResourceEnum.Users,
        modelName: "User",
        sdkObject: ordercloudJavascriptSdk.Users,
        createPriority: 4,
        parentRefField: "BuyerID",
        path: "/buyers/{buyerID}/users",
        isChild: true,
    },
    {
        name: OCResourceEnum.UserGroups,
        modelName: "UserGroup",
        sdkObject: ordercloudJavascriptSdk.UserGroups,
        createPriority: 4,
        parentRefField: "BuyerID",
        path: "/buyers/{buyerID}/usergroups",
        isChild: true,
    },
    {
        name: OCResourceEnum.Addresses,
        modelName: "Address",
        sdkObject: ordercloudJavascriptSdk.Addresses,
        createPriority: 4,
        parentRefField: "BuyerID",
        path: "/buyers/{buyerID}/addresses",
        isChild: true,
    },
    {
        name: OCResourceEnum.CostCenters,
        modelName: "CostCenter",
        sdkObject: ordercloudJavascriptSdk.CostCenters,
        createPriority: 4,
        parentRefField: "BuyerID",
        path: "/buyers/{buyerID}/costcenters",
        isChild: true,
    },
    {
        name: OCResourceEnum.CreditCards,
        modelName: "CreditCard",
        sdkObject: ordercloudJavascriptSdk.CreditCards,
        createPriority: 4,
        parentRefField: "BuyerID",
        path: "/buyers/{buyerID}/creditcards",
        isChild: true,
    },
    {
        name: OCResourceEnum.SpendingAccounts,
        modelName: "SpendingAccount",
        sdkObject: ordercloudJavascriptSdk.SpendingAccounts,
        createPriority: 4,
        parentRefField: "BuyerID",
        path: "/buyers/{buyerID}/spendingaccounts",
        isChild: true,
    },
    {
        name: OCResourceEnum.ApprovalRules,
        modelName: "ApprovalRule",
        sdkObject: ordercloudJavascriptSdk.ApprovalRules,
        createPriority: 5,
        parentRefField: "BuyerID",
        isChild: true,
        path: "/buyers/{buyerID}/approvalrules",
        foreignKeys: {
            ApprovingGroupID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.UserGroups
            }
        }
    },
    {
        name: OCResourceEnum.Catalogs,
        modelName: "Catalog",
        sdkObject: ordercloudJavascriptSdk.Catalogs,
        createPriority: 2,
        path: "/catalogs",
        hasOwnerIDField: true,
        children: [OCResourceEnum.Categories, OCResourceEnum.CategoryAssignments, OCResourceEnum.CategoryProductAssignments]
    },
    {
        name: OCResourceEnum.Categories,
        modelName: "Category",
        sdkObject: ordercloudJavascriptSdk.Categories,
        createPriority: 3,
        parentRefField: "CatalogID",
        isChild: true,
        path: "/catalogs/{catalogID}/categories",
        foreignKeys: {
            ParentID: {
                foreignParentRefField: "CatalogID",
                foreignResource: OCResourceEnum.Categories
            }
        }
    },
    {
        name: OCResourceEnum.Suppliers,
        modelName: "Supplier",
        sdkObject: ordercloudJavascriptSdk.Suppliers,
        createPriority: 2,
        path: "/suppliers",
        children: [OCResourceEnum.SupplierUsers, OCResourceEnum.SupplierUserGroups, OCResourceEnum.SupplierAddresses, OCResourceEnum.SupplierUserGroupsAssignments]
    },
    {
        name: OCResourceEnum.SupplierUsers,
        modelName: "User",
        sdkObject: ordercloudJavascriptSdk.SupplierUsers,
        createPriority: 3,
        parentRefField: "SupplierID",
        path: "/suppliers/{supplierID}/users",
        isChild: true,
    },
    {
        name: OCResourceEnum.SupplierUserGroups,
        modelName: "UserGroup",
        sdkObject: ordercloudJavascriptSdk.SupplierUserGroups,
        createPriority: 3,
        parentRefField: "SupplierID",
        path: "/suppliers/{supplierID}/usergroups",
        isChild: true,
    },
    {
        name: OCResourceEnum.SupplierAddresses,
        modelName: "Address",
        sdkObject: ordercloudJavascriptSdk.SupplierAddresses,
        createPriority: 3,
        parentRefField: "SupplierID",
        path: "/suppliers/{supplierID}/addresses",
        isChild: true,
    },
    {
        name: OCResourceEnum.Products,
        modelName: "Product",
        sdkObject: ordercloudJavascriptSdk.Products,
        createPriority: 4,
        path: "/products",
        hasOwnerIDField: true,
        foreignKeys: {
            DefaultPriceScheduleID: { foreignResource: OCResourceEnum.PriceSchedules },
            ShipFromAddressID: { foreignResource: OCResourceEnum.AdminAddresses },
            DefaultSupplierID: { foreignResource: OCResourceEnum.Suppliers }
        }
    },
    {
        name: OCResourceEnum.PriceSchedules,
        modelName: "PriceSchedule",
        sdkObject: ordercloudJavascriptSdk.PriceSchedules,
        path: "/priceschedules",
        createPriority: 2,
        hasOwnerIDField: true,
    },
    {
        name: OCResourceEnum.Specs,
        modelName: "Spec",
        sdkObject: ordercloudJavascriptSdk.Specs,
        createPriority: 2,
        path: "/specs",
        hasOwnerIDField: true,
        foreignKeys: {
            DefaultOptionID: { foreignResource: OCResourceEnum.SpecOptions, foreignParentRefField: "ID" },
        },
        children: [OCResourceEnum.SpecOptions]
    },
    {
        name: OCResourceEnum.SpecOptions,
        modelName: "SpecOption",
        sdkObject: ordercloudJavascriptSdk.Specs,
        createPriority: 3,
        path: "/specs/{specID}/options",
        parentRefField: "SpecID",
        listMethodName: "ListOptions",
        createMethodName: "CreateOption",
        isChild: true
    },
    {
        name: OCResourceEnum.ProductFacets,
        modelName: "ProductFacet",
        sdkObject: ordercloudJavascriptSdk.ProductFacets,
        path: "/productfacets",
        createPriority: 2
    },
    {
        name: OCResourceEnum.Promotions,
        modelName: "Promotion",
        sdkObject: ordercloudJavascriptSdk.Promotions,
        path: "/promotions",
        createPriority: 2,
        hasOwnerIDField: true
    },
    {
        name: OCResourceEnum.SecurityProfileAssignments,
        modelName: "SecurityProfileAssignment",
        sdkObject: ordercloudJavascriptSdk.SecurityProfiles,
        path: "/securityprofiles/assignments",
        createPriority: 5,
        isAssignment: true,
        foreignKeys: {
            SecurityProfileID: { foreignResource: OCResourceEnum.SecurityProfiles },
            BuyerID: { foreignResource: OCResourceEnum.Buyers },
            SupplierID: { foreignResource: OCResourceEnum.Suppliers },
        },
        customValidationFunc: SecurityProfileAssignmentValidationFunc
    },
    {
        name: OCResourceEnum.AdminUserGroupAssignments,
        modelName: "UserGroupAssignment",
        sdkObject: ordercloudJavascriptSdk.AdminUserGroups,
        createPriority: 3,
        isAssignment: true,
        path: "/usergroups/assignments",
        listMethodName: 'ListUserAssignments',
        createMethodName: 'SaveUserAssignment',
        foreignKeys: {
            UserID: { foreignResource: OCResourceEnum.AdminUsers },
            UserGroupID: { foreignResource: OCResourceEnum.AdminUserGroups },
        },
    },
    {
        name: OCResourceEnum.ApiClientAssignments,
        modelName: "ApiClientAssignment",
        sdkObject: ordercloudJavascriptSdk.ApiClients,
        createPriority: 6,
        isAssignment: true,
        path: "/apiclients/assignments",
        foreignKeys: {
            ApiClientID: { foreignResource: OCResourceEnum.ApiClients },
            BuyerID: { foreignResource: OCResourceEnum.Buyers },
            SupplierID: { foreignResource: OCResourceEnum.Suppliers },
        },
        downloadTransformFunc: function (x) {
            x.ApiClientID = x.ApiClientID.toLowerCase(); // funky platform thing with API CLient ID casing
            return x;
        },
    },
    {
        name: OCResourceEnum.UserGroupAssignments,
        modelName: "UserGroupAssignment",
        sdkObject: ordercloudJavascriptSdk.UserGroups,
        createPriority: 5,
        isAssignment: true,
        path: "/buyers/{buyerID}/usergroups/assignments",
        parentRefField: "BuyerID",
        isChild: true,
        createMethodName: "SaveUserAssignment",
        listMethodName: 'ListUserAssignments',
        foreignKeys: {
            UserID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.Users
            },
            UserGroupID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.UserGroups
            },
        },
    },
    {
        name: OCResourceEnum.AddressAssignments,
        modelName: "AddressAssignment",
        sdkObject: ordercloudJavascriptSdk.Addresses,
        createPriority: 5,
        path: "/buyers/{buyerID}/addresses/assignments",
        isAssignment: true,
        parentRefField: "BuyerID",
        isChild: true,
        foreignKeys: {
            AddressID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.Addresses
            },
            UserID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.Users
            },
            UserGroupID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.UserGroups
            },
        },
    },
    {
        name: OCResourceEnum.CostCenterAssignments,
        modelName: "CostCenterAssignment",
        sdkObject: ordercloudJavascriptSdk.CostCenters,
        createPriority: 5,
        path: "/buyers/{buyerID}/costcenters",
        isAssignment: true,
        parentRefField: "BuyerID",
        isChild: true,
        foreignKeys: {
            CostCenterID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.CostCenters
            },
            UserGroupID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.UserGroups,
            },
        },
    },
    {
        name: OCResourceEnum.CreditCardAssignments,
        modelName: "CreditCardAssignment",
        sdkObject: ordercloudJavascriptSdk.CreditCards,
        createPriority: 5,
        path: "/buyers/{buyerID}/creditcards/assignments",
        isAssignment: true,
        parentRefField: "BuyerID",
        isChild: true,
        foreignKeys: {
            CreditCardID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.CreditCards
            },
            UserID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.Users
            },
            UserGroupID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.UserGroups
            },
        },
    },
    {
        name: OCResourceEnum.SpendingAccountAssignments,
        modelName: "SpendingAccountAssignment",
        sdkObject: ordercloudJavascriptSdk.SpendingAccounts,
        createPriority: 5,
        path: "/buyers/{buyerID}/spendingaccounts/assignments",
        isAssignment: true,
        parentRefField: "BuyerID",
        isChild: true,
        foreignKeys: {
            SpendingAccountID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.SpendingAccounts
            },
            UserID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.Users
            },
            UserGroupID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.UserGroups
            },
        },
    },
    {
        name: OCResourceEnum.SupplierUserGroupsAssignments,
        modelName: "UserGroupAssignment",
        sdkObject: ordercloudJavascriptSdk.SupplierUserGroups,
        createPriority: 4,
        path: "/suppliers/{supplierID}/usergroups/assignments",
        isAssignment: true,
        parentRefField: "SupplierID",
        isChild: true,
        listMethodName: 'ListUserAssignments',
        createMethodName: 'SaveUserAssignment',
        foreignKeys: {
            UserID: {
                foreignParentRefField: "SupplierID",
                foreignResource: OCResourceEnum.SupplierUsers
            },
            UserGroupID: {
                foreignParentRefField: "SupplierID",
                foreignResource: OCResourceEnum.SupplierUserGroups
            },
        },
    },
    {
        name: OCResourceEnum.ProductAssignments,
        modelName: "ProductAssignment",
        sdkObject: ordercloudJavascriptSdk.Products,
        createPriority: 6,
        path: "/products/assignments",
        isAssignment: true,
        foreignKeys: {
            ProductID: { foreignResource: OCResourceEnum.Products },
            BuyerID: { foreignResource: OCResourceEnum.Buyers },
            UserGroupID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.UserGroups
            },
            PriceScheduleID: { foreignResource: OCResourceEnum.PriceSchedules }
        },
    },
    {
        name: OCResourceEnum.CatalogAssignments,
        modelName: "CatalogAssignment",
        sdkObject: ordercloudJavascriptSdk.Catalogs,
        createPriority: 4,
        path: "/catalogs/assignments",
        isAssignment: true,
        foreignKeys: {
            CatalogID: { foreignResource: OCResourceEnum.Catalogs },
            BuyerID: { foreignResource: OCResourceEnum.Buyers }
        },
    },
    {
        name: OCResourceEnum.ProductCatalogAssignment,
        modelName: "ProductCatalogAssignment",
        sdkObject: ordercloudJavascriptSdk.Catalogs,
        createPriority: 5,
        path: "/catalogs/productassignments",
        isAssignment: true,
        listMethodName: 'ListProductAssignments',
        createMethodName: 'SaveProductAssignment',
        foreignKeys: {
            CatalogID: { foreignResource: OCResourceEnum.Catalogs },
            ProductID: { foreignResource: OCResourceEnum.Products }
        },
    },
    {
        name: OCResourceEnum.CategoryAssignments,
        modelName: "CategoryAssignment",
        sdkObject: ordercloudJavascriptSdk.Categories,
        createPriority: 5,
        path: "/catalogs/{catalogID}/categories/assignments",
        isAssignment: true,
        parentRefField: "CatalogID",
        isChild: true,
        foreignKeys: {
            CategoryID: {
                foreignParentRefField: "CatalogID",
                foreignResource: OCResourceEnum.Categories
            },
            BuyerID: { foreignResource: OCResourceEnum.Buyers },
            UserGroupID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.UserGroups
            },
        },
    },
    {
        name: OCResourceEnum.CategoryProductAssignments,
        modelName: "CategoryProductAssignment",
        sdkObject: ordercloudJavascriptSdk.Categories,
        createPriority: 5,
        path: "/catalogs/{catalogID}/categories/productassignments",
        isAssignment: true,
        parentRefField: "CatalogID",
        isChild: true,
        listMethodName: 'ListProductAssignments',
        createMethodName: 'SaveProductAssignment',
        foreignKeys: {
            CategoryID: {
                foreignParentRefField: "CatalogID",
                foreignResource: OCResourceEnum.Categories
            },
            ProductID: { foreignResource: OCResourceEnum.Products },
        },
    },
    {
        name: OCResourceEnum.SpecProductAssignments,
        modelName: "SpecProductAssignment",
        sdkObject: ordercloudJavascriptSdk.Specs,
        createPriority: 5,
        path: "/specs/productassignments",
        isAssignment: true,
        listMethodName: 'ListProductAssignments',
        createMethodName: 'SaveProductAssignment',
        foreignKeys: {
            SpecID: { foreignResource: OCResourceEnum.Specs },
            ProductID: { foreignResource: OCResourceEnum.Products },
            DefaultOptionID: {
                foreignParentRefField: "SpecID",
                foreignResource: OCResourceEnum.SpecOptions
            },
        },
    },
    {
        name: OCResourceEnum.PromotionAssignment,
        modelName: "PromotionAssignment",
        sdkObject: ordercloudJavascriptSdk.Promotions,
        createPriority: 5,
        path: "/promotions/assignments",
        isAssignment: true,
        foreignKeys: {
            PromotionID: { foreignResource: OCResourceEnum.Promotions },
            BuyerID: { foreignResource: OCResourceEnum.Buyers },
            UserGroupID: {
                foreignParentRefField: "BuyerID",
                foreignResource: OCResourceEnum.UserGroups
            },
        },
    },
];
function ApplyDefaults(resource) {
    var _a, _b, _c;
    resource.isAssignment = resource.isAssignment || false;
    resource.listMethodName = resource.listMethodName || (resource.isAssignment ? "ListAssignments" : "List");
    resource.createMethodName = resource.createMethodName || (resource.isAssignment ? "SaveAssignment" : "Create");
    resource.foreignKeys = resource.foreignKeys || {};
    resource.children = resource.children || [];
    resource.isChild = resource.isChild || false;
    resource.requiredCreateFields = (_a = resource.requiredCreateFields) !== null && _a !== void 0 ? _a : [];
    resource.redactFields = (_b = resource.redactFields) !== null && _b !== void 0 ? _b : [];
    resource.hasOwnerIDField = (_c = resource.hasOwnerIDField) !== null && _c !== void 0 ? _c : false;
    return resource;
}
function BuildResourceDirectory(includeOpenAPI) {
    if (includeOpenAPI === void 0) { includeOpenAPI = false; }
    return __awaiter(this, void 0, void 0, function () {
        var openAPISpec;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    if (!includeOpenAPI) return [3 /*break*/, 2];
                    return [4 /*yield*/, axios__default['default'].get("https://api.ordercloud.io/v1/openapi/v3")];
                case 1:
                    openAPISpec = _a.sent();
                    _a.label = 2;
                case 2: return [2 /*return*/, Directory.map(function (resource) {
                        var _a, _b, _c, _d, _e;
                        var modified = ApplyDefaults(resource);
                        if (includeOpenAPI) {
                            var path = openAPISpec.data.paths[resource.path];
                            var operation = (_a = path.post) !== null && _a !== void 0 ? _a : path.put;
                            modified.requiredCreateFields = (_e = (_d = (_c = (_b = operation.requestBody.content) === null || _b === void 0 ? void 0 : _b["application/json"]) === null || _c === void 0 ? void 0 : _c.schema) === null || _d === void 0 ? void 0 : _d.required) !== null && _e !== void 0 ? _e : [];
                            modified.openAPIProperties = openAPISpec.data.components.schemas[resource.modelName].properties;
                            if (modified.isChild) {
                                modified.parentResource = Directory.find(function (x) { return x.children.includes(modified.name); });
                            }
                        }
                        return modified;
                    })];
            }
        });
    });
}

var SerializedMarketplace = /** @class */ (function () {
    function SerializedMarketplace(data) {
        if (data === void 0) { data = null; }
        this.Objects = {};
        this.Assignments = {};
        this.Objects = (data === null || data === void 0 ? void 0 : data.Objects) || {};
        this.Assignments = (data === null || data === void 0 ? void 0 : data.Assignments) || {};
    }
    SerializedMarketplace.prototype.AddRecords = function (resource, records) {
        var _a;
        var typeField = resource.isAssignment ? "Assignments" : "Objects";
        this[typeField][resource.name] = this[typeField][resource.name] || [];
        (_a = this[typeField][resource.name]).push.apply(_a, records);
    };
    SerializedMarketplace.prototype.GetRecords = function (resource) {
        var _a;
        var typeField = resource.isAssignment ? "Assignments" : "Objects";
        return ((_a = this[typeField]) === null || _a === void 0 ? void 0 : _a[resource.name]) || [];
    };
    return SerializedMarketplace;
}());

var IDCache = /** @class */ (function () {
    function IDCache() {
        this.idSets = {};
    }
    IDCache.prototype.add = function (resourceType, key) {
        var set = this.idSets[resourceType];
        if (set === undefined) {
            set = this.idSets[resourceType] = new Set();
        }
        set.add(key);
    };
    IDCache.prototype.has = function (resourceType, key) {
        var _a, _b;
        return (_b = (_a = this.idSets[resourceType]) === null || _a === void 0 ? void 0 : _a.has(key)) !== null && _b !== void 0 ? _b : false;
    };
    return IDCache;
}());

function validate(args) {
    return __awaiter(this, void 0, void 0, function () {
        var rawData, dataUrl, _a, logger, validator, stringData, ex, marketplace, directory, _i, directory_1, resource, hasUsername, hasID, _c, _d, record, _e, directory_2, resource, _f, _g, record, _h, _j, _k, propName, spec, value, foreignKey, typeMatches, _l, _m, error, isValid;
        return __generator(this, function (_o) {
            switch (_o.label) {
                case 0:
                    rawData = args.rawData, dataUrl = args.dataUrl, _a = args.logger, logger = _a === void 0 ? defaultLogger : _a;
                    validator = new Validator();
                    if (!___default['default'].isNil(rawData)) return [3 /*break*/, 5];
                    _o.label = 1;
                case 1:
                    _o.trys.push([1, 3, , 4]);
                    return [4 /*yield*/, axios__default['default'].get(dataUrl)];
                case 2:
                    stringData = (_o.sent()).data;
                    logger("Found marketplace data at url \"" + dataUrl + "\".", MessageType.Success);
                    return [3 /*break*/, 4];
                case 3:
                    _o.sent();
                    logger("Error repsonse from url \"" + dataUrl + "\" when trying to find marketplace data.", MessageType.Error);
                    return [2 /*return*/, null];
                case 4:
                    try {
                        rawData = yaml__default['default'].load(stringData);
                        logger("Valid yaml in \"" + dataUrl + "\"", MessageType.Success);
                    }
                    catch (e) {
                        ex = e;
                        logger("YAML Exception in \"" + dataUrl + "\": " + ex.message, MessageType.Error);
                        return [2 /*return*/, null];
                    }
                    _o.label = 5;
                case 5:
                    marketplace = new SerializedMarketplace(rawData);
                    return [4 /*yield*/, BuildResourceDirectory(true)];
                case 6:
                    directory = _o.sent();
                    // validate duplicate IDs 
                    for (_i = 0, directory_1 = directory; _i < directory_1.length; _i++) {
                        resource = directory_1[_i];
                        validator.currentResource = resource;
                        hasUsername = "Username" in validator.currentResource.openAPIProperties;
                        hasID = hasIDProperty(validator.currentResource);
                        if (hasID || hasUsername) {
                            for (_c = 0, _d = marketplace.GetRecords(validator.currentResource); _c < _d.length; _c++) {
                                record = _d[_c];
                                validator.currentRecord = record;
                                // this step builds validator.idCache, which will be needed for forign key validation later.             
                                if (hasID)
                                    validator.validateDuplicateIDs();
                                if (hasUsername)
                                    validator.validateDuplicateUsernames();
                            }
                        }
                    }
                    // now that idSets are built, another loop for the rest of validation
                    for (_e = 0, directory_2 = directory; _e < directory_2.length; _e++) {
                        resource = directory_2[_e];
                        validator.currentResource = resource;
                        for (_f = 0, _g = marketplace.GetRecords(validator.currentResource); _f < _g.length; _f++) {
                            record = _g[_f];
                            validator.currentRecord = record;
                            for (_h = 0, _j = Object.entries(validator.currentResource.openAPIProperties); _h < _j.length; _h++) {
                                _k = _j[_h], propName = _k[0], spec = _k[1];
                                validator.currentPropertyName = propName;
                                if (spec.readOnly) {
                                    continue;
                                }
                                value = record[propName];
                                foreignKey = validator.currentResource.foreignKeys[propName];
                                if (___default['default'].isNil(value)) {
                                    validator.validateIsRequired(propName);
                                }
                                else {
                                    typeMatches = validator.validateFieldTypeMatches(record[propName], spec);
                                    if (typeMatches && !___default['default'].isNil(foreignKey)) {
                                        validator.validateForeignKeyExists(foreignKey);
                                    }
                                }
                            }
                            if (validator.currentResource.isChild) {
                                validator.currentPropertyName = validator.currentResource.parentRefField;
                                validator.validateParentRef(validator.currentResource.parentResource.name);
                            }
                            if (validator.currentResource.customValidationFunc !== undefined) {
                                validator.currentResource.customValidationFunc(record, validator);
                            }
                        }
                    }
                    for (_l = 0, _m = validator.errors; _l < _m.length; _l++) {
                        error = _m[_l];
                        logger(error, MessageType.Error);
                    }
                    isValid = validator.errors.length === 0;
                    if (isValid) {
                        logger("Data source ready for seeding!", MessageType.Success);
                    }
                    return [2 /*return*/, { errors: validator.errors, isValid: isValid, rawData: marketplace }];
            }
        });
    });
}
function hasIDProperty(resource) {
    return 'ID' in resource.openAPIProperties || resource.name === OCResourceEnum.ApiClients;
}
function getType(value) {
    if (___default['default'].isArray(value))
        return 'array';
    if (___default['default'].isPlainObject(value))
        return 'object';
    if (___default['default'].isString(value))
        return 'string';
    if (___default['default'].isBoolean(value))
        return 'boolean';
    if (___default['default'].isInteger(value))
        return 'integer';
    return 'number';
}
var Validator = /** @class */ (function () {
    function Validator() {
        this.errors = [];
        this.idCache = new IDCache();
        this.usernameCache = new Set();
    }
    Validator.prototype.addError = function (message) {
        this.errors.push(message);
    };
    Validator.prototype.validateIsRequired = function (fieldName) {
        if (this.currentResource.requiredCreateFields.includes(fieldName)) {
            this.addError("Required field " + this.currentResource.name + "." + fieldName + ": cannot have value " + this.currentRecord[fieldName] + ".");
            return false;
        }
        return true;
    };
    Validator.prototype.validateParentRef = function (parentType) {
        var value = this.currentRecord[this.currentResource.parentRefField];
        if (___default['default'].isNil(value)) {
            this.addError("Required field " + this.currentResource.name + "." + this.currentResource.parentRefField + ": cannot have value " + value + ".");
            return false;
        }
        if (!this.validateFieldTypeMatches(value, { type: "string", readOnly: false })) {
            return false;
        }
        if (!this.idCache.has(parentType, value)) {
            this.addError("Invalid reference " + this.currentResource.name + "." + this.currentResource.parentRefField + ": no " + parentType + " found with ID \"" + value + "\".");
            return false;
        }
        return true;
    };
    Validator.prototype.validateForeignKeyExists = function (foreignKey) {
        var field = this.currentPropertyName;
        var value = this.currentRecord[field];
        var setEntry = foreignKey.foreignParentRefField === undefined ? value : this.currentRecord[foreignKey.foreignParentRefField] + "/" + value;
        // find an ID of a particular resource
        var keyExists = this.idCache.has(foreignKey.foreignResource, setEntry);
        if (!keyExists) {
            var message = "Invalid reference " + this.currentResource.name + "." + field + ": no " + foreignKey.foreignResource + " found with ID \"" + value + "\".";
            if (setEntry.includes('/')) {
                message = message.concat(" within the " + foreignKey.foreignParentRefField + " \"" + this.currentRecord[foreignKey.foreignParentRefField] + "\"");
            }
            this.addError(message);
            return false;
        }
        return true;
    };
    // TODO - validate things inside objects or arrays
    Validator.prototype.validateFieldTypeMatches = function (value, spec) {
        // todo - need to do this differently. Get a swagger parser library
        if (spec.allOf) {
            spec.type = "object";
        }
        var error = null;
        var typeError = "Incorrect type " + this.currentResource.name + "." + this.currentPropertyName + ": " + value + " is " + getType(value) + ". Should be " + spec.type + ".";
        switch (spec.type) {
            case 'object':
                if (!___default['default'].isPlainObject(value))
                    error = typeError;
                break;
            case 'array':
                if (!___default['default'].isArray(value))
                    error = typeError;
                break;
            case 'integer':
                if (!___default['default'].isInteger(value)) {
                    error = typeError;
                }
                else {
                    if ((spec.maximum || Infinity) < value) {
                        error = "Maximum for " + this.currentResource.name + "." + this.currentPropertyName + " is " + spec.maximum + ". Found " + value + ".";
                    }
                    if ((spec.minimum || -Infinity) > value) {
                        error = "Minimum for " + this.currentResource.name + "." + this.currentPropertyName + " is " + spec.minimum + ". Found " + value + ".";
                    }
                }
                break;
            case 'number':
                if (!___default['default'].isNumber(value))
                    error = typeError;
                break;
            case 'boolean':
                if (!___default['default'].isBoolean(value))
                    error = typeError;
                break;
            case 'string':
                if (!___default['default'].isString(value)) {
                    error = typeError;
                }
                else {
                    if ((spec.maxLength || Infinity) < value.length) {
                        error = "Max string length for " + this.currentResource.name + "." + this.currentPropertyName + " is " + spec.maxLength + ". Found \"" + value + "\".";
                    }
                    if (spec.format === 'date-time' && !Date.parse(value)) {
                        error = this.currentResource.name + "." + this.currentPropertyName + " should be a date format. Found \"" + value + "\".";
                    }
                }
                break;
        }
        if (error !== null) {
            this.addError(error);
            return false;
        }
        return true;
    };
    // todo - need to validate duplicates that involve more fields that simply ID. e.g. xpindex 
    Validator.prototype.validateDuplicateIDs = function () {
        if (___default['default'].isNil(this.currentRecord.ID) || !hasIDProperty(this.currentResource)) {
            return true;
        }
        var setEntry = this.currentResource.isChild ? this.currentRecord[this.currentResource.parentRefField] + "/" + this.currentRecord.ID : this.currentRecord.ID;
        if (this.idCache.has(this.currentResource.name, setEntry)) {
            var message = "Duplicate ID: multiple " + this.currentResource.name + " with ID \"" + this.currentRecord.ID + "\"";
            if (setEntry.includes('/')) {
                message = message.concat(" within the " + this.currentResource.parentRefField + " \"" + this.currentRecord[this.currentResource.parentRefField] + "\"");
            }
            this.addError(message);
            return false;
        }
        else {
            this.idCache.add(this.currentResource.name, setEntry);
        }
        return true;
    };
    // username needs to be unique within a marketplace
    Validator.prototype.validateDuplicateUsernames = function () {
        var username = this.currentRecord.Username;
        if (___default['default'].isNil(username) || !('Username' in this.currentResource.openAPIProperties)) {
            return true;
        }
        if (this.usernameCache.has(username)) {
            var message = "Duplicate Username: multiple users with username \"" + username + "\"";
            this.addError(message);
            return false;
        }
        else {
            this.usernameCache.add(username);
        }
        return true;
    };
    return Validator;
}());

var REDACTED_MESSAGE = "<Redacted. Leave unchanged.>";
var MARKETPLACE_ID = "<MarketplaceID placeholder>";
var VARIANTS_PROPERTY = "Variants";
var ORDERCLOUD_URLS = {
    staging: "https://stagingapi.ordercloud.io",
    sandbox: "https://sandboxapi.ordercloud.io",
    prod: "https://api.ordercloud.io",
};

var PortalAPI = /** @class */ (function () {
    function PortalAPI() {
        portalJavascriptSdk.Configuration.Set({
            baseApiUrl: "https://portal.ordercloud.io/api/v1"
        });
    }
    PortalAPI.prototype.login = function (username, password) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, portalJavascriptSdk.Auth.Login(username, password)];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    PortalAPI.prototype.getOrganizationToken = function (orgID, accessToken) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, portalJavascriptSdk.ApiClients.GetToken(orgID, null, { accessToken: accessToken })];
                    case 1: return [2 /*return*/, (_a.sent()).access_token];
                }
            });
        });
    };
    PortalAPI.prototype.GetOrganization = function (orgID, accessToken) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, portalJavascriptSdk.Organizations.Get(orgID, { accessToken: accessToken })];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    PortalAPI.prototype.CreateOrganization = function (id, name, accessToken) {
        return __awaiter(this, void 0, void 0, function () {
            var org;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        org = {
                            Id: id,
                            Name: name,
                            Environment: "Sandbox",
                            Region: { Id: "usw" } // US West Azure region
                        };
                        return [4 /*yield*/, portalJavascriptSdk.Organizations.Save(id, org, { accessToken: accessToken })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    return PortalAPI;
}());

function seed(args) {
    var _a, _b, _c;
    return __awaiter(this, void 0, void 0, function () {
        function SetOwnerID(resource, records) {
            if (resource.hasOwnerIDField) {
                for (var _i = 0, records_1 = records; _i < records_1.length; _i++) {
                    var record = records_1[_i];
                    if (record.OwnerID === MARKETPLACE_ID) {
                        record.OwnerID = marketplaceID;
                    }
                }
            }
        }
        function GenerateAndPatchVariants() {
            return __awaiter(this, void 0, void 0, function () {
                var productsWithVariants, _i, productsWithVariants_1, product;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            productsWithVariants = marketplaceData.Objects[OCResourceEnum.Products].filter(function (p) { return p.VariantCount > 0; });
                            ordercloudBulk.Run("Variants", productsWithVariants, function (p) { return ordercloudJavascriptSdk.Products.GenerateVariants(p.ID); });
                            _i = 0, productsWithVariants_1 = productsWithVariants;
                            _a.label = 1;
                        case 1:
                            if (!(_i < productsWithVariants_1.length)) return [3 /*break*/, 4];
                            product = productsWithVariants_1[_i];
                            return [4 /*yield*/, ordercloudBulk.Run("Variants", product[VARIANTS_PROPERTY], function (v) {
                                    var variantID = v.Specs.reduce(function (acc, spec) { return acc + "-" + spec.OptionID; }, product.ID);
                                    return ordercloudJavascriptSdk.Products.SaveVariant(product.ID, variantID, v);
                                })];
                        case 2:
                            _a.sent();
                            _a.label = 3;
                        case 3:
                            _i++;
                            return [3 /*break*/, 1];
                        case 4:
                            logger("Generated variants for " + productsWithVariants.length + " products.", MessageType.Progress);
                            return [2 /*return*/];
                    }
                });
            });
        }
        // Need to remove and cache Spec.DefaultOptionID in order to PATCH it after the options are created.
        function UploadSpecs(resource) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            records.forEach(function (r) {
                                if (!___default['default'].isNil(r.DefaultOptionID)) {
                                    specDefaultOptionIDList.push({ ID: r.ID, DefaultOptionID: r.DefaultOptionID }); // save for later step
                                    r.DefaultOptionID = null; // set null so create spec succeeds 
                                }
                            });
                            return [4 /*yield*/, ordercloudBulk.CreateAll(resource, records)];
                        case 1:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            });
        }
        // Patch Spec.DefaultOptionID after the options are created.
        function UploadSpecOptions(resource) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0: return [4 /*yield*/, ordercloudBulk.CreateAll(resource, records)];
                        case 1:
                            _a.sent();
                            return [4 /*yield*/, ordercloudBulk.Run("SpecOption", specDefaultOptionIDList, function (x) { return ordercloudJavascriptSdk.Specs.Patch(x.ID, { DefaultOptionID: x.DefaultOptionID }); })];
                        case 2:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            });
        }
        function UploadApiClients(resource) {
            return __awaiter(this, void 0, void 0, function () {
                var results, i;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            records.forEach(function (r) {
                                if (r.ClientSecret === REDACTED_MESSAGE) {
                                    r.ClientSecret = Random.generateClientSecret();
                                }
                            });
                            return [4 /*yield*/, ordercloudBulk.CreateAll(resource, records)];
                        case 1:
                            results = _a.sent();
                            // Now that we have created the APIClients, we actually know what their IDs are.  
                            for (i = 0; i < records.length; i++) {
                                apiClientIDMap[records[i].ID] = results[i].ID;
                            }
                            return [2 /*return*/];
                    }
                });
            });
        }
        function UploadImpersonationConfigs(resource) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            records.forEach(function (r) { return r.ClientID = apiClientIDMap[r.ClientID]; });
                            return [4 /*yield*/, ordercloudBulk.CreateAll(resource, records)];
                        case 1:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            });
        }
        function UploadOpenIdConnects(resource) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            records.forEach(function (r) { return r.OrderCloudApiClientID = apiClientIDMap[r.OrderCloudApiClientID]; });
                            return [4 /*yield*/, ordercloudBulk.CreateAll(resource, records)];
                        case 1:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            });
        }
        function UploadCategories(resource) {
            return __awaiter(this, void 0, void 0, function () {
                var depthCohort, results;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            depthCohort = records.filter(function (r) { return r.ParentID === null; });
                            _a.label = 1;
                        case 1:
                            if (!(depthCohort.length > 0)) return [3 /*break*/, 3];
                            return [4 /*yield*/, ordercloudBulk.CreateAll(resource, depthCohort)];
                        case 2:
                            results = _a.sent();
                            // get children of those just created
                            depthCohort = records.filter(function (r) { return results.some(function (result) { return r.ParentID === result.ID; }); });
                            return [3 /*break*/, 1];
                        case 3: return [2 /*return*/];
                    }
                });
            });
        }
        function UploadMessageSenders(resource) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            records.forEach(function (r) {
                                if (r.SharedKey === REDACTED_MESSAGE) {
                                    r.SharedKey = webhookSecret;
                                }
                            });
                            return [4 /*yield*/, ordercloudBulk.CreateAll(resource, records)];
                        case 1:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            });
        }
        function UploadIntegrationEvents(resource) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            records.forEach(function (r) {
                                if (r.HashKey === REDACTED_MESSAGE) {
                                    r.HashKey = webhookSecret;
                                }
                            });
                            return [4 /*yield*/, ordercloudBulk.CreateAll(resource, records)];
                        case 1:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            });
        }
        function UploadWebhooks(resource) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            records.forEach(function (r) {
                                r.ApiClientIDs = r.ApiClientIDs.map(function (id) { return apiClientIDMap[id]; });
                                if (r.HashKey === REDACTED_MESSAGE) {
                                    r.HashKey = webhookSecret;
                                }
                            });
                            return [4 /*yield*/, ordercloudBulk.CreateAll(resource, records)];
                        case 1:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            });
        }
        function UploadApiClientAssignments(resource) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            records.forEach(function (r) { return r.ApiClientID = apiClientIDMap[r.ApiClientID]; });
                            return [4 /*yield*/, ordercloudBulk.CreateAll(resource, records)];
                        case 1:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            });
        }
        var username, password, marketplaceID, marketplaceName, portalToken, rawData, dataUrl, _d, logger, template, validateResponse, portal, org_token, marketplaceData, ordercloudBulk, apiClientIDMap, specDefaultOptionIDList, webhookSecret, directory, _i, _g, resource, records, apiClients, results;
        return __generator(this, function (_h) {
            switch (_h.label) {
                case 0:
                    username = args.username, password = args.password, marketplaceID = args.marketplaceID, marketplaceName = args.marketplaceName, portalToken = args.portalToken, rawData = args.rawData, dataUrl = args.dataUrl, _d = args.logger, logger = _d === void 0 ? defaultLogger : _d;
                    template = templates.find(function (x) { return x.name === dataUrl; });
                    if (!___default['default'].isNil(template)) {
                        dataUrl = template.dataUrl;
                    }
                    return [4 /*yield*/, validate({ rawData: rawData, dataUrl: dataUrl })];
                case 1:
                    validateResponse = _h.sent();
                    if (((_a = validateResponse === null || validateResponse === void 0 ? void 0 : validateResponse.errors) === null || _a === void 0 ? void 0 : _a.length) !== 0)
                        return [2 /*return*/];
                    portal = new PortalAPI();
                    if (!___default['default'].isNil(portalToken)) return [3 /*break*/, 5];
                    if (___default['default'].isNil(username) || ___default['default'].isNil(password)) {
                        return [2 /*return*/, logger("Missing required arguments: username and password", MessageType.Error)];
                    }
                    _h.label = 2;
                case 2:
                    _h.trys.push([2, 4, , 5]);
                    return [4 /*yield*/, portal.login(username, password)];
                case 3:
                    portalToken = (_h.sent()).access_token;
                    return [3 /*break*/, 5];
                case 4:
                    _h.sent();
                    return [2 /*return*/, logger("Username \"" + username + "\" and Password \"" + password + "\" were not valid", MessageType.Error)];
                case 5:
                    // Confirm orgID doesn't already exist
                    marketplaceID = marketplaceID || Random.generateOrgID();
                    _h.label = 6;
                case 6:
                    _h.trys.push([6, 8, , 9]);
                    return [4 /*yield*/, portal.GetOrganization(marketplaceID, portalToken)];
                case 7:
                    _h.sent();
                    return [2 /*return*/, logger("A marketplace with ID \"" + marketplaceID + "\" already exists.", MessageType.Error)];
                case 8:
                    _h.sent();
                    return [3 /*break*/, 9];
                case 9:
                    // Create Marketplace
                    marketplaceName = marketplaceName || ((_c = (_b = dataUrl === null || dataUrl === void 0 ? void 0 : dataUrl.split("/")) === null || _b === void 0 ? void 0 : _b.pop()) === null || _c === void 0 ? void 0 : _c.split(".")[0]) || marketplaceID;
                    return [4 /*yield*/, portal.CreateOrganization(marketplaceID, marketplaceName, portalToken)];
                case 10:
                    _h.sent();
                    logger("Created new marketplace with Name \"" + marketplaceName + "\" and ID \"" + marketplaceID + "\".", MessageType.Success);
                    return [4 /*yield*/, portal.getOrganizationToken(marketplaceID, portalToken)];
                case 11:
                    org_token = _h.sent();
                    ordercloudJavascriptSdk.Configuration.Set({ baseApiUrl: ORDERCLOUD_URLS.sandbox }); // always sandbox for upload
                    ordercloudJavascriptSdk.Tokens.SetAccessToken(org_token);
                    marketplaceData = new SerializedMarketplace(validateResponse.rawData);
                    ordercloudBulk = new OrderCloudBulk(new Bottleneck__default['default']({
                        minTime: 100,
                        maxConcurrent: 6
                    }));
                    apiClientIDMap = {};
                    specDefaultOptionIDList = [];
                    webhookSecret = Random.generateWebhookSecret();
                    return [4 /*yield*/, BuildResourceDirectory(false)];
                case 12:
                    directory = _h.sent();
                    _i = 0, _g = directory.sort(function (a, b) { return a.createPriority - b.createPriority; });
                    _h.label = 13;
                case 13:
                    if (!(_i < _g.length)) return [3 /*break*/, 37];
                    resource = _g[_i];
                    records = marketplaceData.GetRecords(resource);
                    SetOwnerID(resource, records);
                    if (!(resource.name === OCResourceEnum.ApiClients)) return [3 /*break*/, 15];
                    return [4 /*yield*/, UploadApiClients(resource)];
                case 14:
                    _h.sent();
                    return [3 /*break*/, 35];
                case 15:
                    if (!(resource.name === OCResourceEnum.ImpersonationConfigs)) return [3 /*break*/, 17];
                    return [4 /*yield*/, UploadImpersonationConfigs(resource)];
                case 16:
                    _h.sent();
                    return [3 /*break*/, 35];
                case 17:
                    if (!(resource.name === OCResourceEnum.Specs)) return [3 /*break*/, 19];
                    return [4 /*yield*/, UploadSpecs(resource)];
                case 18:
                    _h.sent();
                    return [3 /*break*/, 35];
                case 19:
                    if (!(resource.name === OCResourceEnum.SpecOptions)) return [3 /*break*/, 21];
                    return [4 /*yield*/, UploadSpecOptions(resource)];
                case 20:
                    _h.sent();
                    return [3 /*break*/, 35];
                case 21:
                    if (!(resource.name === OCResourceEnum.Webhooks)) return [3 /*break*/, 23];
                    return [4 /*yield*/, UploadWebhooks(resource)];
                case 22:
                    _h.sent();
                    return [3 /*break*/, 35];
                case 23:
                    if (!(resource.name === OCResourceEnum.OpenIdConnects)) return [3 /*break*/, 25];
                    return [4 /*yield*/, UploadOpenIdConnects(resource)];
                case 24:
                    _h.sent();
                    return [3 /*break*/, 35];
                case 25:
                    if (!(resource.name === OCResourceEnum.ApiClientAssignments)) return [3 /*break*/, 27];
                    return [4 /*yield*/, UploadApiClientAssignments(resource)];
                case 26:
                    _h.sent();
                    return [3 /*break*/, 35];
                case 27:
                    if (!(resource.name === OCResourceEnum.MessageSenders)) return [3 /*break*/, 29];
                    return [4 /*yield*/, UploadMessageSenders(resource)];
                case 28:
                    _h.sent();
                    return [3 /*break*/, 35];
                case 29:
                    if (!(resource.name === OCResourceEnum.IntegrationEvents)) return [3 /*break*/, 31];
                    return [4 /*yield*/, UploadIntegrationEvents(resource)];
                case 30:
                    _h.sent();
                    return [3 /*break*/, 35];
                case 31:
                    if (!(resource.name === OCResourceEnum.Categories)) return [3 /*break*/, 33];
                    return [4 /*yield*/, UploadCategories(resource)];
                case 32:
                    _h.sent();
                    return [3 /*break*/, 35];
                case 33: return [4 /*yield*/, ordercloudBulk.CreateAll(resource, records)];
                case 34:
                    _h.sent();
                    _h.label = 35;
                case 35:
                    if (records.length != 0) {
                        logger("Created " + records.length + " " + resource.name + ".", MessageType.Progress);
                    }
                    _h.label = 36;
                case 36:
                    _i++;
                    return [3 /*break*/, 13];
                case 37: return [4 /*yield*/, GenerateAndPatchVariants()];
                case 38:
                    _h.sent();
                    logger("Done! Seeded a new marketplace with ID \"" + marketplaceID + "\" and Name \"" + marketplaceName + "\".", MessageType.Success);
                    apiClients = marketplaceData.Objects[OCResourceEnum.ApiClients].map(function (apiClient) {
                        apiClient.ID = apiClientIDMap[apiClient.ID];
                        return apiClient;
                    });
                    results = {
                        marketplaceName: marketplaceName,
                        marketplaceID: marketplaceID,
                        accessToken: org_token,
                        apiClients: apiClients
                    };
                    return [2 /*return*/, results];
            }
        });
    });
}

function download(args) {
    return __awaiter(this, void 0, void 0, function () {
        function RedactSensitiveFields(resource, records) {
            if (resource.redactFields.length === 0)
                return;
            for (var _i = 0, records_2 = records; _i < records_2.length; _i++) {
                var record = records_2[_i];
                for (var _a = 0, _b = resource.redactFields; _a < _b.length; _a++) {
                    var field = _b[_a];
                    if (!___default['default'].isNil(record[field])) {
                        record[field] = REDACTED_MESSAGE;
                    }
                }
            }
        }
        function PlaceHoldMarketplaceID(resource, records) {
            if (resource.hasOwnerIDField) {
                for (var _i = 0, records_3 = records; _i < records_3.length; _i++) {
                    var record = records_3[_i];
                    if (record.OwnerID === marketplaceID) {
                        record.OwnerID = MARKETPLACE_ID;
                    }
                }
            }
        }
        var username, password, environment, marketplaceID, portalToken, _a, logger, validEnvironments, url, portal, org_token, e_1, decoded, ordercloudBulk, marketplace, directory, _i, directory_1, resource, records, _c, records_1, product, variants, _loop_1, childRecords, _d, _e, childResourceName;
        return __generator(this, function (_f) {
            switch (_f.label) {
                case 0:
                    username = args.username, password = args.password, environment = args.environment, marketplaceID = args.marketplaceID, portalToken = args.portalToken, _a = args.logger, logger = _a === void 0 ? defaultLogger : _a;
                    validEnvironments = ['staging', 'sandbox', 'prod'];
                    if (!marketplaceID) {
                        return [2 /*return*/, logger("Missing required argument: marketplaceID", MessageType.Error)];
                    }
                    if (!validEnvironments.includes(environment)) {
                        return [2 /*return*/, logger("environment must be one of " + validEnvironments.join(", "), MessageType.Error)];
                    }
                    url = ORDERCLOUD_URLS[environment];
                    // Set up configuration
                    ordercloudJavascriptSdk.Configuration.Set({ baseApiUrl: url });
                    portal = new PortalAPI();
                    if (!___default['default'].isNil(portalToken)) return [3 /*break*/, 4];
                    if (___default['default'].isNil(username) || ___default['default'].isNil(password)) {
                        return [2 /*return*/, logger("Missing required arguments: username and password", MessageType.Error)];
                    }
                    _f.label = 1;
                case 1:
                    _f.trys.push([1, 3, , 4]);
                    return [4 /*yield*/, portal.login(username, password)];
                case 2:
                    portalToken = (_f.sent()).access_token;
                    return [3 /*break*/, 4];
                case 3:
                    _f.sent();
                    return [2 /*return*/, logger("Username \"" + username + "\" and Password \"" + password + "\" were not valid", MessageType.Error)];
                case 4:
                    _f.trys.push([4, 6, , 7]);
                    return [4 /*yield*/, portal.getOrganizationToken(marketplaceID, portalToken)];
                case 5:
                    org_token = _f.sent();
                    return [3 /*break*/, 7];
                case 6:
                    e_1 = _f.sent();
                    console.log(JSON.stringify(e_1));
                    return [2 /*return*/, logger("Marketplace with ID \"" + marketplaceID + "\" not found", MessageType.Error)];
                case 7:
                    decoded = jwt_decode__default['default'](org_token);
                    if (decoded.aud !== url) {
                        return [2 /*return*/, logger("Marketplace \"" + marketplaceID + "\" found, but is not in environment \"" + environment + "\"", MessageType.Error)];
                    }
                    ordercloudJavascriptSdk.Tokens.SetAccessToken(org_token);
                    logger("Found your Marketplace \"" + marketplaceID + "\" . Beginning download.", MessageType.Success);
                    ordercloudBulk = new OrderCloudBulk(new Bottleneck__default['default']({
                        minTime: 100,
                        maxConcurrent: 8
                    }));
                    marketplace = new SerializedMarketplace();
                    return [4 /*yield*/, BuildResourceDirectory(false)];
                case 8:
                    directory = _f.sent();
                    _i = 0, directory_1 = directory;
                    _f.label = 9;
                case 9:
                    if (!(_i < directory_1.length)) return [3 /*break*/, 20];
                    resource = directory_1[_i];
                    if (resource.isChild) {
                        return [3 /*break*/, 19]; // resource will be handled as part of its parent
                    }
                    return [4 /*yield*/, ordercloudBulk.ListAll(resource)];
                case 10:
                    records = _f.sent();
                    RedactSensitiveFields(resource, records);
                    PlaceHoldMarketplaceID(resource, records);
                    if (resource.downloadTransformFunc !== undefined) {
                        records = records.map(resource.downloadTransformFunc);
                    }
                    if (!(resource.name === OCResourceEnum.Products)) return [3 /*break*/, 14];
                    _c = 0, records_1 = records;
                    _f.label = 11;
                case 11:
                    if (!(_c < records_1.length)) return [3 /*break*/, 14];
                    product = records_1[_c];
                    if (!(product.VariantCount > 0)) return [3 /*break*/, 13];
                    return [4 /*yield*/, ordercloudBulk.ListAllWithFunction("Variants", ordercloudJavascriptSdk.Products.ListVariants, product.ID)];
                case 12:
                    variants = _f.sent();
                    product[VARIANTS_PROPERTY] = variants;
                    _f.label = 13;
                case 13:
                    _c++;
                    return [3 /*break*/, 11];
                case 14:
                    marketplace.AddRecords(resource, records);
                    _loop_1 = function (childResourceName) {
                        var childResource, _g, records_4, parentRecord, _h, childRecords_1, childRecord;
                        return __generator(this, function (_j) {
                            switch (_j.label) {
                                case 0:
                                    childResource = directory.find(function (x) { return x.name === childResourceName; });
                                    _g = 0, records_4 = records;
                                    _j.label = 1;
                                case 1:
                                    if (!(_g < records_4.length)) return [3 /*break*/, 4];
                                    parentRecord = records_4[_g];
                                    return [4 /*yield*/, ordercloudBulk.ListAll(childResource, parentRecord.ID)];
                                case 2:
                                    childRecords = _j.sent(); // assume ID exists. Which is does for all parent types.
                                    for (_h = 0, childRecords_1 = childRecords; _h < childRecords_1.length; _h++) {
                                        childRecord = childRecords_1[_h];
                                        childRecord[childResource.parentRefField] = parentRecord.ID;
                                    }
                                    if (childResource.downloadTransformFunc !== undefined) {
                                        childRecords = childRecords.map(resource.downloadTransformFunc);
                                    }
                                    marketplace.AddRecords(childResource, childRecords);
                                    _j.label = 3;
                                case 3:
                                    _g++;
                                    return [3 /*break*/, 1];
                                case 4:
                                    if (childRecords.length !== 0) {
                                        logger("Found " + childRecords.length + " " + childResourceName);
                                    }
                                    return [2 /*return*/];
                            }
                        });
                    };
                    _d = 0, _e = resource.children;
                    _f.label = 15;
                case 15:
                    if (!(_d < _e.length)) return [3 /*break*/, 18];
                    childResourceName = _e[_d];
                    return [5 /*yield**/, _loop_1(childResourceName)];
                case 16:
                    _f.sent();
                    _f.label = 17;
                case 17:
                    _d++;
                    return [3 /*break*/, 15];
                case 18:
                    if (records.length !== 0) {
                        logger("Found " + records.length + " " + resource.name);
                    }
                    _f.label = 19;
                case 19:
                    _i++;
                    return [3 /*break*/, 9];
                case 20:
                    // Write to file
                    logger("Done downloading data from org \"" + marketplaceID + "\".", MessageType.Success);
                    return [2 /*return*/, marketplace];
            }
        });
    });
}

exports.download = download;
exports.seed = seed;
exports.validate = validate;
