diff --git a/ui-v2/app/adapters/acl.js b/ui-v2/app/adapters/acl.js
index 7f691be7bd57..408969a10424 100644
--- a/ui-v2/app/adapters/acl.js
+++ b/ui-v2/app/adapters/acl.js
@@ -1,9 +1,6 @@
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
-import { get } from '@ember/object';
-import EmberError from '@ember/error';
-import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/acl';
+import { SLUG_KEY } from 'consul-ui/models/acl';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
-import { OK as HTTP_OK, UNAUTHORIZED as HTTP_UNAUTHORIZED } from 'consul-ui/utils/http/status';
export default Adapter.extend({
requestForQuery: function(request, { dc, index }) {
@@ -25,65 +22,45 @@ export default Adapter.extend({
${{ index }}
`;
},
- requestForCreateRecord: function(request, data) {
+ requestForCreateRecord: function(request, serialized, data) {
// https://www.consul.io/api/acl.html#create-acl-token
return request`
PUT /v1/acl/create?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+
+ ${serialized}
`;
},
- requestForUpdateRecord: function(request, data) {
- // the id is in the data, don't add it in here
+ requestForUpdateRecord: function(request, serialized, data) {
+ // the id is in the data, don't add it into the URL
// https://www.consul.io/api/acl.html#update-acl-token
return request`
PUT /v1/acl/update?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+
+ ${serialized}
`;
},
- requestForDeleteRecord: function(request, data) {
+ requestForDeleteRecord: function(request, serialized, data) {
// https://www.consul.io/api/acl.html#delete-acl-token
return request`
PUT /v1/acl/destroy/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
- requestForCloneRecord: function(request, data) {
+ requestForCloneRecord: function(request, serialized, data) {
// https://www.consul.io/api/acl.html#clone-acl-token
return request`
PUT /v1/acl/clone/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
clone: function(store, type, id, snapshot) {
- const serializer = store.serializerFor(type.modelName);
- const unserialized = this.snapshotToJSON(snapshot, type);
- const serialized = serializer.serialize(snapshot, {});
- return get(this, 'client')
- .request(request => this.requestForClone(request, unserialized), serialized)
- .then(respond => serializer.respondForQueryRecord(respond, unserialized));
- },
- handleResponse: function(status, headers, payload, requestData) {
- let response = payload;
- const method = requestData.method;
- if (status === HTTP_OK) {
- const url = this.parseURL(requestData.url);
- switch (true) {
- case response === true:
- response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY);
- break;
- case this.isQueryRecord(url):
- response = this.handleSingleResponse(url, response[0], PRIMARY_KEY, SLUG_KEY);
- break;
- case this.isUpdateRecord(url, method):
- case this.isCreateRecord(url, method):
- case this.isCloneRecord(url, method):
- response = this.handleSingleResponse(url, response, PRIMARY_KEY, SLUG_KEY);
- break;
- default:
- response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
- }
- } else if (status === HTTP_UNAUTHORIZED) {
- const e = new EmberError();
- e.code = status;
- e.message = payload;
- throw e;
- }
- return this._super(status, headers, response, requestData);
+ return this.request(
+ function(adapter, request, serialized, unserialized) {
+ return adapter.requestForCloneRecord(request, serialized, unserialized);
+ },
+ function(serializer, response, serialized, unserialized) {
+ return serializer.respondForCreateRecord(response, serialized, unserialized);
+ },
+ snapshot,
+ type.modelName
+ );
},
});
diff --git a/ui-v2/app/adapters/application.js b/ui-v2/app/adapters/application.js
index ea13bbfa9357..1e90593fe0c3 100644
--- a/ui-v2/app/adapters/application.js
+++ b/ui-v2/app/adapters/application.js
@@ -1,8 +1,44 @@
import Adapter from './http';
import { inject as service } from '@ember/service';
-
+import { get } from '@ember/object';
export const DATACENTER_QUERY_PARAM = 'dc';
export default Adapter.extend({
repo: service('settings'),
client: service('client/http'),
+ // TODO: kinda protected for the moment
+ // decide where this should go either read/write from http
+ // should somehow use this or vice versa
+ request: function(req, resp, obj, modelName) {
+ const client = get(this, 'client');
+ const store = get(this, 'store');
+ const adapter = this;
+
+ let unserialized, serialized;
+ const serializer = store.serializerFor(modelName);
+ // workable way to decide whether this is a snapshot
+ // Snapshot is private so we can't do instanceof here
+ if (obj.constructor.name === 'Snapshot') {
+ unserialized = obj.attributes();
+ serialized = serializer.serialize(obj, {});
+ } else {
+ unserialized = obj;
+ serialized = unserialized;
+ }
+
+ return client
+ .request(function(request) {
+ return req(adapter, request, serialized, unserialized);
+ })
+ .catch(function(e) {
+ return adapter.error(e);
+ })
+ .then(function(response) {
+ // TODO: When HTTPAdapter:responder changes, this will also need to change
+ return resp(serializer, response, serialized, unserialized);
+ });
+ // TODO: Potentially add specific serializer errors here
+ // .catch(function(e) {
+ // return Promise.reject(e);
+ // });
+ },
});
diff --git a/ui-v2/app/adapters/http.js b/ui-v2/app/adapters/http.js
index c91ffa065dd5..d6d2e284451b 100644
--- a/ui-v2/app/adapters/http.js
+++ b/ui-v2/app/adapters/http.js
@@ -1,4 +1,5 @@
import Adapter from 'ember-data/adapter';
+import { get } from '@ember/object';
import {
AbortError,
TimeoutError,
@@ -10,12 +11,47 @@ import {
InvalidError,
AdapterError,
} from 'ember-data/adapters/errors';
-import { get } from '@ember/object';
+// TODO: This is a little skeleton cb function
+// is to be replaced soon with something slightly more involved
+const responder = function(response) {
+ return response;
+};
+const read = function(adapter, serializer, client, type, query) {
+ return client
+ .request(function(request) {
+ return adapter[`requestFor${type}`](request, query);
+ })
+ .catch(function(e) {
+ return adapter.error(e);
+ })
+ .then(function(response) {
+ return serializer[`respondFor${type}`](responder(response), query);
+ });
+ // TODO: Potentially add specific serializer errors here
+ // .catch(function(e) {
+ // return Promise.reject(e);
+ // });
+};
+const write = function(adapter, serializer, client, type, snapshot) {
+ const unserialized = snapshot.attributes();
+ const serialized = serializer.serialize(snapshot, {});
+ return client
+ .request(function(request) {
+ return adapter[`requestFor${type}`](request, serialized, unserialized);
+ })
+ .catch(function(e) {
+ return adapter.error(e);
+ })
+ .then(function(response) {
+ return serializer[`respondFor${type}`](responder(response), serialized, unserialized);
+ });
+ // TODO: Potentially add specific serializer errors here
+ // .catch(function(e) {
+ // return Promise.reject(e);
+ // });
+};
export default Adapter.extend({
- snapshotToJSON: function(snapshot, type, options) {
- return snapshot.attributes();
- },
error: function(err) {
const errors = [
{
@@ -62,51 +98,45 @@ export default Adapter.extend({
throw error;
},
query: function(store, type, query) {
- const serializer = store.serializerFor(type.modelName);
- return get(this, 'client')
- .request(request => this.requestForQuery(request, query))
- .catch(e => this.error(e))
- .then(respond => serializer.respondForQuery(respond, query, type));
+ return read(this, store.serializerFor(type.modelName), get(this, 'client'), 'Query', query);
},
queryRecord: function(store, type, query) {
- const serializer = store.serializerFor(type.modelName);
- return get(this, 'client')
- .request(request => this.requestForQueryRecord(request, query))
- .catch(e => this.error(e))
- .then(respond => serializer.respondForQueryRecord(respond, query));
+ return read(
+ this,
+ store.serializerFor(type.modelName),
+ get(this, 'client'),
+ 'QueryRecord',
+ query
+ );
},
findAll: function(store, type) {
- const serializer = store.serializerFor(type.modelName);
- return get(this, 'client')
- .request(request => this.requestForFindAll(request))
- .catch(e => this.error(e))
- .then(respond => serializer.respondForFindAll(respond));
+ return read(this, store.serializerFor(type.modelName), get(this, 'client'), 'FindAll');
},
createRecord: function(store, type, snapshot) {
- const serializer = store.serializerFor(type.modelName);
- const unserialized = this.snapshotToJSON(snapshot, type);
- const serialized = serializer.serialize(snapshot, {});
- return get(this, 'client')
- .request(request => this.requestForCreateRecord(request, unserialized), serialized)
- .catch(e => this.error(e))
- .then(respond => serializer.respondForCreateRecord(respond, unserialized, type));
+ return write(
+ this,
+ store.serializerFor(type.modelName),
+ get(this, 'client'),
+ 'CreateRecord',
+ snapshot
+ );
},
updateRecord: function(store, type, snapshot) {
- const serializer = store.serializerFor(type.modelName);
- const unserialized = this.snapshotToJSON(snapshot, type);
- const serialized = serializer.serialize(snapshot, {});
- return get(this, 'client')
- .request(request => this.requestForUpdateRecord(request, unserialized), serialized)
- .catch(e => this.error(e))
- .then(respond => serializer.respondForUpdateRecord(respond, unserialized, type));
+ return write(
+ this,
+ store.serializerFor(type.modelName),
+ get(this, 'client'),
+ 'UpdateRecord',
+ snapshot
+ );
},
deleteRecord: function(store, type, snapshot) {
- const serializer = store.serializerFor(type.modelName);
- const unserialized = this.snapshotToJSON(snapshot, type);
- const serialized = serializer.serialize(snapshot, {});
- return get(this, 'client')
- .request(request => this.requestForDeleteRecord(request, unserialized), serialized)
- .catch(e => this.error(e))
- .then(respond => serializer.respondForDeleteRecord(respond, unserialized, type));
+ return write(
+ this,
+ store.serializerFor(type.modelName),
+ get(this, 'client'),
+ 'DeleteRecord',
+ snapshot
+ );
},
});
diff --git a/ui-v2/app/adapters/intention.js b/ui-v2/app/adapters/intention.js
index 2b30b3d2ac3e..f0dee774e805 100644
--- a/ui-v2/app/adapters/intention.js
+++ b/ui-v2/app/adapters/intention.js
@@ -19,18 +19,22 @@ export default Adapter.extend({
${{ index }}
`;
},
- requestForCreateRecord: function(request, data) {
+ requestForCreateRecord: function(request, serialized, data) {
// TODO: need to make sure we remove dc
return request`
POST /v1/connect/intentions?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+
+ ${serialized}
`;
},
- requestForUpdateRecord: function(request, data) {
+ requestForUpdateRecord: function(request, serialized, data) {
return request`
PUT /v1/connect/intentions/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+
+ ${serialized}
`;
},
- requestForDeleteRecord: function(request, data) {
+ requestForDeleteRecord: function(request, serialized, data) {
return request`
DELETE /v1/connect/intentions/${data[SLUG_KEY]}?${{
[API_DATACENTER_KEY]: data[DATACENTER_KEY],
diff --git a/ui-v2/app/adapters/kv.js b/ui-v2/app/adapters/kv.js
index 6b81a5dd6157..2655953e9d67 100644
--- a/ui-v2/app/adapters/kv.js
+++ b/ui-v2/app/adapters/kv.js
@@ -28,19 +28,25 @@ export default Adapter.extend({
${{ index }}
`;
},
- requestForCreateRecord: function(request, data) {
+ // TODO: Should we replace text/plain here with x-www-form-encoded?
+ // See https://github.com/hashicorp/consul/issues/3804
+ requestForCreateRecord: function(request, serialized, data) {
return request`
PUT /v1/kv/${keyToArray(data[SLUG_KEY])}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
- Content-Type: application/x-www-form-urlencoded
+ Content-Type: text/plain; charset=utf-8
+
+ ${serialized}
`;
},
- requestForUpdateRecord: function(request, data) {
+ requestForUpdateRecord: function(request, serialized, data) {
return request`
PUT /v1/kv/${keyToArray(data[SLUG_KEY])}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
- Content-Type: application/x-www-form-urlencoded
+ Content-Type: text/plain; charset=utf-8
+
+ ${serialized}
`;
},
- requestForDeleteRecord: function(request, data) {
+ requestForDeleteRecord: function(request, serialized, data) {
let recurse;
if (isFolder(data[SLUG_KEY])) {
recurse = null;
diff --git a/ui-v2/app/adapters/policy.js b/ui-v2/app/adapters/policy.js
index ec2d0d244a0a..ed5772878dc5 100644
--- a/ui-v2/app/adapters/policy.js
+++ b/ui-v2/app/adapters/policy.js
@@ -21,17 +21,21 @@ export default Adapter.extend({
${{ index }}
`;
},
- requestForCreateRecord: function(request, data) {
+ requestForCreateRecord: function(request, serialized, data) {
return request`
PUT /v1/acl/policy?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+
+ ${serialized}
`;
},
- requestForUpdateRecord: function(request, data) {
+ requestForUpdateRecord: function(request, serialized, data) {
return request`
PUT /v1/acl/policy/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+
+ ${serialized}
`;
},
- requestForDeleteRecord: function(request, data) {
+ requestForDeleteRecord: function(request, serialized, data) {
return request`
DELETE /v1/acl/policy/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
diff --git a/ui-v2/app/adapters/role.js b/ui-v2/app/adapters/role.js
index 84f6d39330b7..e7089640b3a3 100644
--- a/ui-v2/app/adapters/role.js
+++ b/ui-v2/app/adapters/role.js
@@ -21,17 +21,21 @@ export default Adapter.extend({
${{ index }}
`;
},
- requestForCreateRecord: function(request, data) {
+ requestForCreateRecord: function(request, serialized, data) {
return request`
PUT /v1/acl/role?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+
+ ${serialized}
`;
},
- requestForUpdateRecord: function(request, data) {
+ requestForUpdateRecord: function(request, serialized, data) {
return request`
PUT /v1/acl/role/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+
+ ${serialized}
`;
},
- requestForDeleteRecord: function(request, data) {
+ requestForDeleteRecord: function(request, serialized, data) {
return request`
DELETE /v1/acl/role/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
diff --git a/ui-v2/app/adapters/session.js b/ui-v2/app/adapters/session.js
index f178fa877b48..c324796a1b79 100644
--- a/ui-v2/app/adapters/session.js
+++ b/ui-v2/app/adapters/session.js
@@ -23,7 +23,7 @@ export default Adapter.extend({
${{ index }}
`;
},
- requestForDeleteRecord: function(request, data) {
+ requestForDeleteRecord: function(request, serialized, data) {
return request`
PUT /v1/session/destroy/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
diff --git a/ui-v2/app/adapters/token.js b/ui-v2/app/adapters/token.js
index ce2a5faca869..9f51ae70b51d 100644
--- a/ui-v2/app/adapters/token.js
+++ b/ui-v2/app/adapters/token.js
@@ -1,6 +1,5 @@
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
import { inject as service } from '@ember/service';
-import { get } from '@ember/object';
import { SLUG_KEY } from 'consul-ui/models/token';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
@@ -24,36 +23,39 @@ export default Adapter.extend({
${{ index }}
`;
},
- requestForCreateRecord: function(request, data) {
+ requestForCreateRecord: function(request, serialized, data) {
return request`
PUT /v1/acl/token?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
- requestForUpdateRecord: function(request, data) {
- // TODO: Serializer - Pretty sure this can go now
+ requestForUpdateRecord: function(request, serialized, data) {
+ // TODO: here we check data['Rules'] not serialized['Rules']
+ // data.Rules is not undefined, and serialized.Rules is not null
+ // revisit this at some point we should probably use serialized here
+
// If a token has Rules, use the old API
if (typeof data['Rules'] !== 'undefined') {
- // TODO: need to clean up vars sent
- data['ID'] = data['SecretID'];
- data['Name'] = data['Description'];
+ // https://www.consul.io/api/acl/legacy.html#update-acl-token
return request`
PUT /v1/acl/update?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+
+ ${serialized}
`;
}
- if (typeof data['SecretID'] !== 'undefined') {
- delete data['SecretID'];
- }
return request`
PUT /v1/acl/token/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
+
+ ${serialized}
`;
},
- requestForDeleteRecord: function(request, data) {
+ requestForDeleteRecord: function(request, serialized, data) {
return request`
DELETE /v1/acl/token/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
- requestForSelf: function(request, { dc, index, secret }) {
- // do we need dc and index here?
+ requestForSelf: function(request, serialized, { dc, index, secret }) {
+ // TODO: Change here and elsewhere to use Authorization Bearer Token
+ // https://github.com/hashicorp/consul/pull/4502
return request`
GET /v1/acl/token/self?${{ dc }}
X-Consul-Token: ${secret}
@@ -61,33 +63,48 @@ export default Adapter.extend({
${{ index }}
`;
},
- requestForClone: function(request, { dc, id }) {
+ requestForCloneRecord: function(request, serialized, unserialized) {
// this uses snapshots
+ const id = unserialized[SLUG_KEY];
+ const dc = unserialized[DATACENTER_KEY];
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return request`
- PUT /v1/acl/token/${id}/clone?${{ dc }}
+ PUT /v1/acl/token/${id}/clone?${{ [API_DATACENTER_KEY]: dc }}
`;
},
// TODO: self doesn't get passed a snapshot right now
// ideally it would just for consistency
- // thing is its probably not the same shape as a 'Token'
- self: function(store, type, unserialized) {
- const serializer = store.serializerFor(type.modelName);
- // const unserialized = this.snapshotToJSON(snapshot, type);
- const serialized = unserialized; //serializer.serialize(snapshot, {});
- return get(this, 'client')
- .request(request => this.requestForSelf(request, unserialized), serialized)
- .then(respond => serializer.respondForQueryRecord(respond, unserialized));
+ // thing is its probably not the same shape as a 'Token',
+ // plus we can't create Snapshots as they are private, see services/store.js
+ self: function(store, type, id, unserialized) {
+ return this.request(
+ function(adapter, request, serialized, unserialized) {
+ return adapter.requestForSelf(request, serialized, unserialized);
+ },
+ function(serializer, response, serialized, unserialized) {
+ return serializer.respondForQueryRecord(response, serialized, unserialized);
+ },
+ unserialized,
+ type.modelName
+ );
},
- // TODO: Does id even need to be here now?
clone: function(store, type, id, snapshot) {
- const serializer = store.serializerFor(type.modelName);
- const unserialized = this.snapshotToJSON(snapshot, type);
- const serialized = serializer.serialize(snapshot, {});
- return get(this, 'client')
- .request(request => this.requestForClone(request, unserialized), serialized)
- .then(respond => serializer.respondForQueryRecord(respond, unserialized));
+ return this.request(
+ function(adapter, request, serialized, unserialized) {
+ return adapter.requestForCloneRecord(request, serialized, unserialized);
+ },
+ function(serializer, response, serialized, unserialized) {
+ // here we just have to pass through the dc (like when querying)
+ // eventually the id is created with this dc value and the id talen from the
+ // json response of `acls/token/*/clone`
+ return serializer.respondForQueryRecord(response, {
+ [API_DATACENTER_KEY]: unserialized[SLUG_KEY],
+ });
+ },
+ snapshot,
+ type.modelName
+ );
},
});
diff --git a/ui-v2/app/serializers/application.js b/ui-v2/app/serializers/application.js
index 41bb5d63b9ca..486228251bb3 100644
--- a/ui-v2/app/serializers/application.js
+++ b/ui-v2/app/serializers/application.js
@@ -37,7 +37,7 @@ export default Serializer.extend({
attachHeaders(headers, this.fingerprint(this.primaryKey, this.slugKey, query.dc)(body))
);
},
- respondForCreateRecord: function(respond, data) {
+ respondForCreateRecord: function(respond, serialized, data) {
const slugKey = this.slugKey;
const primaryKey = this.primaryKey;
return respond((headers, body) => {
@@ -49,7 +49,7 @@ export default Serializer.extend({
return this.fingerprint(primaryKey, slugKey, data[DATACENTER_KEY])(body);
});
},
- respondForUpdateRecord: function(respond, data) {
+ respondForUpdateRecord: function(respond, serialized, data) {
const slugKey = this.slugKey;
const primaryKey = this.primaryKey;
return respond((headers, body) => {
@@ -60,7 +60,7 @@ export default Serializer.extend({
return this.fingerprint(primaryKey, slugKey, data[DATACENTER_KEY])(body);
});
},
- respondForDeleteRecord: function(respond, data) {
+ respondForDeleteRecord: function(respond, serialized, data) {
const slugKey = this.slugKey;
const primaryKey = this.primaryKey;
return respond((headers, body) => {
diff --git a/ui-v2/app/serializers/token.js b/ui-v2/app/serializers/token.js
index ef48aa072b76..026e92f80fda 100644
--- a/ui-v2/app/serializers/token.js
+++ b/ui-v2/app/serializers/token.js
@@ -10,21 +10,29 @@ export default Serializer.extend(WithPolicies, WithRoles, {
slugKey: SLUG_KEY,
attrs: ATTRS,
serialize: function(snapshot, options) {
- const data = this._super(...arguments);
- // TODO: Check this as it used to be only on update
- // Pretty sure Rules will only ever be on update as you can't
- // create legacy tokens here
- if (typeof data['Rules'] !== 'undefined') {
- data['ID'] = data['SecretID'];
- data['Name'] = data['Description'];
+ let data = this._super(...arguments);
+ // If a token has Rules, use the old API shape
+ // notice we use a null check here (not an undefined check)
+ // as we are dealing with the serialized model not raw user data
+ if (data['Rules'] !== null) {
+ data = {
+ ID: data.SecretID,
+ Name: data.Description,
+ Type: data.Type,
+ Rules: data.Rules,
+ };
}
// make sure we never send the SecretID
- if (data && typeof data['SecretID'] !== 'undefined') {
+ // TODO: If we selectively format the request payload in the adapter
+ // we won't have to do this here
+ // see side note in https://github.com/hashicorp/consul/pull/6285
+ // which will mean most if not all of this method can go
+ if (data) {
delete data['SecretID'];
}
return data;
},
- respondForUpdateRecord: function(respond, query) {
+ respondForUpdateRecord: function(respond, serialized, data) {
return this._super(
cb =>
respond((headers, body) => {
@@ -44,7 +52,8 @@ export default Serializer.extend(WithPolicies, WithRoles, {
}
return cb(headers, body);
}),
- query
+ serialized,
+ data
);
},
});
diff --git a/ui-v2/app/services/client/http.js b/ui-v2/app/services/client/http.js
index dc52c4e4f62c..51d316458e71 100644
--- a/ui-v2/app/services/client/http.js
+++ b/ui-v2/app/services/client/http.js
@@ -30,6 +30,11 @@ const dispose = function(request) {
}
return request;
};
+// TODO: Potentially url should check if any of the params
+// passed to it are undefined (null is fine). We could then get rid of the
+// multitude of checks we do throughout the adapters
+// right now createURL converts undefined to '' so we need to check thats not needed
+// anywhere (todo written here for visibility)
const url = createURL(encodeURIComponent);
export default Service.extend({
dom: service('dom'),
@@ -76,9 +81,10 @@ export default Service.extend({
url: function() {
return url(...arguments);
},
- request: function(cb, body) {
+ request: function(cb) {
const client = this;
return cb(function(strs, ...values) {
+ let body = {};
const doubleBreak = strs.reduce(function(prev, item, i) {
if (item.indexOf('\n\n') !== -1) {
return i;
@@ -87,11 +93,15 @@ export default Service.extend({
}, -1);
if (doubleBreak !== -1) {
body = values.splice(doubleBreak).reduce(function(prev, item) {
- return {
- ...prev,
- ...item,
- };
- }, body || {});
+ if (typeof item !== 'string') {
+ return {
+ ...prev,
+ ...item,
+ };
+ } else {
+ return item;
+ }
+ }, body);
}
let temp = url(strs, ...values).split(' ');
const method = temp.shift();
@@ -165,15 +175,21 @@ export default Service.extend({
// for write-like actions
// potentially we should change things so you _have_ to do that
// as doing it this way is a little magical
- if (method !== 'GET') {
- if (headers['Content-Type'].indexOf('json') !== -1) {
- options.data = JSON.stringify(body);
- } else {
- // TODO: Does this need urlencoding? Assuming jQuery does this
- options.data = body;
- }
+ if (method !== 'GET' && headers['Content-Type'].indexOf('json') !== -1) {
+ options.data = JSON.stringify(body);
+ } else {
+ // TODO: Does this need urlencoding? Assuming jQuery does this
+ options.data = body;
}
}
+ // temporarily reset the headers/content-type so it works the same
+ // as previously, should be able to remove this once the data layer
+ // rewrite is over and we can assert sending via form-encoded is fine
+ // also see adapters/kv content-types in requestForCreate/UpdateRecord
+ // also see https://github.com/hashicorp/consul/issues/3804
+ options.contentType = 'application/json; charset=utf-8';
+ headers['Content-Type'] = options.contentType;
+ //
options.beforeSend = function(xhr) {
if (headers) {
Object.keys(headers).forEach(key => xhr.setRequestHeader(key, headers[key]));
diff --git a/ui-v2/app/services/store.js b/ui-v2/app/services/store.js
index 081508e9a3ed..c4fa4becec01 100644
--- a/ui-v2/app/services/store.js
+++ b/ui-v2/app/services/store.js
@@ -21,7 +21,7 @@ export default Store.extend({
self: function(modelName, token) {
// TODO: no normalization, type it properly for the moment
const adapter = this.adapterFor(modelName);
- return adapter.self(this, { modelName: modelName }, token);
+ return adapter.self(this, { modelName: modelName }, token.secret, token);
},
queryLeader: function(modelName, query) {
// TODO: no normalization, type it properly for the moment
diff --git a/ui-v2/app/templates/dc/acls/tokens/edit.hbs b/ui-v2/app/templates/dc/acls/tokens/edit.hbs
index 8d40c1509c16..0d35fe34dabd 100644
--- a/ui-v2/app/templates/dc/acls/tokens/edit.hbs
+++ b/ui-v2/app/templates/dc/acls/tokens/edit.hbs
@@ -43,7 +43,7 @@
{{/confirmation-dialog}}
{{/if}}
{{#if (not (token/is-legacy item))}}
-
+
{{/if}}
{{/if}}
{{/block-slot}}
diff --git a/ui-v2/tests/acceptance/dc/acls/tokens/clone.feature b/ui-v2/tests/acceptance/dc/acls/tokens/clone.feature
new file mode 100644
index 000000000000..61d9bdf3785b
--- /dev/null
+++ b/ui-v2/tests/acceptance/dc/acls/tokens/clone.feature
@@ -0,0 +1,29 @@
+@setupApplicationTest
+Feature: dc / acls / tokens / clone: Cloning an ACL token
+ Background:
+ Given 1 datacenter model with the value "datacenter"
+ And 1 token model from yaml
+ ---
+ AccessorID: token
+ SecretID: ee52203d-989f-4f7a-ab5a-2bef004164ca
+ ---
+ Scenario: Cloning an ACL token from the listing page
+ When I visit the tokens page for yaml
+ ---
+ dc: datacenter
+ ---
+ And I click actions on the tokens
+ And I click clone on the tokens
+ Then a PUT request is made to "/v1/acl/token/token/clone?dc=datacenter"
+ Then "[data-notification]" has the "notification-clone" class
+ And "[data-notification]" has the "success" class
+ Scenario: Using an ACL token from the detail page
+ When I visit the token page for yaml
+ ---
+ dc: datacenter
+ token: token
+ ---
+ And I click clone
+ Then the url should be /datacenter/acls/tokens
+ Then "[data-notification]" has the "notification-clone" class
+ And "[data-notification]" has the "success" class
diff --git a/ui-v2/tests/acceptance/steps/dc/acls/tokens/clone-steps.js b/ui-v2/tests/acceptance/steps/dc/acls/tokens/clone-steps.js
new file mode 100644
index 000000000000..9bfbe9ac9b4f
--- /dev/null
+++ b/ui-v2/tests/acceptance/steps/dc/acls/tokens/clone-steps.js
@@ -0,0 +1,10 @@
+import steps from '../../../steps';
+
+// step definitions that are shared between features should be moved to the
+// tests/acceptance/steps/steps.js file
+
+export default function(assert) {
+ return steps(assert).then('I should find a file', function() {
+ assert.ok(true, this.step);
+ });
+}
diff --git a/ui-v2/tests/integration/adapters/acl-test.js b/ui-v2/tests/integration/adapters/acl-test.js
index 5b921124fa02..930463de3785 100644
--- a/ui-v2/tests/integration/adapters/acl-test.js
+++ b/ui-v2/tests/integration/adapters/acl-test.js
@@ -37,10 +37,14 @@ module('Integration | Adapter | acl', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/create?dc=${dc}`;
const actual = adapter
- .requestForCreateRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForCreateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -49,10 +53,14 @@ module('Integration | Adapter | acl', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/update?dc=${dc}`;
const actual = adapter
- .requestForUpdateRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForUpdateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -61,10 +69,14 @@ module('Integration | Adapter | acl', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/destroy/${id}?dc=${dc}`;
const actual = adapter
- .requestForDeleteRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForDeleteRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('/n')[0];
assert.equal(actual, expected);
});
@@ -73,10 +85,14 @@ module('Integration | Adapter | acl', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/clone/${id}?dc=${dc}`;
const actual = adapter
- .requestForCloneRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForCloneRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
diff --git a/ui-v2/tests/integration/adapters/intention-test.js b/ui-v2/tests/integration/adapters/intention-test.js
index 5d6df796216d..172a2dd5ca80 100644
--- a/ui-v2/tests/integration/adapters/intention-test.js
+++ b/ui-v2/tests/integration/adapters/intention-test.js
@@ -37,10 +37,14 @@ module('Integration | Adapter | intention', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `POST /v1/connect/intentions?dc=${dc}`;
const actual = adapter
- .requestForCreateRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForCreateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -49,10 +53,14 @@ module('Integration | Adapter | intention', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/connect/intentions/${id}?dc=${dc}`;
const actual = adapter
- .requestForUpdateRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForUpdateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -61,10 +69,14 @@ module('Integration | Adapter | intention', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `DELETE /v1/connect/intentions/${id}?dc=${dc}`;
const actual = adapter
- .requestForDeleteRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForDeleteRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
diff --git a/ui-v2/tests/integration/adapters/kv-test.js b/ui-v2/tests/integration/adapters/kv-test.js
index 5ccdf402477e..fdcfe1c8660a 100644
--- a/ui-v2/tests/integration/adapters/kv-test.js
+++ b/ui-v2/tests/integration/adapters/kv-test.js
@@ -47,11 +47,15 @@ module('Integration | Adapter | kv', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/kv/${id}?dc=${dc}`;
const actual = adapter
- .requestForCreateRecord(client.url, {
- Datacenter: dc,
- Key: id,
- Value: '',
- })
+ .requestForCreateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ Key: id,
+ Value: '',
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -60,11 +64,15 @@ module('Integration | Adapter | kv', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/kv/${id}?dc=${dc}`;
const actual = adapter
- .requestForUpdateRecord(client.url, {
- Datacenter: dc,
- Key: id,
- Value: '',
- })
+ .requestForUpdateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ Key: id,
+ Value: '',
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -72,10 +80,14 @@ module('Integration | Adapter | kv', function(hooks) {
const adapter = this.owner.lookup('adapter:kv');
const client = this.owner.lookup('service:client/http');
const expected = `DELETE /v1/kv/${id}?dc=${dc}`;
- const actual = adapter.requestForDeleteRecord(client.url, {
- Datacenter: dc,
- Key: id,
- });
+ const actual = adapter.requestForDeleteRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ Key: id,
+ }
+ );
assert.equal(actual, expected);
});
test('requestForDeleteRecord returns the correct url/method for folders', function(assert) {
@@ -83,10 +95,14 @@ module('Integration | Adapter | kv', function(hooks) {
const client = this.owner.lookup('service:client/http');
const folder = `${id}/`;
const expected = `DELETE /v1/kv/${folder}?dc=${dc}&recurse`;
- const actual = adapter.requestForDeleteRecord(client.url, {
- Datacenter: dc,
- Key: folder,
- });
+ const actual = adapter.requestForDeleteRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ Key: folder,
+ }
+ );
assert.equal(actual, expected);
});
});
diff --git a/ui-v2/tests/integration/adapters/policy-test.js b/ui-v2/tests/integration/adapters/policy-test.js
index 8b7e5f30616b..9077fc0c5eac 100644
--- a/ui-v2/tests/integration/adapters/policy-test.js
+++ b/ui-v2/tests/integration/adapters/policy-test.js
@@ -37,9 +37,13 @@ module('Integration | Adapter | policy', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/policy?dc=${dc}`;
const actual = adapter
- .requestForCreateRecord(client.url, {
- Datacenter: dc,
- })
+ .requestForCreateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -48,10 +52,14 @@ module('Integration | Adapter | policy', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/policy/${id}?dc=${dc}`;
const actual = adapter
- .requestForUpdateRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForUpdateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -60,10 +68,14 @@ module('Integration | Adapter | policy', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `DELETE /v1/acl/policy/${id}?dc=${dc}`;
const actual = adapter
- .requestForDeleteRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForDeleteRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
diff --git a/ui-v2/tests/integration/adapters/role-test.js b/ui-v2/tests/integration/adapters/role-test.js
index 4de929c577e5..d5a8b8bfdbe1 100644
--- a/ui-v2/tests/integration/adapters/role-test.js
+++ b/ui-v2/tests/integration/adapters/role-test.js
@@ -37,9 +37,13 @@ module('Integration | Adapter | role', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/role?dc=${dc}`;
const actual = adapter
- .requestForCreateRecord(client.url, {
- Datacenter: dc,
- })
+ .requestForCreateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -48,10 +52,14 @@ module('Integration | Adapter | role', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/role/${id}?dc=${dc}`;
const actual = adapter
- .requestForUpdateRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForUpdateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -60,10 +68,14 @@ module('Integration | Adapter | role', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `DELETE /v1/acl/role/${id}?dc=${dc}`;
const actual = adapter
- .requestForDeleteRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForDeleteRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
diff --git a/ui-v2/tests/integration/adapters/session-test.js b/ui-v2/tests/integration/adapters/session-test.js
index 10b71694c7d9..c8bc9614b359 100644
--- a/ui-v2/tests/integration/adapters/session-test.js
+++ b/ui-v2/tests/integration/adapters/session-test.js
@@ -48,10 +48,14 @@ module('Integration | Adapter | session', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/session/destroy/${id}?dc=${dc}`;
const actual = adapter
- .requestForDeleteRecord(client.url, {
- Datacenter: dc,
- ID: id,
- })
+ .requestForDeleteRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ ID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
diff --git a/ui-v2/tests/integration/adapters/token-test.js b/ui-v2/tests/integration/adapters/token-test.js
index c08ef0cc3220..a310e25e2750 100644
--- a/ui-v2/tests/integration/adapters/token-test.js
+++ b/ui-v2/tests/integration/adapters/token-test.js
@@ -57,9 +57,13 @@ module('Integration | Adapter | token', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/token?dc=${dc}`;
const actual = adapter
- .requestForCreateRecord(client.url, {
- Datacenter: dc,
- })
+ .requestForCreateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -68,10 +72,14 @@ module('Integration | Adapter | token', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/token/${id}?dc=${dc}`;
const actual = adapter
- .requestForUpdateRecord(client.url, {
- Datacenter: dc,
- AccessorID: id,
- })
+ .requestForUpdateRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ AccessorID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -80,11 +88,15 @@ module('Integration | Adapter | token', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/update?dc=${dc}`;
const actual = adapter
- .requestForUpdateRecord(client.url, {
- Rules: 'key {}',
- Datacenter: dc,
- AccessorID: id,
- })
+ .requestForUpdateRecord(
+ client.url,
+ {},
+ {
+ Rules: 'key {}',
+ Datacenter: dc,
+ AccessorID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
@@ -93,11 +105,64 @@ module('Integration | Adapter | token', function(hooks) {
const client = this.owner.lookup('service:client/http');
const expected = `DELETE /v1/acl/token/${id}?dc=${dc}`;
const actual = adapter
- .requestForDeleteRecord(client.url, {
- Datacenter: dc,
- AccessorID: id,
- })
+ .requestForDeleteRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ AccessorID: id,
+ }
+ )
.split('\n')[0];
assert.equal(actual, expected);
});
+ test('requestForCloneRecord returns the correct url', function(assert) {
+ const adapter = this.owner.lookup('adapter:token');
+ const client = this.owner.lookup('service:client/http');
+ const expected = `PUT /v1/acl/token/${id}/clone?dc=${dc}`;
+ const actual = adapter
+ .requestForCloneRecord(
+ client.url,
+ {},
+ {
+ Datacenter: dc,
+ AccessorID: id,
+ }
+ )
+ .split('\n')[0];
+ assert.equal(actual, expected);
+ });
+ test('requestForSelf returns the correct url', function(assert) {
+ const adapter = this.owner.lookup('adapter:token');
+ const client = this.owner.lookup('service:client/http');
+ const expected = `GET /v1/acl/token/self?dc=${dc}`;
+ const actual = adapter
+ .requestForSelf(
+ client.url,
+ {},
+ {
+ dc: dc,
+ }
+ )
+ .split('\n')[0];
+ assert.equal(actual, expected);
+ });
+ test('requestForSelf sets a token header using a secret', function(assert) {
+ const adapter = this.owner.lookup('adapter:token');
+ const client = this.owner.lookup('service:client/http');
+ const secret = 'sssh';
+ const expected = `X-Consul-Token: ${secret}`;
+ const actual = adapter
+ .requestForSelf(
+ client.url,
+ {},
+ {
+ dc: dc,
+ secret: secret,
+ }
+ )
+ .split('\n')[1]
+ .trim();
+ assert.equal(actual, expected);
+ });
});
diff --git a/ui-v2/tests/pages/dc/acls/tokens/edit.js b/ui-v2/tests/pages/dc/acls/tokens/edit.js
index 15a3e560d934..4a5c533e5fc1 100644
--- a/ui-v2/tests/pages/dc/acls/tokens/edit.js
+++ b/ui-v2/tests/pages/dc/acls/tokens/edit.js
@@ -14,6 +14,7 @@ export default function(
...deletable({}, 'form > div'),
use: clickable('[data-test-use]'),
confirmUse: clickable('button.type-delete'),
+ clone: clickable('[data-test-clone]'),
policies: policySelector(),
roles: roleSelector(),
};
diff --git a/ui-v2/tests/pages/dc/acls/tokens/index.js b/ui-v2/tests/pages/dc/acls/tokens/index.js
index 6cdf957d211e..a6ee639bbcdd 100644
--- a/ui-v2/tests/pages/dc/acls/tokens/index.js
+++ b/ui-v2/tests/pages/dc/acls/tokens/index.js
@@ -25,6 +25,7 @@ export default function(
actions: clickable('label'),
use: clickable('[data-test-use]'),
confirmUse: clickable('button.type-delete'),
+ clone: clickable('[data-test-clone]'),
})
),
filter: filter,
diff --git a/ui-v2/tests/unit/serializers/application-test.js b/ui-v2/tests/unit/serializers/application-test.js
index 51cebc93f927..88fc6d0f4b0c 100644
--- a/ui-v2/tests/unit/serializers/application-test.js
+++ b/ui-v2/tests/unit/serializers/application-test.js
@@ -37,7 +37,7 @@ module('Unit | Serializer | application', function(hooks) {
const expected = {
'primary-key-name': 'name',
};
- const actual = serializer.respondForDeleteRecord(respond, { Name: 'name', dc: 'dc-1' });
+ const actual = serializer.respondForDeleteRecord(respond, {}, { Name: 'name', dc: 'dc-1' });
assert.deepEqual(actual, expected);
// assert.ok(adapter.uidForURL.calledOnce);
});
diff --git a/ui-v2/tests/unit/serializers/kv-test.js b/ui-v2/tests/unit/serializers/kv-test.js
index a12dea80855d..b95b2da0bf6a 100644
--- a/ui-v2/tests/unit/serializers/kv-test.js
+++ b/ui-v2/tests/unit/serializers/kv-test.js
@@ -43,6 +43,7 @@ module('Unit | Serializer | kv', function(hooks) {
const body = true;
return cb(headers, body);
},
+ {},
{
Key: uid,
Datacenter: dc,
@@ -72,6 +73,7 @@ module('Unit | Serializer | kv', function(hooks) {
};
return cb(headers, body);
},
+ {},
{
Key: uid,
Datacenter: dc,