mirror of
https://github.com/nasa/openmct.git
synced 2025-06-04 08:30:48 +00:00
[Persistence] Copy CouchDB adapter
Copy CouchDB adapter to use as a basis for an adapter to ElasticSearch, WTD-1033.
This commit is contained in:
parent
6721bca32a
commit
37310443e6
2
platform/persistence/elastic/README.md
Normal file
2
platform/persistence/elastic/README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
This bundle implements a connection to an external ElasticSearch persistence
|
||||||
|
store in Open MCT Web.
|
39
platform/persistence/elastic/bundle.json
Normal file
39
platform/persistence/elastic/bundle.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "Couch Persistence",
|
||||||
|
"description": "Adapter to read and write objects using a CouchDB instance.",
|
||||||
|
"extensions": {
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"provides": "persistenceService",
|
||||||
|
"type": "provider",
|
||||||
|
"implementation": "CouchPersistenceProvider.js",
|
||||||
|
"depends": [ "$http", "$q", "PERSISTENCE_SPACE", "COUCHDB_PATH" ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"constants": [
|
||||||
|
{
|
||||||
|
"key": "PERSISTENCE_SPACE",
|
||||||
|
"value": "mct"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "COUCHDB_PATH",
|
||||||
|
"value": "/couch/openmct"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "COUCHDB_INDICATOR_INTERVAL",
|
||||||
|
"value": 15000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indicators": [
|
||||||
|
{
|
||||||
|
"implementation": "CouchIndicator.js",
|
||||||
|
"depends": [
|
||||||
|
"$http",
|
||||||
|
"$interval",
|
||||||
|
"COUCHDB_PATH",
|
||||||
|
"COUCHDB_INDICATOR_INTERVAL"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
41
platform/persistence/elastic/src/CouchDocument.js
Normal file
41
platform/persistence/elastic/src/CouchDocument.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*global define*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CouchDocument describes domain object model in a format
|
||||||
|
* which is easily read-written to CouchDB. This includes
|
||||||
|
* Couch's _id and _rev fields, as well as a sseparate
|
||||||
|
* metadata field which contains a subset of information found
|
||||||
|
* in the model itself (to support search optimization with
|
||||||
|
* CouchDB views.)
|
||||||
|
* @constructor
|
||||||
|
* @param {string} id the id under which to store this mode
|
||||||
|
* @param {object} model the model to store
|
||||||
|
* @param {string} rev the revision to include (or undefined,
|
||||||
|
* if no revision should be noted for couch)
|
||||||
|
* @param {boolean} whether or not to mark this documnet as
|
||||||
|
* deleted (see CouchDB docs for _deleted)
|
||||||
|
*/
|
||||||
|
function CouchDocument(id, model, rev, markDeleted) {
|
||||||
|
return {
|
||||||
|
"_id": id,
|
||||||
|
"_rev": rev,
|
||||||
|
"_deleted": markDeleted,
|
||||||
|
"metadata": {
|
||||||
|
"category": "domain object",
|
||||||
|
"type": model.type,
|
||||||
|
"owner": "admin",
|
||||||
|
"name": model.name,
|
||||||
|
"created": Date.now()
|
||||||
|
},
|
||||||
|
"model": model
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return CouchDocument;
|
||||||
|
}
|
||||||
|
);
|
103
platform/persistence/elastic/src/ElasticIndicator.js
Normal file
103
platform/persistence/elastic/src/ElasticIndicator.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*global define*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Set of connection states; changing among these states will be
|
||||||
|
// reflected in the indicator's appearance.
|
||||||
|
// CONNECTED: Everything nominal, expect to be able to read/write.
|
||||||
|
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
|
||||||
|
// SEMICONNECTED: Connected to the database, but it reported an error.
|
||||||
|
// PENDING: Still trying to connect, and haven't failed yet.
|
||||||
|
var CONNECTED = {
|
||||||
|
text: "Connected",
|
||||||
|
glyphClass: "ok",
|
||||||
|
description: "Connected to the domain object database."
|
||||||
|
},
|
||||||
|
DISCONNECTED = {
|
||||||
|
text: "Disconnected",
|
||||||
|
glyphClass: "err",
|
||||||
|
description: "Unable to connect to the domain object database."
|
||||||
|
},
|
||||||
|
SEMICONNECTED = {
|
||||||
|
text: "Unavailable",
|
||||||
|
glyphClass: "caution",
|
||||||
|
description: "Database does not exist or is unavailable."
|
||||||
|
},
|
||||||
|
PENDING = {
|
||||||
|
text: "Checking connection..."
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicator for the current CouchDB connection. Polls CouchDB
|
||||||
|
* at a regular interval (defined by bundle constants) to ensure
|
||||||
|
* that the database is available.
|
||||||
|
*/
|
||||||
|
function CouchIndicator($http, $interval, PATH, INTERVAL) {
|
||||||
|
// Track the current connection state
|
||||||
|
var state = PENDING;
|
||||||
|
|
||||||
|
// Callback if the HTTP request to Couch fails
|
||||||
|
function handleError(err) {
|
||||||
|
state = DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback if the HTTP request succeeds. CouchDB may
|
||||||
|
// report an error, so check for that.
|
||||||
|
function handleResponse(response) {
|
||||||
|
var data = response.data;
|
||||||
|
state = data.error ? SEMICONNECTED : CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to connect to CouchDB, and update the indicator.
|
||||||
|
function updateIndicator() {
|
||||||
|
$http.get(PATH).then(handleResponse, handleError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the indicator initially, and start polling.
|
||||||
|
updateIndicator();
|
||||||
|
$interval(updateIndicator, INTERVAL);
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Get the glyph (single character used as an icon)
|
||||||
|
* to display in this indicator. This will return "D",
|
||||||
|
* which should appear as a database icon.
|
||||||
|
* @returns {string} the character of the database icon
|
||||||
|
*/
|
||||||
|
getGlyph: function () {
|
||||||
|
return "D";
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get the name of the CSS class to apply to the glyph.
|
||||||
|
* This is used to color the glyph to match its
|
||||||
|
* state (one of ok, caution or err)
|
||||||
|
* @returns {string} the CSS class to apply to this glyph
|
||||||
|
*/
|
||||||
|
getGlyphClass: function () {
|
||||||
|
return state.glyphClass;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get the text that should appear in the indicator.
|
||||||
|
* @returns {string} brief summary of connection status
|
||||||
|
*/
|
||||||
|
getText: function () {
|
||||||
|
return state.text;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get a longer-form description of the current connection
|
||||||
|
* space, suitable for display in a tooltip
|
||||||
|
* @returns {string} longer summary of connection status
|
||||||
|
*/
|
||||||
|
getDescription: function () {
|
||||||
|
return state.description;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return CouchIndicator;
|
||||||
|
}
|
||||||
|
);
|
162
platform/persistence/elastic/src/ElasticPersistenceProvider.js
Normal file
162
platform/persistence/elastic/src/ElasticPersistenceProvider.js
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/*global define*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["./CouchDocument"],
|
||||||
|
function (CouchDocument) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// JSLint doesn't like dangling _'s, but CouchDB uses these, so
|
||||||
|
// hide this behind variables.
|
||||||
|
var REV = "_rev",
|
||||||
|
ID = "_id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CouchPersistenceProvider reads and writes JSON documents
|
||||||
|
* (more specifically, domain object models) to/from a CouchDB
|
||||||
|
* instance.
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function ElasticPersistenceProvider($http, $q, SPACE, PATH) {
|
||||||
|
var spaces = [ SPACE ],
|
||||||
|
revs = {};
|
||||||
|
|
||||||
|
// Convert a subpath to a full path, suitable to pass
|
||||||
|
// to $http.
|
||||||
|
function url(subpath) {
|
||||||
|
return PATH + '/' + subpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue a request using $http; get back the plain JS object
|
||||||
|
// from the expected JSON response
|
||||||
|
function request(subpath, method, value) {
|
||||||
|
return $http({
|
||||||
|
method: method,
|
||||||
|
url: url(subpath),
|
||||||
|
data: value
|
||||||
|
}).then(function (response) {
|
||||||
|
return response.data;
|
||||||
|
}, function () {
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorthand methods for GET/PUT methods
|
||||||
|
function get(subpath) {
|
||||||
|
return request(subpath, "GET");
|
||||||
|
}
|
||||||
|
function put(subpath, value) {
|
||||||
|
return request(subpath, "PUT", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull out a list of document IDs from CouchDB's
|
||||||
|
// _all_docs response
|
||||||
|
function getIdsFromAllDocs(allDocs) {
|
||||||
|
return allDocs.rows.map(function (r) { return r.id; });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a domain object model out of CouchDB's response
|
||||||
|
function getModel(response) {
|
||||||
|
if (response && response.model) {
|
||||||
|
revs[response[ID]] = response[REV];
|
||||||
|
return response.model;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the response to a create/update/delete request;
|
||||||
|
// track the rev if it's valid, otherwise return false to
|
||||||
|
// indicate that the request failed.
|
||||||
|
function checkResponse(response) {
|
||||||
|
if (response && response.ok) {
|
||||||
|
revs[response.id] = response.rev;
|
||||||
|
return response.ok;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* List all persistence spaces which this provider
|
||||||
|
* recognizes.
|
||||||
|
*
|
||||||
|
* @returns {Promise.<string[]>} a promise for a list of
|
||||||
|
* spaces supported by this provider
|
||||||
|
*/
|
||||||
|
listSpaces: function () {
|
||||||
|
return $q.when(spaces);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* List all objects (by their identifiers) that are stored
|
||||||
|
* in the given persistence space, per this provider.
|
||||||
|
* @param {string} space the space to check
|
||||||
|
* @returns {Promise.<string[]>} a promise for the list of
|
||||||
|
* identifiers
|
||||||
|
*/
|
||||||
|
listObjects: function (space) {
|
||||||
|
return get("_all_docs").then(getIdsFromAllDocs);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Create a new object in the specified persistence space.
|
||||||
|
* @param {string} space the space in which to store the object
|
||||||
|
* @param {string} key the identifier for the persisted object
|
||||||
|
* @param {object} value a JSONifiable object that should be
|
||||||
|
* stored and associated with the provided identifier
|
||||||
|
* @returns {Promise.<boolean>} a promise for an indication
|
||||||
|
* of the success (true) or failure (false) of this
|
||||||
|
* operation
|
||||||
|
*/
|
||||||
|
createObject: function (space, key, value) {
|
||||||
|
return put(key, new CouchDocument(key, value))
|
||||||
|
.then(checkResponse);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an existing object back from persistence.
|
||||||
|
* @param {string} space the space in which to look for
|
||||||
|
* the object
|
||||||
|
* @param {string} key the identifier for the persisted object
|
||||||
|
* @returns {Promise.<object>} a promise for the stored
|
||||||
|
* object; this will resolve to undefined if no such
|
||||||
|
* object is found.
|
||||||
|
*/
|
||||||
|
readObject: function (space, key) {
|
||||||
|
return get(key).then(getModel);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Update an existing object in the specified persistence space.
|
||||||
|
* @param {string} space the space in which to store the object
|
||||||
|
* @param {string} key the identifier for the persisted object
|
||||||
|
* @param {object} value a JSONifiable object that should be
|
||||||
|
* stored and associated with the provided identifier
|
||||||
|
* @returns {Promise.<boolean>} a promise for an indication
|
||||||
|
* of the success (true) or failure (false) of this
|
||||||
|
* operation
|
||||||
|
*/
|
||||||
|
updateObject: function (space, key, value) {
|
||||||
|
return put(key, new CouchDocument(key, value, revs[key]))
|
||||||
|
.then(checkResponse);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Delete an object in the specified persistence space.
|
||||||
|
* @param {string} space the space from which to delete this
|
||||||
|
* object
|
||||||
|
* @param {string} key the identifier of the persisted object
|
||||||
|
* @param {object} value a JSONifiable object that should be
|
||||||
|
* deleted
|
||||||
|
* @returns {Promise.<boolean>} a promise for an indication
|
||||||
|
* of the success (true) or failure (false) of this
|
||||||
|
* operation
|
||||||
|
*/
|
||||||
|
deleteObject: function (space, key, value) {
|
||||||
|
return put(key, new CouchDocument(key, value, revs[key], true))
|
||||||
|
.then(checkResponse);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ElasticPersistenceProvider;
|
||||||
|
}
|
||||||
|
);
|
44
platform/persistence/elastic/test/CouchDocumentSpec.js
Normal file
44
platform/persistence/elastic/test/CouchDocumentSpec.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DomainObjectProviderSpec. Created by vwoeltje on 11/6/14.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
["../src/CouchDocument"],
|
||||||
|
function (CouchDocument) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// JSLint doesn't like dangling _'s, but CouchDB uses these, so
|
||||||
|
// hide this behind variables.
|
||||||
|
var REV = "_rev",
|
||||||
|
ID = "_id",
|
||||||
|
DELETED = "_deleted";
|
||||||
|
|
||||||
|
describe("A couch document", function () {
|
||||||
|
it("includes an id", function () {
|
||||||
|
expect(new CouchDocument("testId", {})[ID])
|
||||||
|
.toEqual("testId");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("includes a rev only when one is provided", function () {
|
||||||
|
expect(new CouchDocument("testId", {})[REV])
|
||||||
|
.not.toBeDefined();
|
||||||
|
expect(new CouchDocument("testId", {}, "testRev")[REV])
|
||||||
|
.toEqual("testRev");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("includes the provided model", function () {
|
||||||
|
var model = { someKey: "some value" };
|
||||||
|
expect(new CouchDocument("testId", model).model)
|
||||||
|
.toEqual(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("marks documents as deleted only on request", function () {
|
||||||
|
expect(new CouchDocument("testId", {}, "testRev")[DELETED])
|
||||||
|
.not.toBeDefined();
|
||||||
|
expect(new CouchDocument("testId", {}, "testRev", true)[DELETED])
|
||||||
|
.toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
111
platform/persistence/elastic/test/CouchIndicatorSpec.js
Normal file
111
platform/persistence/elastic/test/CouchIndicatorSpec.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../src/CouchIndicator"],
|
||||||
|
function (CouchIndicator) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("The CouchDB status indicator", function () {
|
||||||
|
var mockHttp,
|
||||||
|
mockInterval,
|
||||||
|
testPath,
|
||||||
|
testInterval,
|
||||||
|
mockPromise,
|
||||||
|
indicator;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockHttp = jasmine.createSpyObj("$http", [ "get" ]);
|
||||||
|
mockInterval = jasmine.createSpy("$interval");
|
||||||
|
mockPromise = jasmine.createSpyObj("promise", [ "then" ]);
|
||||||
|
testPath = "/test/path";
|
||||||
|
testInterval = 12321; // Some number
|
||||||
|
|
||||||
|
mockHttp.get.andReturn(mockPromise);
|
||||||
|
|
||||||
|
indicator = new CouchIndicator(
|
||||||
|
mockHttp,
|
||||||
|
mockInterval,
|
||||||
|
testPath,
|
||||||
|
testInterval
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("polls for changes", function () {
|
||||||
|
expect(mockInterval).toHaveBeenCalledWith(
|
||||||
|
jasmine.any(Function),
|
||||||
|
testInterval
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has a database icon", function () {
|
||||||
|
expect(indicator.getGlyph()).toEqual("D");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("consults the database at the configured path", function () {
|
||||||
|
expect(mockHttp.get).toHaveBeenCalledWith(testPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("changes when the database connection is nominal", function () {
|
||||||
|
var initialText = indicator.getText(),
|
||||||
|
initialDescrption = indicator.getDescription(),
|
||||||
|
initialGlyphClass = indicator.getGlyphClass();
|
||||||
|
|
||||||
|
// Nominal just means getting back an objeect, without
|
||||||
|
// an error field.
|
||||||
|
mockPromise.then.mostRecentCall.args[0]({ data: {} });
|
||||||
|
|
||||||
|
// Verify that these values changed;
|
||||||
|
// don't test for specific text.
|
||||||
|
expect(indicator.getText()).not.toEqual(initialText);
|
||||||
|
expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
|
||||||
|
expect(indicator.getDescription()).not.toEqual(initialDescrption);
|
||||||
|
|
||||||
|
// Do check for specific class
|
||||||
|
expect(indicator.getGlyphClass()).toEqual("ok");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("changes when the server reports an error", function () {
|
||||||
|
var initialText = indicator.getText(),
|
||||||
|
initialDescrption = indicator.getDescription(),
|
||||||
|
initialGlyphClass = indicator.getGlyphClass();
|
||||||
|
|
||||||
|
// Nominal just means getting back an objeect, with
|
||||||
|
// an error field.
|
||||||
|
mockPromise.then.mostRecentCall.args[0](
|
||||||
|
{ data: { error: "Uh oh." } }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify that these values changed;
|
||||||
|
// don't test for specific text.
|
||||||
|
expect(indicator.getText()).not.toEqual(initialText);
|
||||||
|
expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
|
||||||
|
expect(indicator.getDescription()).not.toEqual(initialDescrption);
|
||||||
|
|
||||||
|
// Do check for specific class
|
||||||
|
expect(indicator.getGlyphClass()).toEqual("caution");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it("changes when the server cannot be reached", function () {
|
||||||
|
var initialText = indicator.getText(),
|
||||||
|
initialDescrption = indicator.getDescription(),
|
||||||
|
initialGlyphClass = indicator.getGlyphClass();
|
||||||
|
|
||||||
|
// Nominal just means getting back an objeect, without
|
||||||
|
// an error field.
|
||||||
|
mockPromise.then.mostRecentCall.args[1]({ data: {} });
|
||||||
|
|
||||||
|
// Verify that these values changed;
|
||||||
|
// don't test for specific text.
|
||||||
|
expect(indicator.getText()).not.toEqual(initialText);
|
||||||
|
expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
|
||||||
|
expect(indicator.getDescription()).not.toEqual(initialDescrption);
|
||||||
|
|
||||||
|
// Do check for specific class
|
||||||
|
expect(indicator.getGlyphClass()).toEqual("err");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -0,0 +1,171 @@
|
|||||||
|
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DomainObjectProviderSpec. Created by vwoeltje on 11/6/14.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
["../src/CouchPersistenceProvider"],
|
||||||
|
function (CouchPersistenceProvider) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("The couch persistence provider", function () {
|
||||||
|
var mockHttp,
|
||||||
|
mockQ,
|
||||||
|
testSpace = "testSpace",
|
||||||
|
testPath = "/test/db",
|
||||||
|
capture,
|
||||||
|
provider;
|
||||||
|
|
||||||
|
function mockPromise(value) {
|
||||||
|
return {
|
||||||
|
then: function (callback) {
|
||||||
|
return mockPromise(callback(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockHttp = jasmine.createSpy("$http");
|
||||||
|
mockQ = jasmine.createSpyObj("$q", ["when"]);
|
||||||
|
|
||||||
|
mockQ.when.andCallFake(mockPromise);
|
||||||
|
|
||||||
|
// Capture promise results
|
||||||
|
capture = jasmine.createSpy("capture");
|
||||||
|
|
||||||
|
provider = new CouchPersistenceProvider(
|
||||||
|
mockHttp,
|
||||||
|
mockQ,
|
||||||
|
testSpace,
|
||||||
|
testPath
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reports available spaces", function () {
|
||||||
|
provider.listSpaces().then(capture);
|
||||||
|
expect(capture).toHaveBeenCalledWith([testSpace]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// General pattern of tests below is to simulate CouchDB's
|
||||||
|
// response, verify that request looks like what CouchDB
|
||||||
|
// would expect, and finally verify that CouchPersistenceProvider's
|
||||||
|
// return values match what is expected.
|
||||||
|
it("lists all available documents", function () {
|
||||||
|
mockHttp.andReturn(mockPromise({
|
||||||
|
data: { rows: [ { id: "a" }, { id: "b" }, { id: "c" } ] }
|
||||||
|
}));
|
||||||
|
provider.listObjects().then(capture);
|
||||||
|
expect(mockHttp).toHaveBeenCalledWith({
|
||||||
|
url: "/test/db/_all_docs", // couch document listing
|
||||||
|
method: "GET"
|
||||||
|
});
|
||||||
|
expect(capture).toHaveBeenCalledWith(["a", "b", "c"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows object creation", function () {
|
||||||
|
var model = { someKey: "some value" };
|
||||||
|
mockHttp.andReturn(mockPromise({
|
||||||
|
data: { "_id": "abc", "_rev": "xyz", "ok": true }
|
||||||
|
}));
|
||||||
|
provider.createObject("testSpace", "abc", model).then(capture);
|
||||||
|
expect(mockHttp).toHaveBeenCalledWith({
|
||||||
|
url: "/test/db/abc",
|
||||||
|
method: "PUT",
|
||||||
|
data: {
|
||||||
|
"_id": "abc",
|
||||||
|
metadata: jasmine.any(Object),
|
||||||
|
model: model
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(capture).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows object models to be read back", function () {
|
||||||
|
var model = { someKey: "some value" };
|
||||||
|
mockHttp.andReturn(mockPromise({
|
||||||
|
data: { "_id": "abc", "_rev": "xyz", "model": model }
|
||||||
|
}));
|
||||||
|
provider.readObject("testSpace", "abc").then(capture);
|
||||||
|
expect(mockHttp).toHaveBeenCalledWith({
|
||||||
|
url: "/test/db/abc",
|
||||||
|
method: "GET"
|
||||||
|
});
|
||||||
|
expect(capture).toHaveBeenCalledWith(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows object update", function () {
|
||||||
|
var model = { someKey: "some value" };
|
||||||
|
|
||||||
|
// First do a read to populate rev tags...
|
||||||
|
mockHttp.andReturn(mockPromise({
|
||||||
|
data: { "_id": "abc", "_rev": "xyz", "model": {} }
|
||||||
|
}));
|
||||||
|
provider.readObject("testSpace", "abc");
|
||||||
|
|
||||||
|
// Now perform an update
|
||||||
|
mockHttp.andReturn(mockPromise({
|
||||||
|
data: { "_id": "abc", "_rev": "uvw", "ok": true }
|
||||||
|
}));
|
||||||
|
provider.updateObject("testSpace", "abc", model).then(capture);
|
||||||
|
expect(mockHttp).toHaveBeenCalledWith({
|
||||||
|
url: "/test/db/abc",
|
||||||
|
method: "PUT",
|
||||||
|
data: {
|
||||||
|
"_id": "abc",
|
||||||
|
"_rev": "xyz",
|
||||||
|
metadata: jasmine.any(Object),
|
||||||
|
model: model
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(capture).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows object deletion", function () {
|
||||||
|
// First do a read to populate rev tags...
|
||||||
|
mockHttp.andReturn(mockPromise({
|
||||||
|
data: { "_id": "abc", "_rev": "xyz", "model": {} }
|
||||||
|
}));
|
||||||
|
provider.readObject("testSpace", "abc");
|
||||||
|
|
||||||
|
// Now perform an update
|
||||||
|
mockHttp.andReturn(mockPromise({
|
||||||
|
data: { "_id": "abc", "_rev": "uvw", "ok": true }
|
||||||
|
}));
|
||||||
|
provider.deleteObject("testSpace", "abc", {}).then(capture);
|
||||||
|
expect(mockHttp).toHaveBeenCalledWith({
|
||||||
|
url: "/test/db/abc",
|
||||||
|
method: "PUT",
|
||||||
|
data: {
|
||||||
|
"_id": "abc",
|
||||||
|
"_rev": "xyz",
|
||||||
|
"_deleted": true,
|
||||||
|
metadata: jasmine.any(Object),
|
||||||
|
model: {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(capture).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reports failure to create objects", function () {
|
||||||
|
var model = { someKey: "some value" };
|
||||||
|
mockHttp.andReturn(mockPromise({
|
||||||
|
data: { "_id": "abc", "_rev": "xyz", "ok": false }
|
||||||
|
}));
|
||||||
|
provider.createObject("testSpace", "abc", model).then(capture);
|
||||||
|
expect(capture).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns undefined when objects are not found", function () {
|
||||||
|
// Act like a 404
|
||||||
|
mockHttp.andReturn({
|
||||||
|
then: function (success, fail) {
|
||||||
|
return mockPromise(fail());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
provider.readObject("testSpace", "abc").then(capture);
|
||||||
|
expect(capture).toHaveBeenCalledWith(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
5
platform/persistence/elastic/test/suite.json
Normal file
5
platform/persistence/elastic/test/suite.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
"CouchDocument",
|
||||||
|
"CouchIndicator",
|
||||||
|
"CouchPersistenceProvider"
|
||||||
|
]
|
Loading…
x
Reference in New Issue
Block a user