AC-1065 in anticipation of potentially having to sort the keys of any incoming json variable objects, consolidated the variable handling code into a single helper. Anytime variable data is fetched from the API it gets passed to the helper and turned into a YAML doc. The helper checks the incoming data type for string or object. If string it first attempts to parse as YAML. If it fails, then tries JSON. If no joy, fails gracefully, logging the error and notifying the user. Anytime variables need to be sent to the API, a method in the helper is called to parse the variable text into a JSON object.

This commit is contained in:
Chris Houseknecht
2014-03-05 18:13:49 -05:00
parent 1ba917afaf
commit 93bd1d859a
8 changed files with 284 additions and 420 deletions

View File

@@ -85,7 +85,8 @@ angular.module('ansible', [
'StreamListDefinition', 'StreamListDefinition',
'HomeGroupListDefinition', 'HomeGroupListDefinition',
'HomeHostListDefinition', 'HomeHostListDefinition',
'ActivityDetailDefinition' 'ActivityDetailDefinition',
'VariablesHelper'
]) ])
.config(['$routeProvider', .config(['$routeProvider',
function ($routeProvider) { function ($routeProvider) {

View File

@@ -108,7 +108,7 @@ JobTemplatesList.$inject = ['$scope', '$rootScope', '$location', '$log', '$route
function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm, function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GetBasePath, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GetBasePath,
InventoryList, CredentialList, ProjectList, LookUpInit, md5Setup, ParseTypeChange, Wait, Empty) { InventoryList, CredentialList, ProjectList, LookUpInit, md5Setup, ParseTypeChange, Wait, Empty, ToJSON) {
ClearScope(); ClearScope();
@@ -286,20 +286,8 @@ function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routePa
$scope.formSave = function () { $scope.formSave = function () {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start'); Wait('start');
var data = {}, json_data, fld; var data = {}, fld;
try { try {
// Make sure we have valid variable data
if ($scope.parseType === 'json') {
json_data = JSON.parse($scope.variables); //make sure JSON parses
} else {
json_data = jsyaml.load($scope.variables); //parse yaml
}
// Make sure our JSON is actually an object
if (typeof json_data !== 'object') {
throw "failed to return an object!";
}
for (fld in form.fields) { for (fld in form.fields) {
if (form.fields[fld].type === 'select' && fld !== 'playbook') { if (form.fields[fld].type === 'select' && fld !== 'playbook') {
data[fld] = $scope[fld].value; data[fld] = $scope[fld].value;
@@ -309,11 +297,7 @@ function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routePa
} }
} }
} }
data.extra_vars = ToJSON($scope.parseType, $scope.variables, true);
data.extra_vars = JSON.stringify(json_data, undefined, '\t');
if (Empty(data.extra_vars)) {
data.extra_vars = "";
}
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
Rest.post(data) Rest.post(data)
@@ -346,14 +330,14 @@ function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routePa
JobTemplatesAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobTemplateForm', JobTemplatesAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobTemplateForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope',
'GetBasePath', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'GetBasePath', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit',
'md5Setup', 'ParseTypeChange', 'Wait', 'Empty' 'md5Setup', 'ParseTypeChange', 'Wait', 'Empty', 'ToJSON'
]; ];
function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm, GenerateForm, Rest, function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm, GenerateForm, Rest,
Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList,
CredentialList, ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, ParseTypeChange, JobStatusToolTip, FormatDate, CredentialList, ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, ParseTypeChange, JobStatusToolTip, FormatDate,
Wait, Stream, Empty, Prompt) { Wait, Stream, Empty, Prompt, ParseVariableString, ToJSON) {
ClearScope(); ClearScope();
@@ -569,7 +553,7 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP
Rest.setUrl(defaultUrl + ':id/'); Rest.setUrl(defaultUrl + ':id/');
Rest.get({ params: { id: id } }) Rest.get({ params: { id: id } })
.success(function (data) { .success(function (data) {
var fld, i, json_obj, related, set; var fld, i, related, set;
LoadBreadCrumbs({ path: '/job_templates/' + id, title: data.name }); LoadBreadCrumbs({ path: '/job_templates/' + id, title: data.name });
for (fld in form.fields) { for (fld in form.fields) {
if (fld !== 'variables' && data[fld] !== null && data[fld] !== undefined) { if (fld !== 'variables' && data[fld] !== null && data[fld] !== undefined) {
@@ -590,27 +574,7 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP
} }
if (fld === 'variables') { if (fld === 'variables') {
// Parse extra_vars, converting to YAML. // Parse extra_vars, converting to YAML.
if ($.isEmptyObject(data.extra_vars) || data.extra_vars === "{}" || data.extra_vars === "null" || $scope.variables = ParseVariableString(data.extra_vars);
data.extra_vars === "" || data.extra_vars === null) {
$scope.variables = "---";
} else {
$scope.variables = '---';
try {
json_obj = JSON.parse(data.extra_vars);
$scope.variables = jsyaml.safeDump(json_obj);
}
catch (e) {
$log.info('Attempt to parse extra_vars as JSON faild. Attempting to parse as YAML');
try {
json_obj = jsyaml.safeLoad(data.extra_vars);
$scope.variables = jsyaml.safeDump(json_obj);
}
catch(e2) {
ProcessErrors($scope, data.extra_vars, e2.message, null, { hdr: 'Error!',
msg: 'Attempts to parse variables as JSON and YAML failed. Last attempt returned: ' + e2.message });
}
}
}
master.variables = $scope.variables; master.variables = $scope.variables;
} }
if (form.fields[fld].type === 'lookup' && data.summary_fields[form.fields[fld].sourceModel]) { if (form.fields[fld].type === 'lookup' && data.summary_fields[form.fields[fld].sourceModel]) {
@@ -702,20 +666,10 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP
$scope.formSave = function () { $scope.formSave = function () {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start'); Wait('start');
var data = {}, json_data, fld; var data = {}, fld;
try { try {
// Make sure we have valid variable data // Make sure we have valid variable data
if ($scope.parseType === 'json') { data.extra_vars = ToJSON($scope.parseType, $scope.variables, true);
json_data = JSON.parse($scope.variables); //make sure JSON parses
} else {
json_data = jsyaml.load($scope.variables); //parse yaml
}
// Make sure our JSON is actually an object
if (typeof json_data !== 'object') {
throw "failed to return an object!";
}
for (fld in form.fields) { for (fld in form.fields) {
if (form.fields[fld].type === 'select' && fld !== 'playbook') { if (form.fields[fld].type === 'select' && fld !== 'playbook') {
data[fld] = $scope[fld].value; data[fld] = $scope[fld].value;
@@ -725,12 +679,6 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP
} }
} }
} }
data.extra_vars = JSON.stringify(json_data, undefined, '\t');
if (data.extra_vars === "null" || data.extra_vars === null) {
data.extra_vars = "";
}
Rest.setUrl(defaultUrl + id + '/'); Rest.setUrl(defaultUrl + id + '/');
Rest.put(data) Rest.put(data)
.success(function (data) { .success(function (data) {
@@ -810,5 +758,6 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP
JobTemplatesEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobTemplateForm', JobTemplatesEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobTemplateForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit',
'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'PromptPasswords', 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'PromptPasswords',
'GetBasePath', 'md5Setup', 'ParseTypeChange', 'JobStatusToolTip', 'FormatDate', 'Wait', 'Stream', 'Empty', 'Prompt' 'GetBasePath', 'md5Setup', 'ParseTypeChange', 'JobStatusToolTip', 'FormatDate', 'Wait', 'Stream', 'Empty', 'Prompt',
'ParseVariableString', 'ToJSON'
]; ];

View File

@@ -8,8 +8,6 @@
* *
*/ */
/* global jsyaml:false */
'use strict'; 'use strict';
function JobsListCtrl($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobList, GenerateList, LoadBreadCrumbs, Prompt, function JobsListCtrl($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobList, GenerateList, LoadBreadCrumbs, Prompt,
@@ -189,7 +187,8 @@ JobsListCtrl.$inject = ['$scope', '$rootScope', '$location', '$log', '$routePara
function JobsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobForm, JobTemplateForm, GenerateForm, Rest, function JobsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobForm, JobTemplateForm, GenerateForm, Rest,
Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList,
CredentialList, ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, FormatDate, JobStatusToolTip, Wait, Empty) { CredentialList, ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, FormatDate, JobStatusToolTip, Wait, Empty,
ParseVariableString) {
ClearScope(); ClearScope();
@@ -374,28 +373,7 @@ function JobsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, J
} }
} }
if (fld === 'variables') { if (fld === 'variables') {
// Parse extra_vars, converting to YAML. $scope.variables = ParseVariableString(data.extra_vars);
if ($.isEmptyObject(data.extra_vars) || data.extra_vars === "{}" || data.extra_vars === "null" ||
data.extra_vars === "" || data.extra_vars === null) {
$scope.variables = "---";
} else {
$scope.variables = '---';
try {
json_obj = JSON.parse(data.extra_vars);
$scope.variables = jsyaml.safeDump(json_obj);
}
catch (e) {
$log.info('Attempt to parse extra_vars as JSON faild. Attempting to parse as YAML');
try {
json_obj = jsyaml.safeLoad(data.extra_vars);
$scope.variables = jsyaml.safeDump(json_obj);
}
catch(e2) {
ProcessErrors($scope, data.extra_vars, e2.message, null, { hdr: 'Error!',
msg: 'Attempts to parse variables as JSON and YAML failed. Last attempt returned: ' + e2.message });
}
}
}
} }
if (JobTemplateForm.fields[fld].type === 'lookup' && data.summary_fields[JobTemplateForm.fields[fld].sourceModel]) { if (JobTemplateForm.fields[fld].type === 'lookup' && data.summary_fields[JobTemplateForm.fields[fld].sourceModel]) {
$scope[JobTemplateForm.fields[fld].sourceModel + '_' + JobTemplateForm.fields[fld].sourceField] = $scope[JobTemplateForm.fields[fld].sourceModel + '_' + JobTemplateForm.fields[fld].sourceField] =
@@ -446,5 +424,5 @@ function JobsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, J
JobsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobForm', 'JobTemplateForm', JobsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobForm', 'JobTemplateForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit',
'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'PromptPasswords', 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'PromptPasswords',
'GetBasePath', 'md5Setup', 'FormatDate', 'JobStatusToolTip', 'Wait', 'Empty' 'GetBasePath', 'md5Setup', 'FormatDate', 'JobStatusToolTip', 'Wait', 'Empty', 'ParseVariableString'
]; ];

View File

@@ -12,8 +12,7 @@
angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'GroupListDefinition', 'SearchHelper', angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'GroupListDefinition', 'SearchHelper',
'PaginationHelpers', 'ListGenerator', 'AuthService', 'GroupsHelper', 'InventoryHelper', 'SelectionHelper', 'PaginationHelpers', 'ListGenerator', 'AuthService', 'GroupsHelper', 'InventoryHelper', 'SelectionHelper',
'JobSubmissionHelper', 'RefreshHelper', 'PromptDialog', 'CredentialsListDefinition', 'InventoryTree', 'JobSubmissionHelper', 'RefreshHelper', 'PromptDialog', 'CredentialsListDefinition', 'InventoryTree',
'InventoryStatusDefinition' 'InventoryStatusDefinition', 'VariablesHelper'])
])
.factory('GetSourceTypeOptions', ['Rest', 'ProcessErrors', 'GetBasePath', .factory('GetSourceTypeOptions', ['Rest', 'ProcessErrors', 'GetBasePath',
function (Rest, ProcessErrors, GetBasePath) { function (Rest, ProcessErrors, GetBasePath) {
@@ -322,10 +321,10 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
.factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', .factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'Wait', 'GetChoices', 'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'Wait', 'GetChoices',
'GetSourceTypeOptions', 'LookUpInit', 'BuildTree', 'SourceChange', 'WatchInventoryWindowResize', 'GetSourceTypeOptions', 'LookUpInit', 'BuildTree', 'SourceChange', 'WatchInventoryWindowResize', 'ToJSON',
function ($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, function ($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, ParseTypeChange, Wait, GetChoices, GetSourceTypeOptions, LookUpInit, BuildTree, GetBasePath, ParseTypeChange, Wait, GetChoices, GetSourceTypeOptions, LookUpInit, BuildTree,
SourceChange, WatchInventoryWindowResize) { SourceChange, WatchInventoryWindowResize, ToJSON) {
return function (params) { return function (params) {
var inventory_id = params.inventory_id, var inventory_id = params.inventory_id,
@@ -403,8 +402,7 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
var parseError = false, var parseError = false,
data = {}, data = {},
regions, r, i, regions, r, i;
json_data;
// Update the selector tree with new group name, descr // Update the selector tree with new group name, descr
//SetNodeName({ scope: scope['selectedNode'], group_id: group_id, //SetNodeName({ scope: scope['selectedNode'], group_id: group_id,
@@ -428,30 +426,7 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
data.source_regions = r.join(); data.source_regions = r.join();
if (scope.source.value === 'ec2') { if (scope.source.value === 'ec2') {
// for ec2, validate variable data data.source_vars = ToJSON(scope.envParseType, scope.source_vars, true);
try {
if (scope.envParseType === 'json') {
json_data = JSON.parse(scope.source_vars); //make sure JSON parses
} else {
json_data = jsyaml.load(scope.source_vars); //parse yaml
}
// Make sure our JSON is actually an object
if (typeof json_data !== 'object') {
throw "failed to return an object!";
}
// Send JSON as a string
if ($.isEmptyObject(json_data)) {
data.source_vars = "";
} else {
data.source_vars = JSON.stringify(json_data, undefined, '\t');
}
} catch (err) {
parseError = true;
scope.$emit('SaveComplete', true);
Alert("Error", "Error parsing extra variables. Parser returned: " + err);
}
} }
if (!parseError) { if (!parseError) {
@@ -484,54 +459,28 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
// Save // Save
scope.formModalAction = function () { scope.formModalAction = function () {
var json_data, data; var data;
Wait('start'); Wait('start');
try { scope.formModalActionDisabled = true;
scope.formModalActionDisabled = true; data = {
name: scope.name,
// Make sure we have valid variable data description: scope.description
if (scope.parseType === 'json') { };
json_data = JSON.parse(scope.variables); //make sure JSON parses if (inventory_id) {
} else { data.inventory = inventory_id;
json_data = jsyaml.load(scope.variables); //parse yaml
}
// Make sure our JSON is actually an object
if (typeof json_data !== 'object') {
throw "failed to return an object!";
}
data = {
name: scope.name,
description: scope.description
};
if (inventory_id) {
data.inventory = inventory_id;
}
if ($.isEmptyObject(json_data)) {
data.variables = "";
} else {
data.variables = JSON.stringify(json_data, undefined, '\t');
}
Rest.setUrl(defaultUrl);
Rest.post(data)
.success(function (data) {
scope.$emit('formSaveSuccess', data.id, GetBasePath('inventory_sources') + data.id + '/');
})
.error(function (data, status) {
Wait('stop');
scope.formModalActionDisabled = false;
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to add new group. POST returned status: ' + status });
});
} catch (err) {
Wait('stop');
scope.formModalActionDisabled = false;
Alert("Error", "Error parsing group variables. Parser returned: " + err);
} }
data.variables = ToJSON(scope.parseType, scope.variables, true);
Rest.setUrl(defaultUrl);
Rest.post(data)
.success(function (data) {
scope.$emit('formSaveSuccess', data.id, GetBasePath('inventory_sources') + data.id + '/');
})
.error(function (data, status) {
Wait('stop');
scope.formModalActionDisabled = false;
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to add new group. POST returned status: ' + status });
});
}; };
scope.sourceChange = function () { scope.sourceChange = function () {
@@ -584,9 +533,12 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
.factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', .factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate', 'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate',
'LookUpInit', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find','WatchInventoryWindowResize', 'LookUpInit', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find','WatchInventoryWindowResize',
'ParseVariableString', 'ToJSON',
function ($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, function ($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, LookUpInit, Empty, Wait, GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, LookUpInit, Empty, Wait,
GetChoices, UpdateGroup, SourceChange, Find, WatchInventoryWindowResize) { GetChoices, UpdateGroup, SourceChange, Find, WatchInventoryWindowResize,
ParseVariableString, ToJSON) {
return function (params) { return function (params) {
var parent_scope = params.scope, var parent_scope = params.scope,
@@ -647,11 +599,7 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
Rest.setUrl(scope.variable_url); Rest.setUrl(scope.variable_url);
Rest.get() Rest.get()
.success(function (data) { .success(function (data) {
if ($.isEmptyObject(data)) { scope.variables = ParseVariableString(data);
scope.variables = "---";
} else {
scope.variables = jsyaml.safeDump(data);
}
scope.$emit('groupVariablesLoaded'); scope.$emit('groupVariablesLoaded');
}) })
.error(function (data, status) { .error(function (data, status) {
@@ -670,7 +618,7 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
Rest.setUrl(scope.source_url); Rest.setUrl(scope.source_url);
Rest.get() Rest.get()
.success(function (data) { .success(function (data) {
var fld, i, j, flag, found, json_obj, set, opts, list; var fld, i, j, flag, found, set, opts, list;
for (fld in form.fields) { for (fld in form.fields) {
if (fld === 'checkbox_group') { if (fld === 'checkbox_group') {
for (i = 0; i < form.fields[fld].fields.length; i++) { for (i = 0; i < form.fields[fld].fields.length; i++) {
@@ -706,13 +654,7 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
} }
} else if (fld === 'source_vars') { } else if (fld === 'source_vars') {
// Parse source_vars, converting to YAML. // Parse source_vars, converting to YAML.
if ($.isEmptyObject(data.source_vars) || data.source_vars === "{}" || scope.source_vars = ParseVariableString(data.source_vars);
data.source_vars === "null" || data.source_vars === "") {
scope.source_vars = "---";
} else {
json_obj = JSON.parse(data.source_vars);
scope.source_vars = jsyaml.safeDump(json_obj);
}
master.source_vars = scope.variables; master.source_vars = scope.variables;
} else if (data[fld]) { } else if (data[fld]) {
scope[fld] = data[fld]; scope[fld] = data[fld];
@@ -882,7 +824,7 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
// related fields, then call SaveComplete to wrap things up. // related fields, then call SaveComplete to wrap things up.
var parseError = false, var parseError = false,
regions, r, i, json_data, regions, r, i,
data = { data = {
group: group_id, group: group_id,
source: ((scope.source && scope.source.value) ? scope.source.value : ''), source: ((scope.source && scope.source.value) ? scope.source.value : ''),
@@ -903,29 +845,7 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
if (scope.source && scope.source.value === 'ec2') { if (scope.source && scope.source.value === 'ec2') {
// for ec2, validate variable data // for ec2, validate variable data
try { data.source_vars = ToJSON(scope.envParseType, scope.source_vars, true);
if (scope.envParseType === 'json') {
json_data = JSON.parse(scope.source_vars); //make sure JSON parses
} else {
json_data = jsyaml.load(scope.source_vars); //parse yaml
}
// Make sure our JSON is actually an object
if (typeof json_data !== 'object') {
throw "failed to return an object!";
}
// Send JSON as a string
if ($.isEmptyObject(json_data)) {
data.source_vars = "";
} else {
data.source_vars = JSON.stringify(json_data);
}
} catch (err) {
parseError = true;
scope.$emit('SaveComplete', true);
Alert("Error", "Error parsing extra variables. Parser returned: " + err);
}
} }
if (!parseError) { if (!parseError) {
@@ -953,57 +873,42 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
// Save // Save
scope.formModalAction = function () { scope.formModalAction = function () {
Wait('start'); Wait('start');
try { var fld, data, json_data;
var fld, data, json_data;
// Make sure we have valid variable data json_data = ToJSON(scope.parseType, scope.variables);
if (scope.parseType === 'json') {
json_data = JSON.parse(scope.variables); //make sure JSON parses
} else {
json_data = jsyaml.load(scope.variables); //parse yaml
}
// Make sure our JSON is actually an object data = {};
if (typeof json_data !== 'object') { for (fld in form.fields) {
throw "failed to return an object!"; data[fld] = scope[fld];
}
data = {};
for (fld in form.fields) {
data[fld] = scope[fld];
}
data.inventory = inventory_id;
Rest.setUrl(defaultUrl);
Rest.put(data)
.success(function () {
if (scope.variables) {
//update group variables
Rest.setUrl(scope.variable_url);
Rest.put(json_data)
.success(function () {
scope.$emit('formSaveSuccess');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to update group variables. PUT status: ' + status });
});
} else {
scope.$emit('formSaveSuccess');
}
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, form, {
hdr: 'Error!',
msg: 'Failed to update group: ' + group_id + '. PUT status: ' + status
});
});
} catch (err) {
Wait('stop');
Alert("Error", "Error parsing group variables. Parser returned: " + err);
} }
data.inventory = inventory_id;
Rest.setUrl(defaultUrl);
Rest.put(data)
.success(function () {
if (scope.variables) {
//update group variables
Rest.setUrl(scope.variable_url);
Rest.put(json_data)
.success(function () {
scope.$emit('formSaveSuccess');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to update group variables. PUT status: ' + status });
});
} else {
scope.$emit('formSaveSuccess');
}
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, form, {
hdr: 'Error!',
msg: 'Failed to update group: ' + group_id + '. PUT status: ' + status
});
});
}; };
// Start the update process // Start the update process

View File

@@ -14,7 +14,7 @@
angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition', angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition',
'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'AuthService', 'HostsHelper', 'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'AuthService', 'HostsHelper',
'InventoryHelper', 'RelatedSearchHelper', 'InventoryFormDefinition', 'SelectionHelper', 'InventoryHelper', 'RelatedSearchHelper', 'InventoryFormDefinition', 'SelectionHelper',
'HostGroupsFormDefinition' 'HostGroupsFormDefinition', 'VariablesHelper'
]) ])
.factory('SetEnabledMsg', [ function() { .factory('SetEnabledMsg', [ function() {
@@ -320,8 +320,9 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostList, Gener
.factory('HostsCreate', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm', .factory('HostsCreate', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'WatchInventoryWindowResize', 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'WatchInventoryWindowResize',
'ToJSON',
function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors, function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, HostsReload, ParseTypeChange, Wait, WatchInventoryWindowResize) { GetBasePath, HostsReload, ParseTypeChange, Wait, WatchInventoryWindowResize, ToJSON) {
return function(params) { return function(params) {
var parent_scope = params.scope, var parent_scope = params.scope,
@@ -380,55 +381,27 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
Wait('start'); Wait('start');
try { var fld, data={};
var fld, json_data, data={}; scope.formModalActionDisabled = true;
scope.formModalActionDisabled = true; data.variables = ToJSON(scope.parseType, scope.variables, true);
for (fld in form.fields) {
// Make sure we have valid variable data if (fld !== 'variables') {
if (scope.parseType === 'json') { data[fld] = scope[fld];
json_data = JSON.parse(scope.variables); //make sure JSON parses
} }
else {
json_data = jsyaml.load(scope.variables); //parse yaml
}
// Make sure our JSON is actually an object
if (typeof json_data !== 'object') {
throw "failed to return an object!";
}
for (fld in form.fields) {
if (fld !== 'variables') {
data[fld] = scope[fld];
}
}
data.inventory = inventory_id;
if ($.isEmptyObject(json_data)) {
data.variables = "";
}
else {
data.variables = JSON.stringify(json_data, undefined, '\t');
}
Rest.setUrl(defaultUrl);
Rest.post(data)
.success( function() {
scope.$emit('HostSaveComplete');
})
.error( function(data, status) {
Wait('stop');
scope.formModalActionDisabled = false;
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new host. POST returned status: ' + status });
});
}
catch(err) {
Wait('stop');
scope.formModalActionDisabled = false;
Alert("Error", "Error parsing host variables. Parser returned: " + err);
} }
data.inventory = inventory_id;
Rest.setUrl(defaultUrl);
Rest.post(data)
.success( function() {
scope.$emit('HostSaveComplete');
})
.error( function(data, status) {
Wait('stop');
scope.formModalActionDisabled = false;
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new host. POST returned status: ' + status });
});
}; };
// Cancel // Cancel
@@ -447,9 +420,10 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
.factory('HostsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm', .factory('HostsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'Find', 'SetStatus', 'ApplyEllipsis', 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'Find', 'SetStatus', 'ApplyEllipsis',
'WatchInventoryWindowResize', 'WatchInventoryWindowResize', 'ToJSON', 'ParseVariableString',
function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors, function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, HostsReload, ParseTypeChange, Wait, Find, SetStatus, ApplyEllipsis, WatchInventoryWindowResize) { GetBasePath, HostsReload, ParseTypeChange, Wait, Find, SetStatus, ApplyEllipsis, WatchInventoryWindowResize, ToJSON,
ParseVariableString) {
return function(params) { return function(params) {
var parent_scope = params.scope, var parent_scope = params.scope,
@@ -486,12 +460,7 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
Rest.setUrl(scope.variable_url); Rest.setUrl(scope.variable_url);
Rest.get() Rest.get()
.success( function(data) { .success( function(data) {
if ($.isEmptyObject(data)) { scope.variables = ParseVariableString(data);
scope.variables = "---";
}
else {
scope.variables = jsyaml.safeDump(data);
}
scope.$emit('hostVariablesLoaded'); scope.$emit('hostVariablesLoaded');
}) })
.error( function(data, status) { .error( function(data, status) {
@@ -570,49 +539,23 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
scope.formModalAction = function() { scope.formModalAction = function() {
Wait('start'); Wait('start');
var fld, data={};
try { data.variables = ToJSON(scope.parseType, scope.variables, true);
// Make sure we have valid variable data for (fld in form.fields) {
var fld, json_data, data={}; data[fld] = scope[fld];
if (scope.parseType === 'json') {
json_data = JSON.parse(scope.variables); //make sure JSON parses
}
else {
json_data = jsyaml.load(scope.variables); //parse yaml
}
// Make sure our JSON is actually an object
if (typeof json_data !== 'object') {
throw "failed to return an object!";
}
for (fld in form.fields) {
data[fld] = scope[fld];
}
data.inventory = inventory_id;
if ($.isEmptyObject(json_data)) {
data.variables = "";
}
else {
data.variables = JSON.stringify(json_data, undefined, '\t');
}
Rest.setUrl(defaultUrl);
Rest.put(data)
.success( function() {
scope.$emit('saveCompleted');
})
.error( function(data, status) {
Wait('stop');
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update host: ' + host_id + '. PUT returned status: ' + status });
});
}
catch(err) {
Wait('stop');
Alert("Error", "Error parsing host variables. Parser returned: " + err);
} }
data.inventory = inventory_id;
Rest.setUrl(defaultUrl);
Rest.put(data)
.success( function() {
scope.$emit('saveCompleted');
})
.error( function(data, status) {
Wait('stop');
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update host: ' + host_id + '. PUT returned status: ' + status });
});
}; };
// Cancel // Cancel

View File

@@ -0,0 +1,109 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* VariablesHelper
*
* Show the CodeMirror variable editor and allow
* toggle between JSON and YAML
*
*/
'use strict';
angular.module('VariablesHelper', ['Utilities'])
/**
variables: string containing YAML or JSON | a JSON object.
If JSON string, convert to JSON object and run through jsyaml.safeDump() to create a YAML document. If YAML,
will attempt to load via jsyaml.safeLoad() and return a YAML document using jsyaml.safeDump(). In all cases
a YAML document is returned.
**/
.factory('ParseVariableString', ['$log', 'ProcessErrors', function ($log, ProcessErrors) {
return function (variables) {
var result = "---", json_obj;
if (typeof variables === 'string') {
if ($.isEmptyObject(variables) || variables === "{}" || variables === "null" ||
variables === "" || variables === null) {
// String is empty, return ---
} else {
try {
json_obj = JSON.parse(variables);
result = jsyaml.safeDump(json_obj);
}
catch (e) {
$log.info('Attempt to parse extra_vars as JSON faild. Attempting to parse as YAML');
try {
json_obj = jsyaml.safeLoad(variables);
result = jsyaml.safeDump(json_obj);
}
catch(e2) {
ProcessErrors(null, variables, e2.message, null, { hdr: 'Error!',
msg: 'Attempts to parse variables as JSON and YAML failed. Last attempt returned: ' + e2.message });
}
}
}
}
else {
// an object was passed in. just convert to yaml
try {
result = jsyaml.safeDump(variables);
}
catch(e3) {
ProcessErrors(null, variables, e3.message, null, { hdr: 'Error!',
msg: 'Attempt to convert JSON object to YAML document failed: ' + e3.message });
}
}
return result;
};
}])
/**
parseType: 'json' | 'yaml'
variables: string containing JSON or YAML
stringify: optional, boolean
Parse the given string according to the parseType to a JSON object. If stringify true,
stringify the object and return the string. Otherwise, return the JSON object.
**/
.factory('ToJSON', ['$log', 'ProcessErrors', function($log, ProcessErrors) {
return function(parseType, variables, stringify) {
var json_data, result;
if (parseType === 'json') {
try {
json_data = JSON.parse(variables); //make sure JSON parses
}
catch(e) {
json_data = {};
$log.error('Failed to parse JSON string. Parser returned: ' + e.message);
ProcessErrors(null, variables, e.message, null, { hdr: 'Error!',
msg: 'Failed to parse JSON string. Parser returned: ' + e.message });
}
} else {
try {
json_data = jsyaml.load(variables);
}
catch(e) {
json_data = {};
$log.error('Failed to parse YAML string. Parser returned: ' + e.message);
ProcessErrors(null, variables, e.message, null, { hdr: 'Error!',
msg: 'Failed to parse YAML string. Parser returned: ' + e.message });
}
}
// Make sure our JSON is actually an object
if (typeof json_data !== 'object') {
ProcessErrors(null, variables, null, null, { hdr: 'Error!',
msg: 'Failed to parse variables. Attempted to parse ' + parseType + ' Parser did not return an object.' });
}
result = json_data;
if (stringify) {
if ($.isEmptyObject(json_data)) {
result = "";
} else {
result = JSON.stringify(json_data, undefined, '\t');
}
}
return result;
};
}]);

View File

@@ -11,7 +11,7 @@
'use strict'; 'use strict';
angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', 'ListGenerator', 'AuthService', angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', 'ListGenerator', 'AuthService',
'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper', 'SearchHelper' 'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper', 'SearchHelper', 'VariablesHelper',
]) ])
.factory('WatchInventoryWindowResize', ['ApplyEllipsis', .factory('WatchInventoryWindowResize', ['ApplyEllipsis',
@@ -40,8 +40,9 @@ angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationLis
]) ])
.factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList', .factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList',
'GetBasePath', 'ParseTypeChange', 'Wait', 'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON',
function (InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, Wait) { function (InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, Wait,
ToJSON) {
return function (params) { return function (params) {
// Save inventory property modifications // Save inventory property modifications
@@ -53,66 +54,57 @@ angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationLis
Wait('start'); Wait('start');
try { // Make sure we have valid variable data
// Make sure we have valid variable data json_data = ToJSON(scope.inventoryParseType, scope.inventory_variables);
if (scope.inventoryParseType === 'json') {
json_data = JSON.parse(scope.inventory_variables); //make sure JSON parses
} else {
json_data = jsyaml.load(scope.inventory_variables); //parse yaml
}
// Make sure our JSON is actually an object // Make sure our JSON is actually an object
if (typeof json_data !== 'object') { if (typeof json_data !== 'object') {
throw "failed to return an object!"; throw "failed to return an object!";
} }
data = {}; data = {};
for (fld in form.fields) { for (fld in form.fields) {
if (fld !== 'inventory_variables') { if (fld !== 'inventory_variables') {
if (form.fields[fld].realName) { if (form.fields[fld].realName) {
data[form.fields[fld].realName] = scope[fld]; data[form.fields[fld].realName] = scope[fld];
} else { } else {
data[fld] = scope[fld]; data[fld] = scope[fld];
}
} }
} }
Rest.setUrl(defaultUrl + scope.inventory_id + '/');
Rest.put(data)
.success(function (data) {
if (scope.inventory_variables) {
Rest.setUrl(data.related.variable_data);
Rest.put(json_data)
.success(function () {
Wait('stop');
scope.$emit('InventorySaved');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to update inventory varaibles. PUT returned status: ' + status
});
});
} else {
scope.$emit('InventorySaved');
}
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to update inventory. POST returned status: ' + status });
});
} catch (err) {
Wait('stop');
Alert("Error", "Error parsing inventory variables. Parser returned: " + err);
} }
Rest.setUrl(defaultUrl + scope.inventory_id + '/');
Rest.put(data)
.success(function (data) {
if (scope.inventory_variables) {
Rest.setUrl(data.related.variable_data);
Rest.put(json_data)
.success(function () {
Wait('stop');
scope.$emit('InventorySaved');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to update inventory varaibles. PUT returned status: ' + status
});
});
} else {
scope.$emit('InventorySaved');
}
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to update inventory. POST returned status: ' + status });
});
}; };
} }
]) ])
.factory('EditInventoryProperties', ['InventoryForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList', .factory('EditInventoryProperties', ['InventoryForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList',
'GetBasePath', 'ParseTypeChange', 'SaveInventory', 'Wait', 'Store', 'SearchInit', 'GetBasePath', 'ParseTypeChange', 'SaveInventory', 'Wait', 'Store', 'SearchInit', 'ParseVariableString',
function (InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, SaveInventory, function (InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, SaveInventory,
Wait, Store, SearchInit) { Wait, Store, SearchInit, ParseVariableString) {
return function (params) { return function (params) {
var parent_scope = params.scope, var parent_scope = params.scope,
@@ -146,23 +138,10 @@ angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationLis
Rest.setUrl(GetBasePath('inventory') + inventory_id + '/'); Rest.setUrl(GetBasePath('inventory') + inventory_id + '/');
Rest.get() Rest.get()
.success(function (data) { .success(function (data) {
var fld, json_obj; var fld;
for (fld in form.fields) { for (fld in form.fields) {
if (fld === 'inventory_variables') { if (fld === 'inventory_variables') {
// Parse variables, converting to YAML. scope.inventory_variables = ParseVariableString(data.variables);
if ($.isEmptyObject(data.variables) || data.variables === "{}" ||
data.variables === "null" || data.variables === "") {
scope.inventory_variables = "---";
} else {
try {
json_obj = JSON.parse(data.variables);
scope.inventory_variables = jsyaml.safeDump(json_obj);
} catch (err) {
Alert('Variable Parse Error', 'Attempted to parse variables for inventory: ' + inventory_id +
'. Parse returned: ' + err);
scope.inventory_variables = '---';
}
}
master.inventory_variables = scope.variables; master.inventory_variables = scope.variables;
} else if (fld === 'inventory_name') { } else if (fld === 'inventory_name') {
scope[fld] = data.name; scope[fld] = data.name;
@@ -174,7 +153,6 @@ angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationLis
scope[fld] = data[fld]; scope[fld] = data[fld];
master[fld] = scope[fld]; master[fld] = scope[fld];
} }
if (form.fields[fld].sourceModel && data.summary_fields && if (form.fields[fld].sourceModel && data.summary_fields &&
data.summary_fields[form.fields[fld].sourceModel]) { data.summary_fields[form.fields[fld].sourceModel]) {
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =

View File

@@ -127,6 +127,7 @@
<script src="{{ STATIC_URL }}js/helpers/Permissions.js"></script> <script src="{{ STATIC_URL }}js/helpers/Permissions.js"></script>
<script src="{{ STATIC_URL }}js/helpers/Groups.js"></script> <script src="{{ STATIC_URL }}js/helpers/Groups.js"></script>
<script src="{{ STATIC_URL }}js/helpers/Hosts.js"></script> <script src="{{ STATIC_URL }}js/helpers/Hosts.js"></script>
<script src="{{ STATIC_URL }}js/helpers/Variables.js"></script>
<script src="{{ STATIC_URL }}js/widgets/JobStatus.js"></script> <script src="{{ STATIC_URL }}js/widgets/JobStatus.js"></script>
<script src="{{ STATIC_URL }}js/widgets/InventorySyncStatus.js"></script> <script src="{{ STATIC_URL }}js/widgets/InventorySyncStatus.js"></script>
<script src="{{ STATIC_URL }}js/widgets/SCMSyncStatus.js"></script> <script src="{{ STATIC_URL }}js/widgets/SCMSyncStatus.js"></script>