Protractor endurance test memory leak - selenium-webdriver

I made a very small test using protractor to open my application (build on angular) and start clicking at random links in a random order. We use this to let the web application run for several days and check for memory leaks.
After some time the memory of the browser (chrome) is huge and when i create a memory dump i find 33k strings all containing the same piece of code (shown below).
I tried searching where this was coming from and it that led me to Webdriver and the code looks like something injected by webdriver to trigger the mouse click ability but from there i'm lost.
Am i doing something wrong or should i clean the memory somewhere in my test?
(When i run the application normally for multiple hours and opening all pages there is no occurrence at all of this string)
"(function() { // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* Enum for WebDriver status codes.
* #enum {number}
*/
var StatusCode = { STALE_ELEMENT_REFERENCE: 10, JAVA_SCRIPT_ERROR: 17, };
/**
* Enum for node types.
* #enum {number}
*/
var NodeType = { ELEMENT: 1, DOCUMENT: 9, };
/**
* Dictionary key to use for holding an element ID.
* #const
* #type {string}
*/
var ELEMENT_KEY = 'ELEMENT';
/**
* True if using W3C Element references.
* #const
* #type {boolean}
*/
var w3cEnabled = false;
/**
* True if shadow dom is enabled.
* #const
* #type {boolean}
*/
var SHADOW_DOM_ENABLED = typeof ShadowRoot === 'function';
/**
* Generates a unique ID to identify an element.
* #void
* #return {string} Randomly generated ID.
*/
function generateUUID() {
var array = new Uint8Array(16);
window.crypto.getRandomValues(array);
array[6] = 0x40 | (array[6] & 0x0f);"
.... (continues for a lot more lines)
The test i try to run is:
import {browser, by, element} from 'protractor';
describe('Webclient', () => {
const MAX_SAFE_TIMEOUT = Math.pow(2, 31) - 1;
beforeAll(async function () {
await browser.get('/');
});
it('Endurance test', (done) => {
runTest();
}, MAX_SAFE_TIMEOUT);
});
let counter = 0;
async function runTest() {
try {
const elements = await element.all(by.css('.header a, .sidebar-left a, .footer a, .content a'));
let random = Math.floor(Math.random() * elements.length);
while (!(await elements[random].isDisplayed())) {
random = Math.floor(Math.random() * elements.length);
}
await elements[random].click();
console.log('click counter:', counter++, await browser.getCurrentUrl());
} catch (e) {
// Do nothing, just continue
}
// Break the chain so that we don't get one big recursive function
setTimeout(() => {
runTest();
}, 0);
}

Related

Run N/Task on multiple saved searches at once

Is there a straightforward way to modify the below code to execute on multiple saved searches and files simultaneously? I have all the file and search id's. but rather than creating 50 different scripts for each one can I just execute on a block of IDs by altering the below?
Thank you in advance.
All the Oracle documentation only seems to specify how to run these tasks on 1 item.
I know that a map/reduce script is a good path to go down for handling larger amounts of data, is that the way with this or can the below just be tweaked slightly?
/**
* #NApiVersion 2.x
* #NScriptType ScheduledScript
* #NModuleScope SameAccount
*/
define(['N/task'],
/**
* #param {record} record
* #param {search} search
*/
function(task) {
var FILE_ID = 433961;
var SEARCH_ID = 1610;
function execute(scriptContext) {
var searchTask = task.create({
taskType: task.TaskType.SEARCH
});
searchTask.savedSearchId = SEARCH_ID;
searchTask.fileId = FILE_ID;
var searchTaskId = searchTask.submit();
}
return {
execute: execute
};
});
/**
* #NApiVersion 2.x
* #NScriptType ScheduledScript
* #NModuleScope SameAccount
*/
define(['N/task'], function (task) {
const todos = [
{
FILE_ID: 433961,
SEARCH_ID: 1610
},
{
FILE_ID: '...',
SEARCH_ID: '...'
},
// ...
]
function execute(scriptContext) {
todos.forEach(function(todo) {
var searchTask = task.create({
taskType: task.TaskType.SEARCH
});
searchTask.savedSearchId = todo.SEARCH_ID;
searchTask.fileId = todo.FILE_ID;
var searchTaskId = searchTask.submit();
})
}
return {
execute: execute
};
});

Forge Autodesk load multiple models in Viewer with ReactJS

We are using Forge Autodesk Viewer to load Forge Models.
We are using the framework ReactJS for our application and we have the function bellow to load one model at a viewer :
function loadModel(viewer, documentId) {
function onDocumentLoadSuccess(viewerDocument) {
// viewerDocument is an instance of Autodesk.Viewing.Document
const bubbleNode = viewerDocument.getRoot();
let defaultModel;
if (props.phaseName) {
defaultModel = bubbleNode.getMasterView(props.phaseName);
} else if (props.guid) {
defaultModel = bubbleNode.findByGuid(props.guid);
} else if (props.viewableID) {
const results = bubbleNode.search({viewableID: props.viewableID});
if (results && results.length) {
defaultModel = results[0];
}
} else if (props.geomIndex) {
const geoms = bubbleNode.search({type: "geometry"});
if (geoms.length) {
if (props.geomIndex < 0 || props.geomIndex >= geoms.length) {
console.warn("GeometryIndex Error: Invalid geometry index.");
}
const index = Math.min(Math.max(props.geomIndex, 0), geoms.length - 1); // Ensure index is valid.
defaultModel = geoms[index];
}
}
if (!defaultModel) defaultModel = bubbleNode.getDefaultGeometry(true);
const skipHiddenFragments = props.skipHiddenFragments || false;
viewer.loadDocumentNode(viewerDocument, defaultModel, {
keepCurrentModels: true,
skipHiddenFragments: skipHiddenFragments,
});
viewer.prefs.set("ghosting", false);
viewer.loadExtension("Autodesk.Viewing.MarkupsCore")
viewer.loadExtension("Autodesk.Viewing.MarkupsGui")
}
function onDocumentLoadFailure() {
console.error("Failed fetching Forge manifest");
}
if (documentId) {
Autodesk.Viewing.Document.load(
documentId,
onDocumentLoadSuccess,
onDocumentLoadFailure
);
} else {
props.eventBus.dispatchEvent({type: "VIEWER_READY", data: {viewer}});
}
}
We actually want to know how we could load multiple models using ReactJS.
Thank you for your response.
There is no difference in making the code support multiple models between w/ and w/o react.js. You can find lots of examples by searching https://stackoverflow.com/search?q=%5Bautodesk-forge%5D+multiple+models
Anyway...
Here is one for loading multiple models, but you must change the props passed to the Viewer component, and event calls outside the Viewer component accordingly.
//process each promise
//refer to http://jsfiddle.net/jfriend00/h3zaw8u8/
const promisesInSequence = (tasks, callback) => {
const results = [];
return tasks.reduce((p, item) => {
return p.then(() => {
return callback(item).then((data) => {
results.push(data);
return results;
});
});
}, Promise.resolve());
};
const AGGREGATE_GEOMETRY_LOADED_EVENT = 'aggregateGeometryLoaded';
/**
* #component
* Component for rendering LMV
* #param {Object} props
* #param {("AutodeskProduction"|"AutodeskStaging"|"MD20ProdUS"|"MD20ProdEU")} [props.env] Forge API environment
* #param {Function} props.getToken Returns the Forge API token to access LMV
* #param {"derivativeV2"|"derivativeV2_EU"|"modelDerivativeV2"|"fluent"|"D3S"|"D3S_EU"} [props.api] Default = "derivativeV2". Please refer to LMV documentation for more information.
* #param {Object[]} [props.docUrns] Model data to be loaded
* #param {string} [modelURNs[].urn] Document URN of the model to be loaded
* #param {Object} [modelURNs[].options] model options used in loading the specfic model
* #param {string} [modelURNs[].options.phaseName] phaseName of view to load in scene.
* #param {string} [modelURNs[].options.guid] guid of BubbleNode to load in scene.
* #param {string} [modelURNs[].options.viewableID] viewableID of BubbleNode to load in scene.
* #param {number} [modelURNs[].options.geomIndex] Index of geometry to load in scene.
* #param {Boolean} [modelURNs[].options.skipHiddenFragments] Boolean to specify if hidden fragments should be skipped (Default: false,
* Hidden fragments are required for heatmaps in rooms, only applicable to SVF2)
* #param {OnModelLoaded} [props.onModelLoaded] Callback function invoked when the model has loaded
* #param {OnViewerInitialized} [props.onViewerInitialized] Callback function invoked when LMV has been intialized
* #param {string[]} [props.extensions] List of extension ids forwarded to viewer config to load.
* #param {Object.<string, Object>} [props.disabledExtensions] Default extensions to prevent being loaded.
* #param {Object} [props.viewerOptions] Options object to forward to Autodesk.Viewing.Initializer
* #memberof Autodesk.DataVisualization.UI
* #alias Autodesk.DataVisualization.UI.Viewer
*/
export default function Viewer(props) {
const viewerRef = useRef(null);
const viewerDomRef = useRef(null);
function onModelLoaded(event) {
const viewer = viewerRef.current;
// const av = Autodesk.Viewing;
// viewer.removeEventListener(av.GEOMETRY_LOADED_EVENT, onModelLoaded);
viewer.removeEventListener(AGGREGATE_GEOMETRY_LOADED_EVENT, onModelLoaded);
if (props.onModelLoaded) {
props.onModelLoaded(viewer, event);
}
}
/**
* Initializes LMV.
*
* #memberof Autodesk.DataVisualization.UI
* #alias Autodesk.DataVisualization.UI.Viewer#initializeViewer
* #private
*/
function initializeViewer() {
let viewerOptions = props.viewerOptions;
var options = Object.assign({}, viewerOptions, {
env: props.env,
api: props.api || "derivativeV2", // for models uploaded to EMEA change this option to 'derivativeV2_EU'
getAccessToken: async function (onTokenReady) {
let token = await props.getToken();
var timeInSeconds = 3600; // Use value provided by Forge Authentication (OAuth) API
onTokenReady(token, timeInSeconds);
},
});
Autodesk.Viewing.Initializer(options, async function () {
const extensionsToLoad = props.extensions;
const extensionsWithConfig = [];
const extensionsWithoutConfig = [];
for (let key in extensionsToLoad) {
const config = extensionsToLoad[key];
if (Object.keys(config).length === 0) {
extensionsWithoutConfig.push(key);
} else {
extensionsWithConfig.push(key);
}
}
const viewer = new Autodesk.Viewing.GuiViewer3D(viewerDomRef.current, {
extensions: extensionsWithoutConfig,
disabledExtensions: props.disabledExtensions || {},
});
extensionsWithConfig.forEach((ext) => {
viewer.loadExtension(ext, extensionsToLoad[ext]);
});
viewerRef.current = viewer;
const startedCode = viewer.start(undefined, undefined, undefined, undefined, options);
if (startedCode > 0) {
console.error("Failed to create a Viewer: WebGL not supported.");
return;
}
// loadModel(viewer, props.docUrn);
await loadModels(viewer, props.docUrns);
if (props.onViewerInitialized) {
props.onViewerInitialized(viewer);
}
});
}
/**
* Loads the specified models into the viewer.
*
* #param {Object} viewer Initialized LMV object
* #param {string} documentId Document URN of the model to be loaded
* #param {Object} options
* #param {string} [options.phaseName] phaseName of view to load in scene.
* #param {string} [options.guid] guid of BubbleNode to load in scene.
* #param {string} [options.viewableID] viewableID of BubbleNode to load in scene.
* #param {number} [options.geomIndex] Index of geometry to load in scene.
* #param {Boolean} [options.skipHiddenFragments] Boolean to specify if hidden fragments should be skipped (Default: false,
* Hidden fragments are required for heatmaps in rooms, only applicable to SVF2)
* #memberof Autodesk.DataVisualization.UI
* #alias Autodesk.DataVisualization.UI.Viewer#loadModelAsync
* #private
*/
async function loadModelAsync(viewer, documentId, options) {
return new Promise((resolve, reject) => {
async function onDocumentLoadSuccess(viewerDocument) {
// viewerDocument is an instance of Autodesk.Viewing.Document
const bubbleNode = viewerDocument.getRoot();
let defaultModel;
if (options.phaseName) {
defaultModel = bubbleNode.getMasterView(options.phaseName);
} else if (options.guid) {
defaultModel = bubbleNode.findByGuid(options.guid);
} else if (options.viewableID) {
const results = bubbleNode.search({ viewableID: options.viewableID });
if (results && results.length) {
defaultModel = results[0];
}
} else if (options.geomIndex) {
const geoms = bubbleNode.search({ type: "geometry" });
if (geoms.length) {
if (options.geomIndex < 0 || options.geomIndex >= geoms.length) {
console.warn("GeometryIndex Error: Invalid geometry index.");
}
const index = Math.min(Math.max(options.geomIndex, 0), geoms.length - 1); // Ensure index is valid.
defaultModel = geoms[index];
}
}
if (!defaultModel) defaultModel = bubbleNode.getDefaultGeometry(true);
const skipHiddenFragments = options.skipHiddenFragments || false;
let model = await viewer.loadDocumentNode(viewerDocument, defaultModel, {
keepCurrentModels: true,
skipHiddenFragments: skipHiddenFragments,
});
// modify the preference settings, since ghosting is causing heavy z-fighting with the room geometry
// it would be good we turn it off
if (!viewer.model)
viewer.prefs.set("ghosting", false);
await viewer.waitForLoadDone();
resolve(model);
}
function onDocumentLoadFailure() {
console.error("Failed fetching Forge manifest");
}
if (documentId) {
Autodesk.Viewing.Document.load(
documentId,
onDocumentLoadSuccess,
onDocumentLoadFailure
);
} else {
props.eventBus.dispatchEvent({ type: "VIEWER_READY", data: { viewer } });
}
});
}
/**
* Loads the specified models into the viewer.
*
* #param {Object} viewer Initialized LMV object
* #param {Object[]} modelURNs Model data to be loaded
* #param {string} [modelURNs[].urn] Document URN of the model to be loaded
* #param {Object} [modelURNs[].options] model options used in loading the specfic model
* #param {string} [modelURNs[].options.phaseName] phaseName of view to load in scene.
* #param {string} [modelURNs[].options.guid] guid of BubbleNode to load in scene.
* #param {string} [modelURNs[].options.viewableID] viewableID of BubbleNode to load in scene.
* #param {number} [modelURNs[].options.geomIndex] Index of geometry to load in scene.
* #param {Boolean} [modelURNs[].options.skipHiddenFragments] Boolean to specify if hidden fragments should be skipped (Default: false,
* Hidden fragments are required for heatmaps in rooms, only applicable to SVF2)
* #memberof Autodesk.DataVisualization.UI
* #alias Autodesk.DataVisualization.UI.Viewer#loadModelAsync
* #private
*/
async function loadModelsAsync(viewer, modelURNs) {
// const av = Autodesk.Viewing;
// viewer.addEventListener(av.GEOMETRY_LOADED_EVENT, onModelLoaded, { once: true });
viewer.addEventListener(AGGREGATE_GEOMETRY_LOADED_EVENT, onModelLoaded, { once: true });
const results = await promisesInSequence(modelURNs, (d) => loadModelAsync(d.urn, d.options));
viewer.fireEvent({
type: AGGREGATE_GEOMETRY_LOADED_EVENT,
models: results
});
}
useEffect(() => {
initializeViewer();
return function cleanUp() {
if (viewerRef.current) {
viewerRef.current.finish();
}
};
}, []);
return <div id="forgeViewer" ref={viewerDomRef}></div>;
}
Viewer.displayName = "Viewer";

Adding Array to Custom Google Apps Script for Google Sheets

I have a bit of code to use a ZIP code to find the county and I want to be able to use the function as an array formula so that I can add it to a sheet for form responses.
I tried to look up how to make the function work with an array formula but I don't know enough to apply it.
Is it possible to make this code work as an array formula?
Sample sheet:
https://docs.google.com/spreadsheets/d/1S-TVchlE1sFTIOrbOL-v_N3LJJHLAHLJHkUtVpFeZok/edit#gid=0
/*
*
* Google Maps Formulas for Google Sheets
*
* Written by Amit Agarwal
*
* Web: https://www.labnol.org/google-maps-sheets-200817
*
*/
const md5 = (key = "") => {
const code = key.toLowerCase().replace(/\s/g, "");
return Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, key)
.map((char) => (char + 256).toString(16).slice(-2))
.join("");
};
const getCache = (key) => {
return CacheService.getDocumentCache().get(md5(key));
};
const setCache = (key, value) => {
const expirationInSeconds = 6 * 60 * 60; // max is 6 hours
CacheService.getDocumentCache().put(md5(key), value, expirationInSeconds);
};
/**
* Get the county name of an address on Google Maps.
*
* =GOOGLEMAPS_COUNTY("10 Hanover Square, NY")
*
* #param {String} address The address to lookup.
* #return {String} The county of the address.
* #customFunction
*/
const GOOGLEMAPS_COUNTY = (address) => {
if (!address) {
throw new Error("No address specified!");
}
if (address.map) {
return address.map("administrative_area_level_2");
}
const key = ["administrative_area_level_2", address].join(",");
const value = getCache(key);
if (value !== null) return value;
const { results: [data = null] = [] } = Maps.newGeocoder().geocode(address);
if (data === null) {
throw new Error("Address not found!");
}
const [{ long_name } = {}] = data.address_components.filter(
({ types: [level] }) => {
return level === "administrative_area_level_2";
}
);
if (!long_name) {
throw new Error("County not found!");
}
const answer = `${long_name}`;
setCache(key, answer);
return answer;
};
In your situation, the following sample script is your expected goal?
Sample script:
Please add the following script to your current script and save it.
const GOOGLEMAPS_COUNTY_ARRAY = values => values.map(r => r.map(c => c.toString() ? GOOGLEMAPS_COUNTY(c) : null));
When you use this script, please put a custom function of =GOOGLEMAPS_COUNTY_ARRAY(A2:A) to a cell.
Testing:
When your sample Spreadsheet is used, the following result is obtained.
Reference:
map()

Is [] parameter in the AngularJS module definition compulsory?

In definition of AngularJS module, [] is a parameter for other depended module on this module.
<script>
var app = angular.module('myApp',[]);
app.controller("myCtrl", function($scope) {
$scope.firstName = "John";
$scope.lastName = "Doe";
});
</script>
My Question is,
Is this parameter [] necessary, because the the following link or example they didn't mentioned [] parameter, but in above example(w3schooles), if we remove '[]' parameter then code will not give correct output see it?
Please see the this link openstack, they are not using [] parameter
var module = angular.module('hz.dashboard.launch-instance');
/**
* #ngdoc service
* #name launchInstanceModel
*
* #description
* This is the M part in MVC design pattern for launch instance
* wizard workflow. It is responsible for providing data to the
* view of each step in launch instance workflow and collecting
* user's input from view for creation of new instance. It is
* also the center point of communication between launch instance
* UI and services API.
*/
module.factory('launchInstanceModel', ['$q',
'cinderAPI',
'glanceAPI',
'keystoneAPI',
'neutronAPI',
'novaAPI',
'novaExtensions',
'securityGroup',
'serviceCatalog',
function ($q,
cinderAPI,
glanceAPI,
keystoneAPI,
neutronAPI,
novaAPI,
novaExtensions,
securityGroup,
serviceCatalog) {
var initPromise,
allNamespacesPromise;
// Constants (const in ES6)
var NON_BOOTABLE_IMAGE_TYPES = ['aki', 'ari'],
SOURCE_TYPE_IMAGE = 'image',
SOURCE_TYPE_SNAPSHOT = 'snapshot',
SOURCE_TYPE_VOLUME = 'volume',
SOURCE_TYPE_VOLUME_SNAPSHOT = 'volume_snapshot';
/**
* #ngdoc model api object
*/
var model = {
initializing: false,
initialized: false,
/**
* #name newInstanceSpec
*
* #description
* A dictionary like object containing specification collected from user's
* input. Its required properties include:
*
* #property {String} name: The new server name.
* #property {String} source_type: The type of source
* Valid options: (image | snapshot | volume | volume_snapshot)
* #property {String} source_id: The ID of the image / volume to use.
* #property {String} flavor_id: The ID of the flavor to use.
*
* Other parameters are accepted as per the underlying novaclient:
* - https://github.com/openstack/python-novaclient/blob/master/novaclient/v2/servers.py#L417
* But may be required additional values as per nova:
* - https://github.com/openstack/horizon/blob/master/openstack_dashboard/api/rest/nova.py#L127
*
* The JS code only needs to set the values below as they are made.
* The createInstance function will map them appropriately.
*/
// see initializeNewInstanceSpec
newInstanceSpec: {},
/**
* cloud service properties, they should be READ-ONLY to all UI controllers
*/
availabilityZones: [],
flavors: [],
allowedBootSources: [],
images: [],
allowCreateVolumeFromImage: false,
arePortProfilesSupported: false,
imageSnapshots: [],
keypairs: [],
metadataDefs: {
flavor: null,
image: null,
volume: null
},
networks: [],
neutronEnabled: false,
novaLimits: {},
profiles: [],
securityGroups: [],
volumeBootable: false,
volumes: [],
volumeSnapshots: [],
/**
* api methods for UI controllers
*/
initialize: initialize,
createInstance: createInstance
};
// Local function.
function initializeNewInstanceSpec(){
model.newInstanceSpec = {
availability_zone: null,
admin_pass: null,
config_drive: false,
user_data: '', // REQUIRED Server Key. Null allowed.
disk_config: 'AUTO',
flavor: null, // REQUIRED
instance_count: 1,
key_pair: [], // REQUIRED Server Key
name: null, // REQUIRED
networks: [],
profile: {},
security_groups: [], // REQUIRED Server Key. May be empty.
source_type: null, // REQUIRED for JS logic (image | snapshot | volume | volume_snapshot)
source: [],
vol_create: false, // REQUIRED for JS logic
vol_device_name: 'vda', // May be null
vol_delete_on_terminate: false,
vol_size: 1
};
}
/**
* #ngdoc method
* #name launchInstanceModel.initialize
* #returns {promise}
*
* #description
* Send request to get all data to initialize the model.
*/
function initialize(deep) {
var deferred, promise;
// Each time opening launch instance wizard, we need to do this, or
// we can call the whole methods `reset` instead of `initialize`.
initializeNewInstanceSpec();
if (model.initializing) {
promise = initPromise;
} else if (model.initialized && !deep) {
deferred = $q.defer();
promise = deferred.promise;
deferred.resolve();
} else {
model.initializing = true;
model.allowedBootSources.length = 0;
promise = $q.all([
getImages(),
novaAPI.getAvailabilityZones().then(onGetAvailabilityZones, noop),
novaAPI.getFlavors(true, true).then(onGetFlavors, noop),
novaAPI.getKeypairs().then(onGetKeypairs, noop),
novaAPI.getLimits().then(onGetNovaLimits, noop),
securityGroup.query().then(onGetSecurityGroups, noop),
serviceCatalog.ifTypeEnabled('network').then(getNetworks, noop),
serviceCatalog.ifTypeEnabled('volume').then(getVolumes, noop)
]);
promise.then(
function() {
model.initializing = false;
model.initialized = true;
// This provides supplemental data non-critical to launching
// an instance. Therefore we load it only if the critical data
// all loads successfully.
getMetadataDefinitions();
},
function () {
model.initializing = false;
model.initialized = false;
}
);
}
return promise;
}
/**
* #ngdoc method
* #name launchInstanceModel.createInstance
* #returns {promise}
*
* #description
* Send request for creating server.
*/
function createInstance() {
var finalSpec = angular.copy(model.newInstanceSpec);
cleanNullProperties();
setFinalSpecBootsource(finalSpec);
setFinalSpecFlavor(finalSpec);
setFinalSpecNetworks(finalSpec);
setFinalSpecKeyPairs(finalSpec);
setFinalSpecSecurityGroups(finalSpec);
return novaAPI.createServer(finalSpec);
}
function cleanNullProperties(finalSpec){
// Initially clean fields that don't have any value.
for (var key in finalSpec) {
if (finalSpec.hasOwnProperty(key) && finalSpec[key] === null) {
delete finalSpec[key];
}
}
}
//
// Local
//
function onGetAvailabilityZones(data) {
model.availabilityZones.length = 0;
push.apply(model.availabilityZones, data.data.items
.filter(function (zone) {
return zone.zoneState && zone.zoneState.available;
})
.map(function (zone) {
return zone.zoneName;
})
);
if(model.availabilityZones.length > 0) {
model.newInstanceSpec.availability_zone = model.availabilityZones[0];
}
}
// Flavors
function onGetFlavors(data) {
model.flavors.length = 0;
push.apply(model.flavors, data.data.items);
}
function setFinalSpecFlavor(finalSpec) {
if ( finalSpec.flavor ) {
finalSpec.flavor_id = finalSpec.flavor.id;
} else {
delete finalSpec.flavor_id;
}
delete finalSpec.flavor;
}
// Keypairs
function onGetKeypairs(data) {
angular.extend(
model.keypairs,
data.data.items.map(function (e) {
e.keypair.id = e.keypair.name;
return e.keypair;
}));
}
function setFinalSpecKeyPairs(finalSpec) {
// Nova only wants the key name. It is a required field, even if None.
if(!finalSpec.key_name && finalSpec.key_pair.length === 1){
finalSpec.key_name = finalSpec.key_pair[0].name;
} else if (!finalSpec.key_name) {
finalSpec.key_name = null;
}
delete finalSpec.key_pair;
}
// Security Groups
function onGetSecurityGroups(data) {
model.securityGroups.length = 0;
push.apply(model.securityGroups, data.data.items);
// set initial default
if (model.newInstanceSpec.security_groups.length === 0 &&
model.securityGroups.length > 0) {
model.securityGroups.forEach(function (securityGroup) {
if (securityGroup.name === 'default') {
model.newInstanceSpec.security_groups.push(securityGroup);
}
});
}
}
function setFinalSpecSecurityGroups(finalSpec) {
// pull out the ids from the security groups objects
var security_group_ids = [];
finalSpec.security_groups.forEach(function(securityGroup){
if(model.neutronEnabled) {
security_group_ids.push(securityGroup.id);
} else {
security_group_ids.push(securityGroup.name);
}
});
finalSpec.security_groups = security_group_ids;
}
// Networks
function getNetworks() {
return neutronAPI.getNetworks().then(onGetNetworks, noop);
}
function onGetNetworks(data) {
model.neutronEnabled = true;
model.networks.length = 0;
push.apply(model.networks, data.data.items);
}
function setFinalSpecNetworks(finalSpec) {
finalSpec.nics = [];
finalSpec.networks.forEach(function (network) {
finalSpec.nics.push(
{
"net-id": network.id,
"v4-fixed-ip": ""
});
});
delete finalSpec.networks;
}
// Boot Source
function getImages(){
return glanceAPI.getImages({status:'active'}).then(onGetImages);
}
function isBootableImageType(image){
// This is a blacklist of images that can not be booted.
// If the image container type is in the blacklist
// The evaluation will result in a 0 or greater index.
return NON_BOOTABLE_IMAGE_TYPES.indexOf(image.container_format) < 0;
}
function onGetImages(data) {
model.images.length = 0;
push.apply(model.images, data.data.items.filter(function (image) {
return isBootableImageType(image) &&
(!image.properties || image.properties.image_type !== 'snapshot');
}));
addAllowedBootSource(model.images, SOURCE_TYPE_IMAGE, gettext('Image'));
model.imageSnapshots.length = 0;
push.apply(model.imageSnapshots,data.data.items.filter(function (image) {
return isBootableImageType(image) &&
(image.properties && image.properties.image_type === 'snapshot');
}));
addAllowedBootSource(model.imageSnapshots, SOURCE_TYPE_SNAPSHOT, gettext('Instance Snapshot'));
}
function getVolumes(){
var volumePromises = [];
// Need to check if Volume service is enabled before getting volumes
model.volumeBootable = true;
addAllowedBootSource(model.volumes, SOURCE_TYPE_VOLUME, gettext('Volume'));
addAllowedBootSource(model.volumeSnapshots, SOURCE_TYPE_VOLUME_SNAPSHOT, gettext('Volume Snapshot'));
volumePromises.push(cinderAPI.getVolumes({ status: 'available', bootable: 1 }).then(onGetVolumes));
volumePromises.push(cinderAPI.getVolumeSnapshots({ status: 'available' }).then(onGetVolumeSnapshots));
// Can only boot image to volume if the Nova extension is enabled.
novaExtensions.ifNameEnabled('BlockDeviceMappingV2Boot')
.then(function(){ model.allowCreateVolumeFromImage = true; });
return $q.all(volumePromises);
}
function onGetVolumes(data) {
model.volumes.length = 0;
push.apply(model.volumes, data.data.items);
}
function onGetVolumeSnapshots(data) {
model.volumeSnapshots.length = 0;
push.apply(model.volumeSnapshots, data.data.items);
}
function addAllowedBootSource(rawTypes, type, label) {
if (rawTypes && rawTypes.length > 0) {
model.allowedBootSources.push({
type: type,
label: label
});
}
}
function setFinalSpecBootsource(finalSpec) {
finalSpec.source_id = finalSpec.source && finalSpec.source[0] && finalSpec.source[0].id;
delete finalSpec.source;
switch (finalSpec.source_type.type) {
case SOURCE_TYPE_IMAGE:
setFinalSpecBootImageToVolume(finalSpec);
break;
case SOURCE_TYPE_SNAPSHOT:
break;
case SOURCE_TYPE_VOLUME:
setFinalSpecBootFromVolumeDevice(finalSpec, 'vol');
break;
case SOURCE_TYPE_VOLUME_SNAPSHOT:
setFinalSpecBootFromVolumeDevice(finalSpec, 'snap');
break;
default:
// error condition
console.log("Unknown source type: " + finalSpec.source_type);
}
// The following are all fields gathered into simple fields by
// steps so that the view can simply bind to simple model attributes
// that are then transformed a single time to Nova's expectation
// at launch time.
delete finalSpec.source_type;
delete finalSpec.vol_create;
delete finalSpec.vol_device_name;
delete finalSpec.vol_delete_on_terminate;
delete finalSpec.vol_size;
}
function setFinalSpecBootImageToVolume(finalSpec){
if(finalSpec.vol_create) {
// Specify null to get Autoselection (not empty string)
var device_name = finalSpec.vol_device_name ? finalSpec.vol_device_name : null;
finalSpec.block_device_mapping_v2 = [];
finalSpec.block_device_mapping_v2.push(
{
'device_name': device_name,
'source_type': SOURCE_TYPE_IMAGE,
'destination_type': SOURCE_TYPE_VOLUME,
'delete_on_termination': finalSpec.vol_delete_on_terminate ? 1 : 0,
'uuid': finalSpec.source_id,
'boot_index': '0',
'volume_size': finalSpec.vol_size
}
);
}
}
function setFinalSpecBootFromVolumeDevice(finalSpec, sourceType) {
finalSpec.block_device_mapping = {};
finalSpec.block_device_mapping[finalSpec.vol_device_name] = [
finalSpec.source_id,
':',
sourceType,
'::',
(finalSpec.vol_delete_on_terminate ? 1 : 0)
].join('');
// Source ID must be empty for API
finalSpec.source_id = '';
}
// Nova Limits
function onGetNovaLimits(data) {
angular.extend(model.novaLimits, data.data);
}
// Metadata Definitions
/**
* Metadata definitions provide supplemental information in detail
* rows and should not slow down any of the other load processes.
* All code should be written to treat metadata definitions as
* optional, because they are never guaranteed to exist.
*/
function getMetadataDefinitions() {
// Metadata definitions often apply to multiple
// resource types. It is optimal to make a single
// request for all desired resource types.
var resourceTypes = {
flavor: 'OS::Nova::Flavor',
image: 'OS::Glance::Image',
volume: 'OS::Cinder::Volumes'
};
angular.forEach(resourceTypes, function (resourceType, key) {
glanceAPI.getNamespaces({
'resource_type': resourceType
}, true)
.then(function (data) {
var namespaces = data.data.items;
// This will ensure that the metaDefs model object remains
// unchanged until metadefs are fully loaded. Otherwise,
// partial results are loaded and can result in some odd
// display behavior.
if(namespaces.length) {
model.metadataDefs[key] = namespaces;
}
});
});
}
return model;
}
]);
})();
Status
The array or [] parameter is needed to specify dependent modules when you declare your own module, so this should only happen once per module.
The second notation, without the parameter is just retrieving the module so you can attach controllers/services/filters/... to it.
Use the array notation for the declaration of your module, use the single parameter notation if you want to add something to it.
For example:
in app.module.js
//You want to make use of the ngRoute module,
//so you have to specify a dependency on it
angular.module('app', ['ngRoute']);
You will only specify the dependencies on your module once, when you declare it.
in main.controller.js
//You want to add a controller to your module, so you want to retrieve your module
angular.module('app').controller('mainCtrl', mainCtrl);
function mainCtrl() { };
Now angular will try to find a module by that name instead of creating one, when it doesn't find one, you'll get some errors, which explains your original question.
You will typically do this every time you want to add something to your module.
Note that you could also achieve this by storing your module in a global variable when you create it and then access the module by that variable when you want to add things to it, however as you probably know, creating global variables is a bad practice.
Facing an error with angular is a bliss because it provides the link to description of the error in the console.
From an example page like that...
When defining a module with no module dependencies, the array of dependencies should be defined and empty.
var myApp = angular.module('myApp', []);
To retrieve a reference to the same module for further configuration, call angular.module without the array argument.
var myApp = angular.module('myApp');

Node.js Promises: Push to array asynchronously & save

I am currently trying to push to an array (attribute within a Mongo Model), from a list of items I receive through a request. From those items, I loop through them to see which one is currently in the db and if that is not the case, then I create a new Item and try to save it. I am using promises to accomplish this task but I am unable to figure out why the array is empty after all the promises have fulfilled.
var q = require('q');
var items_to_get = ['1', '2', '3']; // example array
var trans = new Transaction({
items : []
});
var promises = [];
for (var i = 0; i < items_to_get.length; i++) {
var ith = i; //save current i, kinda hacky
var deferred = q.defer(); //init promise
//find an existing item
Item.findOne({simcode: items_to_get[ith]}, function(err, item) {
trans.items.push(item); // push item to transaction
deferred.resolve(item); // resolve the promise
});
promises.push(deferred); // add promise to array, can be rejected or fulfilled
};
q.allSettled(promises).then(function(result) {
console.log(trans.items); //is empty
trans.save();
}
EDIT Resolved: Code bellow, based on http://jsbin.com/bufecilame/1/edit?html,js,output .. credits go to #macqm
var items_to_get = ['1', '2', '3'];
var promises = []; //I made this global
items_to_get.forEach(item) {
upsertItem(item);
}
q.allSettled(promises).then(function(result) {
//loop through array of promises, add items
result.forEach(function(res) {
if (res.state === "fulfilled") {
trans.items.push(res.value);
}
});
trans.save();
promises = []; //empty array, since it's global.
}
//moved main code inside here
function upsertItem(item) {
var deferred = q.defer(); //init promise
//find an existing item
Item.findOne({simcode: item}, function(err, item) {
deferred.resolve(item); // resolve the promise
// don't forget to handle error cases
// use deffered.reject(item) for those
});
promises.push(deferred); // add promise to array
}
This is how I did it without any 3rd party libraries.
Since I just needed to defer and I'm on ES2017 I figured it was best not to over complicate things with unnecessary dependencies.
'use strict';
/**
* #param {function(*)} callee
* #param {Array} args
* #returns {Promise.<*>}
*/
const defer = (callee, args) => {
return new Promise(resolve => {
resolve(callee(...args));
});
};
/**
* #param {Number} one
* #param {Number} two
* #param {Number} timeout
* #returns {Promise.<Number>}
*/
const asyncFunction = (one, two, timeout) => {
return new Promise(resolve => {
setTimeout(resolve, timeout, one + two);
});
};
let promises = [];
promises.push(defer(asyncFunction, [3, 7, 0])); // returns immediately
promises.push(defer(asyncFunction, [10, 20, 100])); // returns after 100ms
promises.push(defer(asyncFunction, [55, 45, 50])); // returns after 50ms
Promise.all(promises).then(results => {
console.log(results);
});
Run the above code and you'll get [ 10, 30, 100 ].
Instead of
promises.push(deferred);
... try ...
promises.push(deferred.promise);
Also, since your promises are resolving to the item that was saved anyway, you can use the result of q.allSettled(...) as your items:
q.allSettled(promises).then(function(results) {
trans.items = results;
trans.save();
});

Resources