eeb04ae436
* Add Partition to all our models * Add partitions into our serializers/fingerprinting * Make some amends to a few adapters ready for partitions * Amend blueprints to avoid linting error * Update all our repositories to include partitions, also Remove enabled/disable nspace repo and just use a nspace with conditionals * Ensure nspace and parition parameters always return '' no matter what * Ensure data-sink finds the model properly This will later be replaced by a @dataSink decorator but we are find kicking that can down the road a little more * Add all the new partition data layer * Add a way to set the title of the page from inside the route and make it accessibile via a route announcer * Make the Consul Route the default/basic one * Tweak nspace and partition abilities not to check the length * Thread partition through all the components that need it * Some ACL tweaks * Move the entire app to use partitions * Delete all the tests we no longer need * Update some Unit tests to use partition * Fix up KV title tests * Fix up a few more acceptance tests * Fixup and temporarily ignore some acceptance tests * Stop using ember-cli-page-objects fillable as it doesn't seem to work * Fix lint error * Remove old ACL related test * Add a tick after filling out forms * Fix token warning modal * Found some more places where we need a partition var * Fixup some more acceptance tests * Tokens still needs a repo service for CRUD * Remove acceptance tests we no longer need * Fixup and "FIXME ignore" a few tests * Remove an s * Disable blocking queries for KV to revert to previous release for now * Fixup adapter tests to follow async/function resolving interface * Fixup all the serializer integration tests * Fixup service/repo integration tests * Fixup deleting acceptance test * Fixup some ent tests * Make sure nspaces passes the dc through for when thats important * ...aaaand acceptance nspaces with the extra dc param
214 lines
6.7 KiB
JavaScript
214 lines
6.7 KiB
JavaScript
import Serializer from './http';
|
|
import { set } from '@ember/object';
|
|
|
|
import {
|
|
HEADERS_SYMBOL as HTTP_HEADERS_SYMBOL,
|
|
HEADERS_INDEX as HTTP_HEADERS_INDEX,
|
|
HEADERS_DATACENTER as HTTP_HEADERS_DATACENTER,
|
|
HEADERS_NAMESPACE as HTTP_HEADERS_NAMESPACE,
|
|
HEADERS_PARTITION as HTTP_HEADERS_PARTITION,
|
|
} from 'consul-ui/utils/http/consul';
|
|
import { CACHE_CONTROL as HTTP_HEADERS_CACHE_CONTROL } from 'consul-ui/utils/http/headers';
|
|
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
|
|
import { NSPACE_KEY } from 'consul-ui/models/nspace';
|
|
import { PARTITION_KEY } from 'consul-ui/models/partition';
|
|
import createFingerprinter from 'consul-ui/utils/create-fingerprinter';
|
|
|
|
const map = function(obj, cb) {
|
|
if (!Array.isArray(obj)) {
|
|
return [obj].map(cb)[0];
|
|
}
|
|
return obj.map(cb);
|
|
};
|
|
|
|
const attachHeaders = function(headers, body, query = {}) {
|
|
// lowercase everything incase we get browser inconsistencies
|
|
const lower = {};
|
|
Object.keys(headers).forEach(function(key) {
|
|
lower[key.toLowerCase()] = headers[key];
|
|
});
|
|
//
|
|
body[HTTP_HEADERS_SYMBOL] = lower;
|
|
return body;
|
|
};
|
|
export default class ApplicationSerializer extends Serializer {
|
|
attachHeaders = attachHeaders;
|
|
fingerprint = createFingerprinter(DATACENTER_KEY, NSPACE_KEY, PARTITION_KEY);
|
|
|
|
respondForQuery(respond, query) {
|
|
return respond((headers, body) =>
|
|
attachHeaders(
|
|
headers,
|
|
map(
|
|
body,
|
|
this.fingerprint(
|
|
this.primaryKey,
|
|
this.slugKey,
|
|
query.dc,
|
|
headers[HTTP_HEADERS_NAMESPACE],
|
|
headers[HTTP_HEADERS_PARTITION]
|
|
)
|
|
),
|
|
query
|
|
)
|
|
);
|
|
}
|
|
|
|
respondForQueryRecord(respond, query) {
|
|
return respond((headers, body) =>
|
|
attachHeaders(
|
|
headers,
|
|
this.fingerprint(
|
|
this.primaryKey,
|
|
this.slugKey,
|
|
query.dc,
|
|
headers[HTTP_HEADERS_NAMESPACE],
|
|
headers[HTTP_HEADERS_PARTITION]
|
|
)(body),
|
|
query
|
|
)
|
|
);
|
|
}
|
|
|
|
respondForCreateRecord(respond, serialized, data) {
|
|
const slugKey = this.slugKey;
|
|
const primaryKey = this.primaryKey;
|
|
|
|
return respond((headers, body) => {
|
|
// If creates are true use the info we already have
|
|
if (body === true) {
|
|
body = data;
|
|
}
|
|
// Creates need a primaryKey adding
|
|
return this.fingerprint(
|
|
primaryKey,
|
|
slugKey,
|
|
data[DATACENTER_KEY],
|
|
headers[HTTP_HEADERS_NAMESPACE],
|
|
data.Partition
|
|
)(body);
|
|
});
|
|
}
|
|
|
|
respondForUpdateRecord(respond, serialized, data) {
|
|
const slugKey = this.slugKey;
|
|
const primaryKey = this.primaryKey;
|
|
|
|
return respond((headers, body) => {
|
|
// If updates are true use the info we already have
|
|
// TODO: We may aswell avoid re-fingerprinting here if we are just going
|
|
// to reuse data then its already fingerprinted and as the response is
|
|
// true we don't have anything changed so the old fingerprint stays the
|
|
// same as long as nothing in the fingerprint has been edited (the
|
|
// namespace?)
|
|
if (body === true) {
|
|
body = data;
|
|
}
|
|
return this.fingerprint(
|
|
primaryKey,
|
|
slugKey,
|
|
data[DATACENTER_KEY],
|
|
headers[HTTP_HEADERS_NAMESPACE],
|
|
headers[HTTP_HEADERS_PARTITION]
|
|
)(body);
|
|
});
|
|
}
|
|
|
|
respondForDeleteRecord(respond, serialized, data) {
|
|
const slugKey = this.slugKey;
|
|
const primaryKey = this.primaryKey;
|
|
|
|
return respond((headers, body) => {
|
|
// Deletes only need the primaryKey/uid returning and they need the slug
|
|
// key AND potential namespace in order to create the correct
|
|
// uid/fingerprint
|
|
return {
|
|
[primaryKey]: this.fingerprint(
|
|
primaryKey,
|
|
slugKey,
|
|
data[DATACENTER_KEY],
|
|
headers[HTTP_HEADERS_NAMESPACE],
|
|
headers[HTTP_HEADERS_PARTITION]
|
|
)({
|
|
[slugKey]: data[slugKey],
|
|
[NSPACE_KEY]: data[NSPACE_KEY],
|
|
[PARTITION_KEY]: data[PARTITION_KEY],
|
|
})[primaryKey],
|
|
};
|
|
});
|
|
}
|
|
|
|
// this could get confusing if you tried to override say
|
|
// `normalizeQueryResponse`
|
|
// TODO: consider creating a method for each one of the
|
|
// `normalize...Response` family
|
|
normalizeResponse(store, modelClass, payload, id, requestType) {
|
|
const normalizedPayload = this.normalizePayload(payload, id, requestType);
|
|
// put the meta onto the response, here this is ok as JSON-API allows this
|
|
// and our specific data is now in response[primaryModelClass.modelName]
|
|
// so we aren't in danger of overwriting anything (which was the reason
|
|
// for the Symbol-like property earlier) use a method modelled on
|
|
// ember-data methods so we have the opportunity to do this on a per-model
|
|
// level
|
|
const meta = this.normalizeMeta(store, modelClass, normalizedPayload, id, requestType);
|
|
if (requestType !== 'query') {
|
|
normalizedPayload.meta = meta;
|
|
}
|
|
const res = super.normalizeResponse(
|
|
store,
|
|
modelClass,
|
|
{
|
|
meta: meta,
|
|
[modelClass.modelName]: normalizedPayload,
|
|
},
|
|
id,
|
|
requestType
|
|
);
|
|
// If the result of the super normalizeResponse is undefined its because
|
|
// the JSONSerializer (which REST inherits from) doesn't recognise the
|
|
// requestType, in this case its likely to be an 'action' request rather
|
|
// than a specific 'load me some data' one. Therefore its ok to bypass the
|
|
// store here for the moment we currently use this for self, but it also
|
|
// would affect any custom methods that use a serializer in our custom
|
|
// service/store
|
|
if (typeof res === 'undefined') {
|
|
return payload;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
timestamp() {
|
|
return new Date().getTime();
|
|
}
|
|
|
|
normalizeMeta(store, modelClass, payload, id, requestType) {
|
|
// Pick the meta/headers back off the payload and cleanup
|
|
const headers = payload[HTTP_HEADERS_SYMBOL] || {};
|
|
delete payload[HTTP_HEADERS_SYMBOL];
|
|
const meta = {
|
|
cacheControl: headers[HTTP_HEADERS_CACHE_CONTROL.toLowerCase()],
|
|
cursor: headers[HTTP_HEADERS_INDEX.toLowerCase()],
|
|
dc: headers[HTTP_HEADERS_DATACENTER.toLowerCase()],
|
|
nspace: headers[HTTP_HEADERS_NAMESPACE.toLowerCase()],
|
|
partition: headers[HTTP_HEADERS_PARTITION.toLowerCase()],
|
|
};
|
|
if (typeof headers['x-range'] !== 'undefined') {
|
|
meta.range = headers['x-range'];
|
|
}
|
|
if (typeof headers['refresh'] !== 'undefined') {
|
|
meta.interval = headers['refresh'] * 1000;
|
|
}
|
|
if (requestType === 'query') {
|
|
meta.date = this.timestamp();
|
|
payload.forEach(function(item) {
|
|
set(item, 'SyncTime', meta.date);
|
|
});
|
|
}
|
|
return meta;
|
|
}
|
|
|
|
normalizePayload(payload, id, requestType) {
|
|
return payload;
|
|
}
|
|
}
|