Fix designate-dashboard lint

Previously the lint test was failing to run because there was no configuration file for it. This patch adds the .eslintrc file and fixes the found lint issues.
This patch also adds tox environments to run the lint and karma tests.

Change-Id: Idcef4c3ce4e9455acceed645c2530355989a7ee2
This commit is contained in:
Michael Johnson 2022-06-23 21:54:32 +00:00
parent 7ee73c9e1c
commit 64993055e7
29 changed files with 371 additions and 313 deletions

41
.eslintrc Normal file
View File

@ -0,0 +1,41 @@
# Set up globals
globals:
angular: false
extends: openstack
# Most environment options are not explicitly enabled or disabled, only
# included here for completeness' sake. They are commented out, because the
# global updates.py script would otherwise override them during a global
# requirements synchronization.
#
# Individual projects should choose which platforms they deploy to.
env:
# browser global variables.
browser: true
# Adds all of the Jasmine testing global variables for version 1.3 and 2.0.
jasmine: true
# Enable eslint-plugin-angular
plugins:
- angular
# Below we adjust rules specific to horizon's usage of openstack's linting
# rules, and its own plugin inclusions.
rules:
#############################################################################
# Disabled Rules from eslint-config-openstack
#############################################################################
valid-jsdoc: 1
brace-style: 1
no-extra-parens: 1
consistent-return: 1
callback-return: 1
guard-for-in: 1
block-scoped-var: 1
semi-spacing: 1
no-redeclare: 1
no-new: 1
no-warning-comments: 0

View File

@ -71,7 +71,8 @@
'designatedashboard.basePath' 'designatedashboard.basePath'
]; ];
function run(registry, basePath) { function run() {
// function run(registry, basePath) {
//registry.setDefaultSummaryTemplateUrl(basePath + 'table/default-drawer.html'); //registry.setDefaultSummaryTemplateUrl(basePath + 'table/default-drawer.html');
} }

View File

@ -37,12 +37,7 @@
'designatedashboard.resources.os-designate-floatingip.actions.unset' 'designatedashboard.resources.os-designate-floatingip.actions.unset'
]; ];
function run( function run(registry, resourceTypeString, setAction, unsetAction) {
registry,
resourceTypeString,
setAction,
unsetAction)
{
var resourceType = registry.getResourceType(resourceTypeString); var resourceType = registry.getResourceType(resourceTypeString);
resourceType resourceType

View File

@ -33,7 +33,7 @@
'horizon.framework.widgets.modal-wait-spinner.service' 'horizon.framework.widgets.modal-wait-spinner.service'
]; ];
/** /*
* @ngDoc factory * @ngDoc factory
* @name designatedashboard.resources.os-designate-floatingip.actions.set * @name designatedashboard.resources.os-designate-floatingip.actions.set
* *
@ -51,44 +51,44 @@
var dnsServiceEnabled; var dnsServiceEnabled;
var title = null; // Set once perform is called var title = null; // Set once perform is called
var formConfig = { var formConfig = {
"schema": { schema: {
"type": "object", type: "object",
"properties": { properties: {
"ptrdname": { ptrdname: {
"type": "string", type: "string",
"pattern": /^.+\.$/ pattern: /^.+\.$/
}, },
"description": { description: {
"type": "string" type: "string"
},
"ttl": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647
}, },
ttl: {
type: "integer",
minimum: 0,
maximum: 2147483647
}
} }
}, },
"form": [ form: [
{ {
"key": "ptrdname", key: "ptrdname",
"title": gettext("Domain Name"), title: gettext("Domain Name"),
"description": gettext("Domain name ending in '.'"), description: gettext("Domain name ending in '.'"),
"validationMessage": gettext("Domain must end with '.'"), validationMessage: gettext("Domain must end with '.'"),
"placeholder": "smtp.example.com.", placeholder: "smtp.example.com.",
"type": "text", type: "text",
"required": true required: true
}, },
{ {
"key": "description", key: "description",
"type": "textarea", type: "textarea",
"title": gettext("Description"), title: gettext("Description"),
"description": gettext("Details about the PTR record.") description: gettext("Details about the PTR record.")
}, },
{ {
"key": "ttl", key: "ttl",
"title": gettext("TTL"), title: gettext("TTL"),
"description": gettext("Time To Live in seconds."), description: gettext("Time To Live in seconds."),
"type": "number" type: "number"
} }
] ]
}; };

View File

@ -34,7 +34,7 @@
'horizon.framework.widgets.modal-wait-spinner.service' 'horizon.framework.widgets.modal-wait-spinner.service'
]; ];
/** /*
* @ngDoc factory * @ngDoc factory
* @name designatedashboard.resources.os-designate-floatingip.actions.unset * @name designatedashboard.resources.os-designate-floatingip.actions.unset
* *
@ -50,20 +50,21 @@
schemaFormModalService, schemaFormModalService,
toast, toast,
waitSpinner) { waitSpinner) {
var dnsServiceEnabled;
var title = null; // Set on perform var title = null; // Set on perform
var currentFloatingIpId; // Used to remember the ID we are modifying since it isn't returned by the unset API call // currentFloatingIpId is used to remember the ID we are modifying since
// it isn't returned by the unset API call
var dnsServiceEnabled, currentFloatingIpId;
// Unset it just a simple case of "set", but with ptrdname of 'null' // Unset it just a simple case of "set", but with ptrdname of 'null'
var formConfig = { var formConfig = {
"schema": { schema: {
"type": "object", type: "object",
"properties": { properties: {
} }
}, },
"form": [ form: [
], ],
"model": { model: {
} }
}; };
@ -118,7 +119,7 @@
waitSpinner.hideModalSpinner(); waitSpinner.hideModalSpinner();
} }
function onSuccess(response) { function onSuccess() {
waitSpinner.hideModalSpinner(); waitSpinner.hideModalSpinner();
toast.add('success', message.success); toast.add('success', message.success);

View File

@ -26,7 +26,7 @@
'horizon.framework.widgets.toast.service' 'horizon.framework.widgets.toast.service'
]; ];
/** /*
* @ngdoc service * @ngdoc service
* @param {Object} httpService * @param {Object} httpService
* @param {Object} toastService * @param {Object} toastService
@ -60,7 +60,7 @@
* @returns {Object} The result of the API call * @returns {Object} The result of the API call
*/ */
function list(params) { function list(params) {
var config = params ? {'params': params} : {}; var config = params ? {params: params} : {};
return httpService.get(apiPassthroughUrl + 'v2/reverse/floatingips', config) return httpService.get(apiPassthroughUrl + 'v2/reverse/floatingips', config)
.catch(function () { .catch(function () {
toastService.add('error', gettext('Unable to retrieve the floating ip PTRs.')); toastService.add('error', gettext('Unable to retrieve the floating ip PTRs.'));
@ -68,7 +68,7 @@
} }
function get(id, params) { function get(id, params) {
var config = params ? {'params': params} : {}; var config = params ? {params: params} : {};
return httpService.get(apiPassthroughUrl + 'v2/reverse/floatingips/' + id, config) return httpService.get(apiPassthroughUrl + 'v2/reverse/floatingips/' + id, config)
.catch(function () { .catch(function () {
toastService.add('error', gettext('Unable to get the floating ip PTR ' + id)); toastService.add('error', gettext('Unable to get the floating ip PTR ' + id));
@ -94,10 +94,11 @@
description: data.description, description: data.description,
ttl: data.ttl ttl: data.ttl
}; };
return httpService.patch(apiPassthroughUrl + 'v2/reverse/floatingips/' + floatingIpID, apiData) return httpService.patch(
apiPassthroughUrl + 'v2/reverse/floatingips/' + floatingIpID, apiData)
.catch(function () { .catch(function () {
toastService.add('error', gettext('Unable to set the floating IP PTR record.')); toastService.add('error', gettext('Unable to set the floating IP PTR record.'));
}) });
} }
/** /**
@ -115,7 +116,7 @@
ptrdname: null, ptrdname: null,
description: null, description: null,
ttl: null ttl: null
}) });
} }
} }
}()); }());

View File

@ -18,7 +18,9 @@
angular angular
.module('designatedashboard.resources.os-designate-floatingip.details') .module('designatedashboard.resources.os-designate-floatingip.details')
.controller('designatedashboard.resources.os-designate-floatingip.details.overviewController', controller); .controller(
'designatedashboard.resources.os-designate-floatingip.details.overviewController',
controller);
controller.$inject = [ controller.$inject = [
'designatedashboard.resources.os-designate-floatingip.resourceType', 'designatedashboard.resources.os-designate-floatingip.resourceType',
@ -33,7 +35,7 @@
) { ) {
var ctrl = this; var ctrl = this;
ctrl.item; ctrl.item = {};
ctrl.resourceType = registry.getResourceType(resourceTypeCode); ctrl.resourceType = registry.getResourceType(resourceTypeCode);
$scope.context.loadPromise.then(onGetResponse); $scope.context.loadPromise.then(onGetResponse);

View File

@ -40,7 +40,8 @@
config.$inject = [ '$provide', '$windowProvider' ]; config.$inject = [ '$provide', '$windowProvider' ];
function config($provide, $windowProvider) { function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'designatedashboard/resources/os-designate-floatingip/'; var path = $windowProvider.$get().STATIC_URL +
'designatedashboard/resources/os-designate-floatingip/';
$provide.constant('designatedashboard.resources.os-designate-floatingip.basePath', path); $provide.constant('designatedashboard.resources.os-designate-floatingip.basePath', path);
} }
@ -52,13 +53,7 @@
'designatedashboard.resources.util' 'designatedashboard.resources.util'
]; ];
function run( function run(detailRoute, registry, api, resourceTypeString, util) {
detailRoute,
registry,
api,
resourceTypeString,
util)
{
var resourceType = registry.getResourceType(resourceTypeString); var resourceType = registry.getResourceType(resourceTypeString);
resourceType resourceType
.setNames(gettext('Floating IP'), gettext('Floating IPs')) .setNames(gettext('Floating IP'), gettext('Floating IPs'))
@ -100,12 +95,13 @@
id: 'address', id: 'address',
priority: 1, priority: 1,
sortDefault: true, sortDefault: true,
template: '<a ng-href="{$ \'' + detailRoute + 'OS::Designate::FloatingIp/\' + item.id $}">{$ item.address $}</a>' template: '<a ng-href="{$ \'' + detailRoute +
'OS::Designate::FloatingIp/\' + item.id $}">{$ item.address $}</a>'
}) })
.append({ .append({
id: 'ptrdname', id: 'ptrdname',
filters: ['noValue'], filters: ['noValue'],
priority: 1, priority: 1
}) })
.append({ .append({
id: 'status', id: 'status',

View File

@ -27,7 +27,7 @@
'designatedashboard.resources.os-designate-recordset.typeMap' 'designatedashboard.resources.os-designate-recordset.typeMap'
]; ];
/** /*
* Service to return a schema form config for action forms. Especially useful for forms * Service to return a schema form config for action forms. Especially useful for forms
* like create and update that differ only in the readonly state of certain form fields. * like create and update that differ only in the readonly state of certain form fields.
* *
@ -43,7 +43,7 @@
///////////////// /////////////////
/** /*
* Returns the create form config * Returns the create form config
* @returns {{schema, form, model}|*} * @returns {{schema, form, model}|*}
*/ */
@ -51,7 +51,7 @@
return getCreateUpdateFormConfig(false); return getCreateUpdateFormConfig(false);
} }
/** /*
* Return the update form config * Return the update form config
* @returns {{schema, form, model}|*} * @returns {{schema, form, model}|*}
*/ */
@ -59,7 +59,7 @@
return getCreateUpdateFormConfig(true); return getCreateUpdateFormConfig(true);
} }
/** /*
* Return the create/update form. The two forms are identical except for * Return the create/update form. The two forms are identical except for
* during update, some fields are read-only. * during update, some fields are read-only.
* *
@ -68,96 +68,96 @@
*/ */
function getCreateUpdateFormConfig(readonly) { function getCreateUpdateFormConfig(readonly) {
return { return {
"schema": { schema: {
"type": "object", type: "object",
"properties": { properties: {
"name": { name: {
"type": "string", type: "string",
"pattern": /^.+\.$/ pattern: /^.+\.$/
}, },
"description": { description: {
"type": "string" type: "string"
}, },
"type": { type: {
"type": "string", type: "string",
"enum": editableTypes enum: editableTypes
}, },
"ttl": { ttl: {
"type": "integer", type: "integer",
"minimum": 1, minimum: 1,
"maximum": 2147483647 maximum: 2147483647
}, },
"records": { records: {
"type": "array", type: "array",
"items": { items: {
"type": "object", type: "object",
"properties": { properties: {
"record": { record: {
"type": "string" type: "string"
} }
} }
}, },
"minItems": 1, minItems: 1,
"uniqueItems": true uniqueItems: true
} }
} }
}, },
"form": [ form: [
{ {
"key": "type", key: "type",
"readonly": readonly, readonly: readonly,
"title": gettext("Type"), title: gettext("Type"),
"description": gettext("Select the type of record set"), description: gettext("Select the type of record set"),
"type": "select", type: "select",
"titleMap": editableTypes.map(function toTitleMap(type) { titleMap: editableTypes.map(function toTitleMap(type) {
return { return {
"value": type, value: type,
"name": typeMap[type] name: typeMap[type]
} };
}), }),
"required": true required: true
}, },
{ {
"key": "name", key: "name",
"readonly": readonly, readonly: readonly,
"type": "text", type: "text",
"title": gettext("Name"), title: gettext("Name"),
"description": gettext("DNS name for the record set, ending in '.'"), description: gettext("DNS name for the record set, ending in '.'"),
"validationMessage": gettext("DNS name must end with '.'"), validationMessage: gettext("DNS name must end with '.'"),
"placeholder": "www.example.com.", placeholder: "www.example.com.",
"required": true required: true
}, },
{ {
"key": "description", key: "description",
"type": "textarea", type: "textarea",
"title": gettext("Description"), title: gettext("Description"),
"description": gettext("Details about the zone.") description: gettext("Details about the zone.")
}, },
{ {
"key": "ttl", key: "ttl",
"title": gettext("TTL"), title: gettext("TTL"),
"description": gettext("Time To Live in seconds."), description: gettext("Time To Live in seconds."),
"type": "number", type: "number",
"required": true required: true
}, },
{ {
"key": "records", key: "records",
"title": gettext("Records"), title: gettext("Records"),
"type": "array", type: "array",
"description": gettext("Records for the record set."), description: gettext("Records for the record set."),
"add": gettext("Add Record"), add: gettext("Add Record"),
"items": [ items: [
{ {
"key": "records[].record", key: "records[].record",
"title": gettext("Record") title: gettext("Record")
} }
], ],
"required": true required: true
} }
], ],
"model": { model: {
"type": "A", type: "A",
"ttl": 3600 ttl: 3600
} }
}; };
} }

View File

@ -35,7 +35,7 @@
'horizon.framework.widgets.modal-wait-spinner.service' 'horizon.framework.widgets.modal-wait-spinner.service'
]; ];
/** /*
* @ngDoc factory * @ngDoc factory
* @name designatedashboard.resources.os-designate-recordset.actions.create * @name designatedashboard.resources.os-designate-recordset.actions.create
* *
@ -73,7 +73,7 @@
dnsServiceEnabled = serviceCatalog.ifTypeEnabled('dns'); dnsServiceEnabled = serviceCatalog.ifTypeEnabled('dns');
} }
function allowed(item) { function allowed() {
return $q.all([ return $q.all([
createRecordSetPolicy, createRecordSetPolicy,
dnsServiceEnabled dnsServiceEnabled

View File

@ -111,7 +111,7 @@
// Remember the record sets we are allowed to delete so that on delete modal submit // Remember the record sets we are allowed to delete so that on delete modal submit
// we can map the recordset ID back to the full recordset. Then we can fetch the // we can map the recordset ID back to the full recordset. Then we can fetch the
// corresponding zone ID // corresponding zone ID
allowedRecordsets = result.pass.map(getEntity) allowedRecordsets = result.pass.map(getEntity);
outcome = deleteModal.open(scope, allowedRecordsets, context).then(createResult); outcome = deleteModal.open(scope, allowedRecordsets, context).then(createResult);
} }
return outcome; return outcome;
@ -157,7 +157,7 @@
function editableRecordType(recordset) { function editableRecordType(recordset) {
return $qExtensions.booleanAsPromise( return $qExtensions.booleanAsPromise(
!(recordset.type == 'NS' && recordset.name == recordset.zone_name) && // not apex NS !(recordset.type === 'NS' && recordset.name === recordset.zone_name) && // not apex NS
editableTypes.indexOf(recordset.type) > -1 editableTypes.indexOf(recordset.type) > -1
); );
} }
@ -165,7 +165,7 @@
function deleteRecordSet(recordSetId) { function deleteRecordSet(recordSetId) {
var recordSet = allowedRecordsets.find(function(element) { var recordSet = allowedRecordsets.find(function(element) {
return element.id === recordSetId; return element.id === recordSetId;
}) });
return recordsetApi.deleteRecordSet(recordSet.zone_id, recordSet.id); return recordsetApi.deleteRecordSet(recordSet.zone_id, recordSet.id);
} }

View File

@ -37,7 +37,7 @@
'horizon.framework.widgets.modal-wait-spinner.service' 'horizon.framework.widgets.modal-wait-spinner.service'
]; ];
/** /*
* @ngDoc factory * @ngDoc factory
* @name designatedashboard.resources.os-designate-recordset.actions.update * @name designatedashboard.resources.os-designate-recordset.actions.update
* *
@ -56,7 +56,7 @@
schemaFormModalService, schemaFormModalService,
toast, toast,
waitSpinner) { waitSpinner) {
var updateRecordSetPolicy, dnsServiceEnabled; var updateRecordSetPolicy;
var title = gettext("Update Record Set"); var title = gettext("Update Record Set");
var message = { var message = {
success: gettext('Record Set %s was successfully updated.') success: gettext('Record Set %s was successfully updated.')
@ -93,7 +93,7 @@
function editableRecordType(recordset) { function editableRecordType(recordset) {
return $qExtensions.booleanAsPromise( return $qExtensions.booleanAsPromise(
!(recordset.type == 'NS' && recordset.name == recordset.zone_name) && // not apex NS !(recordset.type === 'NS' && recordset.name === recordset.zone_name) && // not apex NS
editableTypes.indexOf(recordset.type) > -1 editableTypes.indexOf(recordset.type) > -1
); );
} }
@ -111,7 +111,7 @@
// Map the records objects to record objects // Map the records objects to record objects
if (item.hasOwnProperty("records")) { if (item.hasOwnProperty("records")) {
var records = item.records.map(function (item) { var records = item.records.map(function (item) {
return {"record": item} return {record: item};
}); });
formConfig.model.records = records; formConfig.model.records = records;
} }

View File

@ -27,7 +27,7 @@
'horizon.framework.widgets.toast.service' 'horizon.framework.widgets.toast.service'
]; ];
/** /*
* @ngdoc service * @ngdoc service
* @param {Object} httpService * @param {Object} httpService
* @param {Object} toastService * @param {Object} toastService
@ -48,7 +48,7 @@
/////////////// ///////////////
/** /*
* @name list * @name list
* @description * @description
* Get a list of record sets. * Get a list of record sets.
@ -68,7 +68,7 @@
}); });
} }
/** /*
* @name get * @name get
* @description * @description
* Get a single record set by ID. * Get a single record set by ID.
@ -86,14 +86,15 @@
// common when then delete action removes a record set. Mask this failure by // common when then delete action removes a record set. Mask this failure by
// always returning a successful promise instead of terminating the $http promise // always returning a successful promise instead of terminating the $http promise
// in the .error handler. // in the .error handler.
return httpService.get(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId + '/') return httpService.get(
apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId + '/')
.then(undefined, function onError() { .then(undefined, function onError() {
toastService.add('error', gettext('Unable to retrieve the record set.')); toastService.add('error', gettext('Unable to retrieve the record set.'));
return $q.when({}); return $q.when({});
}); });
} }
/** /*
* @name delete * @name delete
* @description * @description
* Delete a single record set by ID * Delete a single record set by ID
@ -106,7 +107,8 @@
* @returns {*} * @returns {*}
*/ */
function deleteRecordSet(zoneId, recordSetId) { function deleteRecordSet(zoneId, recordSetId) {
return httpService.delete(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId + '/') return httpService.delete(
apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId + '/')
.catch(function () { .catch(function () {
toastService.add('error', gettext('Unable to delete the record set.')); toastService.add('error', gettext('Unable to delete the record set.'));
}); });
@ -127,7 +129,8 @@
description: data.description, description: data.description,
records: data.records records: data.records
}; };
return httpService.put(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId, apiData) return httpService.put(
apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId, apiData)
.catch(function () { .catch(function () {
toastService.add('error', gettext('Unable to update the record set.')); toastService.add('error', gettext('Unable to update the record set.'));
}); });

View File

@ -32,7 +32,7 @@
'designatedashboard.resources.os-designate-recordset.resourceType', 'designatedashboard.resources.os-designate-recordset.resourceType',
'designatedashboard.resources.os-designate-recordset.api', 'designatedashboard.resources.os-designate-recordset.api',
'designatedashboard.resources.os-designate-recordset.basePath', 'designatedashboard.resources.os-designate-recordset.basePath',
'horizon.framework.conf.resource-type-registry.service', 'horizon.framework.conf.resource-type-registry.service'
]; ];
function run( function run(
@ -48,7 +48,7 @@
.setPathParser(pathParser) .setPathParser(pathParser)
.setSummaryTemplateUrl(basePath + 'details/drawer.html'); .setSummaryTemplateUrl(basePath + 'details/drawer.html');
/** /*
* *
* @param identifier * @param identifier
* The object returned by the pathParser containing the zone ID and record set ID to load * The object returned by the pathParser containing the zone ID and record set ID to load
@ -57,7 +57,7 @@
return recordSetApi.get(identifier.zoneId, identifier.recordSetId); return recordSetApi.get(identifier.zoneId, identifier.recordSetId);
} }
/** /*
* Because a record set is contained by a zone, we implement a custom * Because a record set is contained by a zone, we implement a custom
* pathGenerator to encode the zone ID and record set ID for the generic * pathGenerator to encode the zone ID and record set ID for the generic
* details panel. * details panel.
@ -71,7 +71,7 @@
return item.zone_id + '/' + item.id; return item.zone_id + '/' + item.id;
} }
/** /*
* Given a path, extract the zone and record set ids * Given a path, extract the zone and record set ids
* *
* @param path * @param path
@ -86,14 +86,14 @@
return { return {
zoneId: split[0], zoneId: split[0],
recordSetId: split[1] recordSetId: split[1]
} };
} }
resourceType.detailsViews resourceType.detailsViews
.prepend({ .prepend({
id: 'recordsetDetailsOverview', id: 'recordsetDetailsOverview',
name: gettext('Overview'), name: gettext('Overview'),
template: basePath + 'details/overview.html', template: basePath + 'details/overview.html'
}, 0); }, 0);
// Append a record set view to the zones resource view // Append a record set view to the zones resource view
@ -102,7 +102,7 @@
.append({ .append({
id: 'zoneRecordSets', id: 'zoneRecordSets',
name: gettext('Record Sets'), name: gettext('Record Sets'),
template: basePath + 'details/zone-recordsets.html', template: basePath + 'details/zone-recordsets.html'
}); });
} }

View File

@ -18,7 +18,9 @@
angular angular
.module('designatedashboard.resources.os-designate-recordset') .module('designatedashboard.resources.os-designate-recordset')
.controller('designatedashboard.resources.os-designate-recordset.zoneRecordSetsController', controller); .controller(
'designatedashboard.resources.os-designate-recordset.zoneRecordSetsController',
controller);
controller.$inject = [ controller.$inject = [
'$scope', '$scope',

View File

@ -37,18 +37,18 @@
.constant( .constant(
'designatedashboard.resources.os-designate-recordset.typeMap', 'designatedashboard.resources.os-designate-recordset.typeMap',
{ {
'A': gettext('A - Address record'), A: gettext('A - Address record'),
'AAAA': gettext('AAAA - IPv6 address record'), AAAA: gettext('AAAA - IPv6 address record'),
'CNAME': gettext('CNAME - Canonical name record'), CNAME: gettext('CNAME - Canonical name record'),
'MX': gettext('MX - Mail exchange record'), MX: gettext('MX - Mail exchange record'),
'PTR': gettext('PTR - Pointer record'), PTR: gettext('PTR - Pointer record'),
'SPF': gettext('SPF - Sender Policy Framework'), SPF: gettext('SPF - Sender Policy Framework'),
'SRV': gettext('SRV - Service locator'), SRV: gettext('SRV - Service locator'),
'SSHFP': gettext('SSHFP - SSH Public Key Fingerprint'), SSHFP: gettext('SSHFP - SSH Public Key Fingerprint'),
'TXT': gettext('TXT - Text record'), TXT: gettext('TXT - Text record'),
'SOA': gettext('SOA - Start of authority record'), SOA: gettext('SOA - Start of authority record'),
'NS': gettext('NS - Name server'), NS: gettext('NS - Name server'),
'CAA': gettext('CAA - Certificate Authority Authorization record'), CAA: gettext('CAA - Certificate Authority Authorization record')
}) })
.constant( .constant(
'designatedashboard.resources.os-designate-recordset.editableTypes', 'designatedashboard.resources.os-designate-recordset.editableTypes',
@ -63,7 +63,7 @@
"SRV", "SRV",
"SSHFP", "SSHFP",
"TXT", "TXT",
"CAA", "CAA"
]) ])
.config(config) .config(config)
.run(run); .run(run);
@ -71,7 +71,8 @@
config.$inject = ['$provide', '$windowProvider']; config.$inject = ['$provide', '$windowProvider'];
function config($provide, $windowProvider) { function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'designatedashboard/resources/os-designate-recordset/'; var path = $windowProvider.$get().STATIC_URL +
'designatedashboard/resources/os-designate-recordset/';
$provide.constant('designatedashboard.resources.os-designate-recordset.basePath', path); $provide.constant('designatedashboard.resources.os-designate-recordset.basePath', path);
} }
@ -166,7 +167,9 @@
sortDefault: true, sortDefault: true,
filters: ['noName'], filters: ['noName'],
// For link format, see pathGenerator in details.module.js // For link format, see pathGenerator in details.module.js
template: '<a ng-href="{$ \'' + detailRoute + 'OS::Designate::RecordSet/\' + item.zone_id + \'/\' + item.id $}">{$ item.name $}</a>' template: '<a ng-href="{$ \'' + detailRoute +
'OS::Designate::RecordSet/\' + item.zone_id + \'/\' +' +
'item.id $}">{$ item.name $}</a>'
}) })
.append({ .append({
id: 'type', id: 'type',
@ -198,7 +201,7 @@
return { return {
label: typeMap[key], label: typeMap[key],
key: key key: key
} };
}) })
}) })
.append({ .append({
@ -220,7 +223,7 @@
] ]
}); });
/** /*
* list all recordsets within a zone. Requires "zoneId" in the params. All other * list all recordsets within a zone. Requires "zoneId" in the params. All other
* params will be passed unmodified as URL params to the API. * params will be passed unmodified as URL params to the API.
* *

View File

@ -41,7 +41,7 @@
///////////////// /////////////////
/** /*
* Returns the create zone form config * Returns the create zone form config
* @returns {{schema, form, model}|*} * @returns {{schema, form, model}|*}
*/ */
@ -49,7 +49,7 @@
return getCreateUpdateFormConfig(false); return getCreateUpdateFormConfig(false);
} }
/** /*
* Return the update zone form config * Return the update zone form config
* @returns {{schema, form, model}|*} * @returns {{schema, form, model}|*}
*/ */
@ -57,7 +57,7 @@
return getCreateUpdateFormConfig(true); return getCreateUpdateFormConfig(true);
} }
/** /*
* Return the create/update zone form. The two forms are identical except for * Return the create/update zone form. The two forms are identical except for
* during update, some fields are read-only. * during update, some fields are read-only.
* *
@ -66,118 +66,118 @@
*/ */
function getCreateUpdateFormConfig(readonly) { function getCreateUpdateFormConfig(readonly) {
return { return {
"schema": { schema: {
"type": "object", type: "object",
"properties": { properties: {
"name": { name: {
"type": "string", type: "string",
"pattern": /^.+\.$/ pattern: /^.+\.$/
}, },
"description": { description: {
"type": "string" type: "string"
}, },
"email": { email: {
"type": "string", type: "string",
"format": "email", format: "email",
"pattern": /^[^@]+@[^@]+$/ pattern: /^[^@]+@[^@]+$/
}, },
"type": { type: {
"type": "string", type: "string",
"enum": [ enum: [
"PRIMARY", "PRIMARY",
"SECONDARY" "SECONDARY"
] ]
}, },
"ttl": { ttl: {
"type": "integer", type: "integer",
"minimum": 1, minimum: 1,
"maximum": 2147483647 maximum: 2147483647
}, },
"masters": { masters: {
"type": "array", type: "array",
"items": { items: {
"type": "object", type: "object",
"properties": { properties: {
"address": { address: {
"type": "string" type: "string"
} }
} }
}, },
"minItems": 1, minItems: 1,
"uniqueItems": true uniqueItems: true
} }
} }
}, },
"form": [ form: [
{ {
"key": "name", key: "name",
"readonly": readonly, readonly: readonly,
"title": gettext("Name"), title: gettext("Name"),
"description": gettext("Zone name ending in '.'"), description: gettext("Zone name ending in '.'"),
"validationMessage": gettext("Zone must end with '.'"), validationMessage: gettext("Zone must end with '.'"),
"placeholder": "example.com.", placeholder: "example.com.",
"type": "text", type: "text",
"required": true required: true
}, },
{ {
"key": "description", key: "description",
"type": "textarea", type: "textarea",
"title": gettext("Description"), title: gettext("Description"),
"description": gettext("Details about the zone.") description: gettext("Details about the zone.")
}, },
{ {
"key": "email", key: "email",
"title": gettext("Email Address"), title: gettext("Email Address"),
"description": gettext("Email address to contact the zone owner."), description: gettext("Email address to contact the zone owner."),
"validationMessage": gettext("Email address must contain a single '@' character"), validationMessage: gettext("Email address must contain a single '@' character"),
"type": "text", type: "text",
"condition": "model.type == 'PRIMARY'", condition: "model.type == 'PRIMARY'",
"required": true required: true
}, },
{ {
"key": "ttl", key: "ttl",
"title": gettext("TTL"), title: gettext("TTL"),
"description": gettext("Time To Live in seconds."), description: gettext("Time To Live in seconds."),
"type": "number", type: "number",
"condition": "model.type == 'PRIMARY'", condition: "model.type == 'PRIMARY'",
"required": true required: true
}, },
{ {
"key": "type", key: "type",
"readonly": readonly, readonly: readonly,
"title": gettext("Type"), title: gettext("Type"),
"description": gettext("Select the type of zone"), description: gettext("Select the type of zone"),
"type": "select", type: "select",
"titleMap": [ titleMap: [
{ {
"value": "PRIMARY", value: "PRIMARY",
"name": gettext("Primary") name: gettext("Primary")
}, },
{ {
"value": "SECONDARY", value: "SECONDARY",
"name": gettext("Secondary") name: gettext("Secondary")
} }
] ]
}, },
{ {
"key": "masters", key: "masters",
"readonly": readonly, readonly: readonly,
"title": gettext("Masters"), title: gettext("Masters"),
"type": "array", type: "array",
"description": gettext("DNS master(s) for the Secondary zone."), description: gettext("DNS master(s) for the Secondary zone."),
"condition": "model.type == 'SECONDARY'", condition: "model.type == 'SECONDARY'",
"add": gettext("Add Master"), add: gettext("Add Master"),
"items": [ items: [
{ {
"key": "masters[].address", key: "masters[].address",
"title": gettext("IP Address") title: gettext("IP Address")
} }
] ]
} }
], ],
"model": { model: {
"type": "PRIMARY", type: "PRIMARY",
"ttl": 3600 ttl: 3600
} }
}; };
} }

View File

@ -34,7 +34,7 @@
'horizon.framework.widgets.modal-wait-spinner.service' 'horizon.framework.widgets.modal-wait-spinner.service'
]; ];
/** /*
* @ngDoc factory * @ngDoc factory
* @name designatedashboard.resources.os-designate-zone.actions.create * @name designatedashboard.resources.os-designate-zone.actions.create
* *

View File

@ -35,7 +35,7 @@
'horizon.framework.widgets.modal-wait-spinner.service' 'horizon.framework.widgets.modal-wait-spinner.service'
]; ];
/** /*
* @ngDoc factory * @ngDoc factory
* @name designatedashboard.resources.os-designate-zone.actions.update * @name designatedashboard.resources.os-designate-zone.actions.update
* *
@ -52,7 +52,7 @@
schemaFormModalService, schemaFormModalService,
toast, toast,
waitSpinner) { waitSpinner) {
var updateZonePolicy, dnsServiceEnabled; var updateZonePolicy;
var title = gettext("Update Zone"); var title = gettext("Update Zone");
var message = { var message = {
success: gettext('Zone %s was successfully updated.') success: gettext('Zone %s was successfully updated.')
@ -98,8 +98,8 @@
// Map the masters objects to address objects // Map the masters objects to address objects
if (item.hasOwnProperty("masters")) { if (item.hasOwnProperty("masters")) {
var masters = item.masters.map(function (item) { var masters = item.masters.map(function (item) {
return { "address": item } return { address: item };
}) });
formConfig.masters = masters; formConfig.masters = masters;
} }
return schemaFormModalService.open(formConfig).then(onSubmit, onCancel); return schemaFormModalService.open(formConfig).then(onSubmit, onCancel);
@ -112,7 +112,7 @@
if (context.model.hasOwnProperty("masters")) { if (context.model.hasOwnProperty("masters")) {
var masters = context.model.masters.map(function (item) { var masters = context.model.masters.map(function (item) {
return item.address; return item.address;
}) });
zoneModel.masters = masters; zoneModel.masters = masters;
} }

View File

@ -26,7 +26,7 @@
'horizon.framework.widgets.toast.service' 'horizon.framework.widgets.toast.service'
]; ];
/** /*
* @ngdoc service * @ngdoc service
* @param {Object} httpService * @param {Object} httpService
* @param {Object} toastService * @param {Object} toastService
@ -69,7 +69,7 @@
}); });
}*/ }*/
function list(params) { function list(params) {
var config = params ? {'params': params} : {}; var config = params ? {params: params} : {};
return httpService.get(apiPassthroughUrl + 'v2/zones/', config) return httpService.get(apiPassthroughUrl + 'v2/zones/', config)
.catch(function () { .catch(function () {
toastService.add('error', gettext('Unable to retrieve the zone.')); toastService.add('error', gettext('Unable to retrieve the zone.'));
@ -93,7 +93,7 @@
}); });
} }
/** /*
* @name deleteZone * @name deleteZone
* @description * @description
* Delete a single zone by ID * Delete a single zone by ID
@ -121,7 +121,7 @@
return httpService.post(apiPassthroughUrl + 'v2/zones/', data) return httpService.post(apiPassthroughUrl + 'v2/zones/', data)
.catch(function() { .catch(function() {
toastService.add('error', gettext('Unable to create the zone.')); toastService.add('error', gettext('Unable to create the zone.'));
}) });
} }
/** /**
@ -143,10 +143,10 @@
ttl: data.ttl, ttl: data.ttl,
description: data.description description: data.description
}; };
return httpService.patch(apiPassthroughUrl + 'v2/zones/' + id + '/', apiData ) return httpService.patch(apiPassthroughUrl + 'v2/zones/' + id + '/', apiData)
.catch(function() { .catch(function() {
toastService.add('error', gettext('Unable to update the zone.')); toastService.add('error', gettext('Unable to update the zone.'));
}) });
} }
} }
}()); }());

View File

@ -50,7 +50,7 @@
.prepend({ .prepend({
id: 'zoneDetailsOverview', id: 'zoneDetailsOverview',
name: gettext('Overview'), name: gettext('Overview'),
template: basePath + 'details/overview.html', template: basePath + 'details/overview.html'
}, 0); }, 0);
function loadFunction(identifier) { function loadFunction(identifier) {

View File

@ -33,7 +33,7 @@
) { ) {
var ctrl = this; var ctrl = this;
ctrl.item; ctrl.item = {};
ctrl.resourceType = registry.getResourceType(resourceTypeCode); ctrl.resourceType = registry.getResourceType(resourceTypeCode);
$scope.context.loadPromise.then(onGetResponse); $scope.context.loadPromise.then(onGetResponse);

View File

@ -40,7 +40,8 @@
config.$inject = ['$provide', '$windowProvider']; config.$inject = ['$provide', '$windowProvider'];
function config($provide, $windowProvider) { function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'designatedashboard/resources/os-designate-zone/'; var path = $windowProvider.$get().STATIC_URL +
'designatedashboard/resources/os-designate-zone/';
$provide.constant('designatedashboard.resources.os-designate-zone.basePath', path); $provide.constant('designatedashboard.resources.os-designate-zone.basePath', path);
} }
@ -136,7 +137,8 @@
id: 'name', id: 'name',
priority: 1, priority: 1,
sortDefault: true, sortDefault: true,
template: '<a ng-href="{$ \'' + detailRoute + 'OS::Designate::Zone/\' + item.id $}">{$ item.name $}</a>' template: '<a ng-href="{$ \'' + detailRoute +
'OS::Designate::Zone/\' + item.id $}">{$ item.name $}</a>'
}) })
.append({ .append({
id: 'type', id: 'type',
@ -185,9 +187,9 @@
function typeMap() { function typeMap() {
return { return {
'primary': gettext('Primary'), primary: gettext('Primary'),
'secondary': gettext('Secondary') secondary: gettext('Secondary')
} };
} }
function listZones() { function listZones() {

View File

@ -21,10 +21,9 @@
.factory('designatedashboard.resources.util', utilService); .factory('designatedashboard.resources.util', utilService);
utilService.$inject = [ utilService.$inject = [
'horizon.framework.util.q.extensions', 'horizon.framework.util.q.extensions'
]; ];
function utilService($qExtensions) { function utilService($qExtensions) {
var service = { var service = {
notDeleted: notDeleted, notDeleted: notDeleted,
@ -47,9 +46,9 @@
return $qExtensions.booleanAsPromise(resource.status !== 'PENDING'); return $qExtensions.booleanAsPromise(resource.status !== 'PENDING');
} }
/** /*
* Build a model object based on the given item, using only the fields present in the form config 'key's. * Build a model object based on the given item, using only the fields
* Only 'truthy' values are copied. * present in the form config 'key's. Only 'truthy' values are copied.
* *
* @param form - an array of objects describing the form. Must have a 'key' attribute. * @param form - an array of objects describing the form. Must have a 'key' attribute.
* @param item - the data to copy into the model * @param item - the data to copy into the model
@ -68,19 +67,19 @@
function actionMap() { function actionMap() {
return { return {
'none': gettext('None'), none: gettext('None'),
'create': gettext('Create') create: gettext('Create')
} };
} }
function statusMap() { function statusMap() {
return { return {
'active': gettext('Active'), active: gettext('Active'),
'pending': gettext('Pending') pending: gettext('Pending')
} };
} }
/** /*
* hz-resource-table tracks by 'id' which doesn't change when an individual item is updated. * hz-resource-table tracks by 'id' which doesn't change when an individual item is updated.
* Create a synthetic '_timestampId' using the item id plus the specified timestamp field. * Create a synthetic '_timestampId' using the item id plus the specified timestamp field.
* When this field is used as a track-by in hz-resource-table, items in the table to update * When this field is used as a track-by in hz-resource-table, items in the table to update
@ -97,7 +96,7 @@
var _idField = idField || 'id'; var _idField = idField || 'id';
var timestamp = Date.now(); var timestamp = Date.now();
items.map(function annotateFloatingIp(item) { items.map(function annotateFloatingIp(item) {
if ( angular.isDefined(timestampField) ) { if (angular.isDefined(timestampField)) {
timestamp = item[timestampField]; timestamp = item[timestampField];
} }
item._timestampId = item[_idField] + timestamp; item._timestampId = item[_idField] + timestamp;

View File

@ -6,8 +6,9 @@
"repository": "none", "repository": "none",
"license": "Apache 2.0", "license": "Apache 2.0",
"devDependencies": { "devDependencies": {
"eslint": "1.10.3", "eslint": "^1.10.3",
"eslint-config-openstack": "1.2.4", "eslint-config-openstack": "1.2.4",
"eslint-plugin-angular": "^1.0.1",
"jasmine-core": "2.4.1", "jasmine-core": "2.4.1",
"karma": "~1.1.2", "karma": "~1.1.2",
"karma-firefox-launcher": "2.1.0", "karma-firefox-launcher": "2.1.0",
@ -23,4 +24,3 @@
}, },
"dependencies": {} "dependencies": {}
} }

12
tox.ini
View File

@ -66,3 +66,15 @@ exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,rele
[testenv:releasenotes] [testenv:releasenotes]
commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
[testenv:eslint]
# npm must be installed on the system, for example
# sudo apt-get install npm
commands = npm install
npm run lint
[testenv:karma]
# npm must be installed on the system, for example
# sudo apt-get install npm
commands = npm install
npm test