Merge ~mpontillo/maas:dns-ui--add-record into maas:master

Proposed by Mike Pontillo
Status: Superseded
Proposed branch: ~mpontillo/maas:dns-ui--add-record
Merge into: maas:master
Prerequisite: ~mpontillo/maas:use-built-javascript-for-tests
Diff against target: 347 lines (+173/-17)
7 files modified
src/maasserver/static/js/angular/controllers/domain_details.js (+20/-4)
src/maasserver/static/js/angular/controllers/tests/test_domain_details.js (+4/-4)
src/maasserver/static/js/angular/directives/maas_obj_form.js (+17/-3)
src/maasserver/static/js/angular/directives/tests/test_maas_obj_form.js (+25/-0)
src/maasserver/static/js/angular/factories/domains.js (+12/-0)
src/maasserver/static/js/angular/factories/tests/test_domains.js (+47/-0)
src/maasserver/static/partials/domain-details.html (+48/-6)
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
MAAS Lander Pending
Andres Rodriguez Pending
Review via email: mp+341124@code.launchpad.net

This proposal supersedes a proposal from 2018-03-08.

This proposal has been superseded by a proposal from 2018-03-08.

Commit message

Add form to create new DNS records.

 * Change karma configuration to use built JavaScript.
 * Reorganize YUI code into a specific directory.
 * Remove react and react-dom from vendor bundle.

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote : Posted in a previous version of this proposal

UNIT TESTS
-b dns-ui--add-record lp:~mpontillo/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/1876/console
COMMIT: 0bc51c7dacfb0c0a403c6918718914ac07fe94ae

review: Needs Fixing
Revision history for this message
Andres Rodriguez (andreserl) wrote : Posted in a previous version of this proposal

Just one thing in line I think it needs fixing.

review: Needs Fixing
Revision history for this message
Andres Rodriguez (andreserl) wrote : Posted in a previous version of this proposal

lgtm!

review: Approve
Revision history for this message
Mike Pontillo (mpontillo) : Posted in a previous version of this proposal
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Combining this branch with the branch it depends on.

review: Approve

Unmerged commits

dfcf3ba... by Mike Pontillo

Fix JavaScript tests.

0bc51c7... by Mike Pontillo

Fix JavaScript tests.

055af07... by Mike Pontillo

Add options drop-down for record type.

22b9d21... by Mike Pontillo

Support A and AAAA records indirectly.

661a207... by Mike Pontillo

Fix button alignment.

4113f5d... by Mike Pontillo

Add initial form for adding DNS records.

45aa878... by Mike Pontillo

Add 'Add record' button to domain details page.

9e45ddc... by Mike Pontillo

Remove YUI JavaScript file mistakenly added to karma config.

eead99a... by Mike Pontillo

Reorganize legacy YUI code. Use built JavaScript and sourcemaps during testing.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/static/js/angular/controllers/domain_details.js b/src/maasserver/static/js/angular/controllers/domain_details.js
2index d8e50e5..fb0c4a2 100644
3--- a/src/maasserver/static/js/angular/controllers/domain_details.js
4+++ b/src/maasserver/static/js/angular/controllers/domain_details.js
5@@ -23,6 +23,14 @@ angular.module('MAAS').controller('DomainDetailsController', [
6 $scope.domain = null;
7 $scope.predicate = "name";
8 $scope.reverse = false;
9+ $scope.action = null;
10+
11+ $scope.domainsManager = DomainsManager;
12+ $scope.newObject = {};
13+
14+ $scope.supportedRecordTypes = [
15+ 'A', 'AAAA', 'CNAME', 'MX', 'NS', 'SRV', 'SSHFP', 'TXT'
16+ ];
17
18 // Updates the page title.
19 function updateTitle() {
20@@ -61,18 +69,26 @@ angular.module('MAAS').controller('DomainDetailsController', [
21 // Called when the delete domain button is pressed.
22 $scope.deleteButton = function() {
23 $scope.error = null;
24- $scope.confirmingDelete = true;
25+ $scope.actionInProgress = true;
26+ $scope.action = 'delete';
27+ };
28+
29+ // Called when the add record button is pressed.
30+ $scope.addRecordButton = function() {
31+ $scope.error = null;
32+ $scope.actionInProgress = true;
33+ $scope.action = 'add_record';
34 };
35
36 // Called when the cancel delete domain button is pressed.
37- $scope.cancelDeleteButton = function() {
38- $scope.confirmingDelete = false;
39+ $scope.cancelAction = function() {
40+ $scope.actionInProgress = false;
41 };
42
43 // Called when the confirm delete domain button is pressed.
44 $scope.deleteConfirmButton = function() {
45 DomainsManager.deleteDomain($scope.domain).then(function() {
46- $scope.confirmingDelete = false;
47+ $scope.actionInProgress = false;
48 $location.path("/domains");
49 }, function(error) {
50 $scope.error =
51diff --git a/src/maasserver/static/js/angular/controllers/tests/test_domain_details.js b/src/maasserver/static/js/angular/controllers/tests/test_domain_details.js
52index d1cfcb1..391efea 100644
53--- a/src/maasserver/static/js/angular/controllers/tests/test_domain_details.js
54+++ b/src/maasserver/static/js/angular/controllers/tests/test_domain_details.js
55@@ -186,7 +186,7 @@ describe("DomainDetailsController", function() {
56 it("confirms delete", function() {
57 var controller = makeControllerResolveSetActiveItem();
58 $scope.deleteButton();
59- expect($scope.confirmingDelete).toBe(true);
60+ expect($scope.actionInProgress).toBe(true);
61 });
62
63 it("clears error", function() {
64@@ -197,13 +197,13 @@ describe("DomainDetailsController", function() {
65 });
66 });
67
68- describe("cancelDeleteButton", function() {
69+ describe("cancelAction", function() {
70
71 it("cancels delete", function() {
72 var controller = makeControllerResolveSetActiveItem();
73 $scope.deleteButton();
74- $scope.cancelDeleteButton();
75- expect($scope.confirmingDelete).toBe(false);
76+ $scope.cancelAction();
77+ expect($scope.actionInProgress).toBe(false);
78 });
79 });
80
81diff --git a/src/maasserver/static/js/angular/directives/maas_obj_form.js b/src/maasserver/static/js/angular/directives/maas_obj_form.js
82index da907f9..7de2337 100644
83--- a/src/maasserver/static/js/angular/directives/maas_obj_form.js
84+++ b/src/maasserver/static/js/angular/directives/maas_obj_form.js
85@@ -543,10 +543,10 @@ angular.module('MAAS').directive('maasObjField', ['$compile',
86
87 // type and key required.
88 var missingAttrs = [];
89- if(!angular.isString(attrs.type) && attrs.type.length === 0) {
90+ if(!angular.isString(attrs.type) || attrs.type.length === 0) {
91 missingAttrs.push("type");
92 }
93- if(!angular.isString(attrs.key) && attrs.key.length === 0) {
94+ if(!angular.isString(attrs.key) || attrs.key.length === 0) {
95 missingAttrs.push("key");
96 }
97 if(missingAttrs.length > 0) {
98@@ -561,7 +561,8 @@ angular.module('MAAS').directive('maasObjField', ['$compile',
99 // Render the label.
100 var label = attrs.label || attrs.key;
101
102- if(attrs.disableLabel !== "true") {
103+ if(attrs.disableLabel !== "true" &&
104+ !(attrs.type === "hidden")) {
105 var labelElement = angular.element('<label/>');
106 labelElement.attr('for', attrs.key);
107 labelElement.text(label);
108@@ -830,6 +831,19 @@ angular.module('MAAS').directive('maasObjField', ['$compile',
109 return val.text;
110 });
111 };
112+ } else if(attrs.type === "hidden") {
113+ var hiddenScope = scope.$new();
114+ hiddenScope._toggle = controller.registerField(
115+ attrs.key, scope);
116+ inputElement = angular.element([
117+ '<input type="hidden" name="' + attrs.key + '" ',
118+ 'id="' + attrs.key + '" ',
119+ 'value="' + attrs.value + '">',
120+ '</input>'
121+ ].join(''));
122+ inputElement = $compile(inputElement)(hiddenScope);
123+ scope.getValue = () => attrs.value;
124+ scope.updateValue = () => null;
125 } else if(attrs.type === "onoffswitch") {
126 var switchScope = scope.$new();
127 switchScope._toggle = controller.registerField(
128diff --git a/src/maasserver/static/js/angular/directives/tests/test_maas_obj_form.js b/src/maasserver/static/js/angular/directives/tests/test_maas_obj_form.js
129index d37b891..031c881 100644
130--- a/src/maasserver/static/js/angular/directives/tests/test_maas_obj_form.js
131+++ b/src/maasserver/static/js/angular/directives/tests/test_maas_obj_form.js
132@@ -442,6 +442,31 @@ describe("maasObjForm", function() {
133 });
134 });
135
136+ describe("hidden", function() {
137+
138+ var directive, options;
139+ beforeEach(function() {
140+ $scope.obj = {
141+ key: false
142+ };
143+ $scope.manager = {};
144+ var html = [
145+ '<maas-obj-form obj="obj" manager="manager">',
146+ '<maas-obj-field type="hidden" key="key" label="Key" ',
147+ 'value="value">',
148+ '</maas-obj-field>',
149+ '</maas-obj-form>'
150+ ].join('');
151+ directive = compileDirective(html);
152+ });
153+
154+ it("creates hidden input field", function() {
155+ var onoff = angular.element(directive.find("input"));
156+ expect(onoff.length).toBe(1);
157+ });
158+ });
159+
160+
161 describe("single field", function() {
162
163 var directive, updateItemMethod, saveDefer;
164diff --git a/src/maasserver/static/js/angular/factories/domains.js b/src/maasserver/static/js/angular/factories/domains.js
165index 0d3fdf2..2662db8 100644
166--- a/src/maasserver/static/js/angular/factories/domains.js
167+++ b/src/maasserver/static/js/angular/factories/domains.js
168@@ -42,6 +42,18 @@ angular.module('MAAS').factory(
169 return RegionConnection.callMethod("domain.delete", domain);
170 };
171
172+ // Create a DNS record.
173+ DomainsManager.prototype.createDNSRecord = function(record) {
174+ if(record.rrtype === 'A' || record.rrtype === 'AAAA') {
175+ record.ip_addresses = record.rrdata.split(/[ ,]+/);
176+ return RegionConnection.callMethod(
177+ "domain.create_dnsresource", record);
178+ } else {
179+ return RegionConnection.callMethod(
180+ "domain.create_dnsdata", record);
181+ }
182+ };
183+
184 DomainsManager.prototype.getDefaultDomain = function() {
185 if(this._items.length === 0) {
186 return null;
187diff --git a/src/maasserver/static/js/angular/factories/tests/test_domains.js b/src/maasserver/static/js/angular/factories/tests/test_domains.js
188index b740939..7988f01 100644
189--- a/src/maasserver/static/js/angular/factories/tests/test_domains.js
190+++ b/src/maasserver/static/js/angular/factories/tests/test_domains.js
191@@ -14,6 +14,7 @@ describe("DomainsManager", function() {
192 var DomainsManager;
193 beforeEach(inject(function($injector) {
194 DomainsManager = $injector.get("DomainsManager");
195+ RegionConnection = $injector.get("RegionConnection");
196 }));
197
198 // Make a random domain.
199@@ -60,6 +61,52 @@ describe("DomainsManager", function() {
200 });
201 });
202
203+ describe("createDNSRecord", function() {
204+ it("calls create_dnsresource for A record", function() {
205+ spyOn(RegionConnection, "callMethod");
206+ var record = {
207+ 'rrtype': 'A',
208+ 'rrdata': '192.168.0.1'
209+ };
210+ DomainsManager.createDNSRecord(record);
211+ expect(RegionConnection.callMethod).toHaveBeenCalledWith(
212+ "domain.create_dnsresource", record);
213+ });
214+
215+ it("calls create_dnsresource for AAAA record", function() {
216+ spyOn(RegionConnection, "callMethod");
217+ var record = {
218+ 'rrtype': 'AAAA',
219+ 'rrdata': '2001:db8:1'
220+ };
221+ DomainsManager.createDNSRecord(record);
222+ expect(RegionConnection.callMethod).toHaveBeenCalledWith(
223+ "domain.create_dnsresource", record);
224+ });
225+
226+ it("converts rrdata into list for A and AAAA", function() {
227+ spyOn(RegionConnection, "callMethod");
228+ var record = {
229+ 'rrtype': 'AAAA',
230+ 'rrdata': '2001:db8::1, 10.0.0.1 127.0.0.1'
231+ };
232+ DomainsManager.createDNSRecord(record);
233+ expect(record.ip_addresses).toEqual([
234+ '2001:db8::1', '10.0.0.1', '127.0.0.1'
235+ ]);
236+ });
237+
238+ it("calls create_dnsdata for other types", function() {
239+ spyOn(RegionConnection, "callMethod");
240+ var record = {
241+ 'rrtype': 'SRV'
242+ };
243+ DomainsManager.createDNSRecord(record);
244+ expect(RegionConnection.callMethod).toHaveBeenCalledWith(
245+ "domain.create_dnsdata", record);
246+ });
247+ });
248+
249 describe("getDomainByName", function() {
250 it("returns null when no domains", function() {
251 expect(DomainsManager.getDomainByName('meh')).toBe(null);
252diff --git a/src/maasserver/static/partials/domain-details.html b/src/maasserver/static/partials/domain-details.html
253index 451a203..31343c0 100644
254--- a/src/maasserver/static/partials/domain-details.html
255+++ b/src/maasserver/static/partials/domain-details.html
256@@ -13,14 +13,17 @@
257 </p>
258 </div>
259 <div class="col-4">
260- <div class="page-header__controls ng-hide" data-ng-show="isSuperUser() && !isDefaultDomain() && !loading">
261+ <div class="page-header__controls ng-hide" data-ng-show="isSuperUser() && !loading">
262 <button class="p-button--negative"
263 data-ng-click="deleteButton()"
264- data-ng-hide="confirmingDelete">Delete domain</button>
265+ data-ng-hide="actionInProgress || isDefaultDomain()">Delete domain</button>
266+ <button class="p-button"
267+ data-ng-click="addRecordButton()"
268+ data-ng-hide="actionInProgress">Add record</button>
269 </div>
270 </div>
271 </div>
272- <div data-ng-if="confirmingDelete">
273+ <div data-ng-if="actionInProgress && action === 'delete'">
274 <div class="row u-no-margin--top ng-hide" data-ng-hide="canBeDeleted()">
275 <hr />
276 <div class="row">
277@@ -30,7 +33,7 @@
278 </p>
279 </div>
280 <div class="col-4 u-align--right">
281- <button class="p-button--base" type="button" data-ng-click="cancelDeleteButton()">Cancel</button>
282+ <button class="p-button--base" type="button" data-ng-click="cancelAction()">Cancel</button>
283 </div>
284 </div>
285 </div>
286@@ -43,7 +46,7 @@
287 </p>
288 </div>
289 <div class="col-4 u-align--right">
290- <button class="p-button--base" type="button" data-ng-click="cancelDeleteButton()">Cancel</button>
291+ <button class="p-button--base" type="button" data-ng-click="cancelAction()">Cancel</button>
292 <button class="p-button--negative" data-ng-click="deleteConfirmButton()">Delete domain</button>
293 </div>
294 </div>
295@@ -57,12 +60,51 @@
296 </p>
297 </div>
298 <div class="col-4 u-align--right">
299- <button class="p-button--base" type="button" data-ng-click="cancelDeleteButton()">Cancel</button>
300+ <button class="p-button--base" type="button" data-ng-click="cancelAction()">Cancel</button>
301 <button class="p-button--neutral" data-ng-click="deleteConfirmButton()">Retry</button>
302 </div>
303 </div>
304 </div>
305 </div>
306+ <div data-ng-if="actionInProgress && action === 'add_record'">
307+ <maas-obj-form obj="newObject" manager="domainsManager" manager-method="createDNSRecord" class="p-form--stacked"
308+ table-form="true" save-on-blur="false" after-save="cancelAction">
309+ <!-- pre-process="actionDNSRecordPreSave" -->
310+ <section class="row">
311+ <div class="col-6">
312+ <maas-obj-field
313+ subtle="false" type="text" key="name" label="Name" placeholder="Name"
314+ label-width="two" input-width="three"></maas-obj-field>
315+ <maas-obj-field
316+ subtle="false" type="options" key="rrtype" label="Type"
317+ options="type as type for type in supportedRecordTypes"
318+ label-width="two" input-width="three"></maas-obj-field>
319+ </div>
320+ <div class="col-6">
321+ <maas-obj-field
322+ subtle="false" type="text" key="rrdata" label="Data"
323+ placeholder="Data"
324+ label-width="two" input-width="three"></maas-obj-field>
325+ <maas-obj-field
326+ subtle="false" type="text" key="ttl" label="TTL"
327+ placeholder="TTL in seconds (optional)"
328+ label-width="two" input-width="three"></maas-obj-field>
329+ <maas-obj-field type="hidden" key="domain" value="{$ domain.id $}" />
330+ </div>
331+ </section>
332+ <section class="row">
333+ <div class="col-8">
334+ <maas-obj-errors class="page-header__message page-header__message--error"></maas-obj-errors>
335+ </div>
336+ <div class="col-4">
337+ <div class="u-align--right">
338+ <button class="p-button" data-ng-click="cancelAction()">Cancel</button>
339+ <button class="p-button--positive" maas-obj-save>Add record</button>
340+ </div>
341+ </div>
342+ </div>
343+ </maas-obj-form>
344+ </div>
345 </header>
346 <div class="p-strip" data-ng-show="!loading">
347 <section class="row">

Subscribers

People subscribed via source and target branches