UNPKG

@esri/solution-common

Version:

Provides general helper functions for @esri/solution.js.

171 lines 7.54 kB
"use strict"; /** @license * Copyright 2018 Esri * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.topologicallySortItems = void 0; /** * Provides common functions for organizing dependencies among items. * * @module dependencies */ const generalHelpers_1 = require("./generalHelpers"); const templatization_1 = require("./templatization"); // ------------------------------------------------------------------------------------------------------------------ // /** * Topologically sorts a list of items into a build list. * * @param templates A collection of AGO item templates * @returns An object containing three parts: a list of ids of items in the order in which they need to be built * so that dependencies are built before items that require those dependencies, a list of item ids found in a * template's dependencies but not present in the supplied list of templates, and a dictionary containing items * that need to be post-processed due to dependency cycles. */ function topologicallySortItems(templates) { // Cormen, Thomas H.; Leiserson, Charles E.; Rivest, Ronald L.; Stein, Clifford (2009) // Sections 22.3 (Depth-first search) & 22.4 (Topological sort), pp. 603-615 // Introduction to Algorithms (3rd ed.), The MIT Press, ISBN 978-0-262-03384-8 // // DFS(G) // 1 for each vertex u ∈ G,V // 2 u.color = WHITE (not yet visited) // 3 u.π = NIL // 4 time = 0 // 5 for each vertex u ∈ G,V // 6 if u.color == WHITE // 7 DFS-VISIT(G,u) // // DFS-VISIT(G,u) // 1 time = time + 1 (white vertex u has just been discovered) // 2 u.d = time // 3 u.color = GRAY (visited, in progress) // 4 for each v ∈ G.Adj[u] (explore edge (u,v)) // 5 if v.color == WHITE // 6 v.π = u // 7 DFS-VISIT(G,v) // 8 u.color = BLACK (blacken u; it is finished) // 9 time = time + 1 // 10 u.f = time // // TOPOLOGICAL-SORT(G) // 1 call DFS(G) to compute finishing times v.f for each vertex v // 2 as each vertex is finished, insert it onto front of a linked list // 3 return the linked list of vertices let buildOrder = []; // list of ordered vertices--don't need linked list because // we just want relative ordering const missingDependencies = []; const itemsToBePatched = {}; const verticesToVisit = {}; const vertexType = {}; templates.forEach(function (template) { verticesToVisit[template.itemId] = ESortVisitState.NotYetVisited; vertexType[template.itemId] = template.item?.typeKeywords && template.item.typeKeywords.includes("View Service") ? "View Service" : template.item?.tags && template.item.tags.includes("Location Tracking Group") ? "Location Tracking Group" : template.type; }); // Algorithm visits each vertex once. Don't need to record times or "from' nodes ("π" in pseudocode) templates.forEach(function (template) { if (verticesToVisit[template.itemId] === ESortVisitState.NotYetVisited) { // if not yet visited visit(template.itemId); } }); // Visit vertex function visit(vertexId) { verticesToVisit[vertexId] = ESortVisitState.InProgress; // visited, in progress // Visit dependents if not already visited; template has to be in templates list because calls to visit() // are based on verticiesToVisit[], which is initialized using the templates list const template = (0, templatization_1.findTemplateInList)(templates, vertexId); // There are two sources of dependencies const dependencies = (template.dependencies || []).concat((0, generalHelpers_1.getProp)(template, "properties.syncViews") || []); dependencies.forEach(function (dependencyId) { if (verticesToVisit[dependencyId] === ESortVisitState.NotYetVisited) { // if not yet visited visit(dependencyId); } else if (verticesToVisit[dependencyId] === ESortVisitState.InProgress) { // visited, in progress, therefore a cycle // save the dependency that needs to be patched if (itemsToBePatched[vertexId]) { itemsToBePatched[vertexId].push(dependencyId); } else { itemsToBePatched[vertexId] = [dependencyId]; } } else if (verticesToVisit[dependencyId] !== ESortVisitState.Finished) { /* istanbul ignore else */ if (missingDependencies.indexOf(dependencyId) < 0) { missingDependencies.push(dependencyId); } } }); verticesToVisit[vertexId] = ESortVisitState.Finished; buildOrder.push(vertexId); // add to end of list of ordered vertices because we want dependents first } // Two special circumstances to deal with // 1) Location Tracking Group needs to be created before its associated view because the groupId is used in the view name // - Move tracking groups to the front of the list // 2) Feature Services need to be created before views that rely on them // - Move all of the feature services to the beginning of the build order while maintaining their relative ordering // - Distinguish between base feature services and view feature services const trackerGroupIds = []; const fsIds = []; const fsViewIds = []; const otherIds = []; do { const id = buildOrder.shift(); if (id) { if (vertexType[id] === "Location Tracking Group") { trackerGroupIds.push(id); } else if (vertexType[id] === "Feature Service") { fsIds.push(id); } else if (vertexType[id] === "View Service") { fsViewIds.push(id); } else { otherIds.push(id); } } } while (buildOrder.length > 0); buildOrder = trackerGroupIds.concat(fsIds, fsViewIds, otherIds); const orderingResults = { buildOrder: buildOrder, missingDependencies: missingDependencies, itemsToBePatched: itemsToBePatched, }; return orderingResults; } exports.topologicallySortItems = topologicallySortItems; /** * A visit flag used in the topological sort algorithm. * * @private */ var ESortVisitState; (function (ESortVisitState) { /** not yet visited */ ESortVisitState[ESortVisitState["NotYetVisited"] = 0] = "NotYetVisited"; /** visited, in progress */ ESortVisitState[ESortVisitState["InProgress"] = 1] = "InProgress"; /** finished */ ESortVisitState[ESortVisitState["Finished"] = 2] = "Finished"; })(ESortVisitState || (ESortVisitState = {})); //# sourceMappingURL=dependencies.js.map