mirror of
https://github.com/nasa/openmct.git
synced 2024-12-23 15:02:23 +00:00
Merge pull request #1030 from nasa/api-tutorial/objects
Api tutorial/objects
This commit is contained in:
commit
d51e6bfd92
@ -19,6 +19,7 @@
|
|||||||
"comma-separated-values": "^3.6.4",
|
"comma-separated-values": "^3.6.4",
|
||||||
"FileSaver.js": "^0.0.2",
|
"FileSaver.js": "^0.0.2",
|
||||||
"zepto": "^1.1.6",
|
"zepto": "^1.1.6",
|
||||||
"eventemitter3": "^1.2.0"
|
"eventemitter3": "^1.2.0",
|
||||||
|
"lodash": "3.10.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,14 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
require(['main'], function (mct) {
|
require(['main'], function (mct) {
|
||||||
require([
|
require([
|
||||||
|
'./tutorials/grootprovider/groots',
|
||||||
'./tutorials/todo/todo',
|
'./tutorials/todo/todo',
|
||||||
'./tutorials/todo/bundle',
|
'./tutorials/todo/bundle',
|
||||||
'./example/imagery/bundle',
|
'./example/imagery/bundle',
|
||||||
'./example/eventGenerator/bundle',
|
'./example/eventGenerator/bundle',
|
||||||
'./example/generator/bundle'
|
'./example/generator/bundle',
|
||||||
], function (todoPlugin) {
|
], function (grootify, todoPlugin) {
|
||||||
|
grootify(mct);
|
||||||
todoPlugin(mct);
|
todoPlugin(mct);
|
||||||
mct.start();
|
mct.start();
|
||||||
})
|
})
|
||||||
|
3
main.js
3
main.js
@ -35,7 +35,8 @@ requirejs.config({
|
|||||||
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
||||||
"text": "bower_components/text/text",
|
"text": "bower_components/text/text",
|
||||||
"uuid": "bower_components/node-uuid/uuid",
|
"uuid": "bower_components/node-uuid/uuid",
|
||||||
"zepto": "bower_components/zepto/zepto.min"
|
"zepto": "bower_components/zepto/zepto.min",
|
||||||
|
"lodash": "bower_components/lodash/lodash"
|
||||||
},
|
},
|
||||||
"shim": {
|
"shim": {
|
||||||
"angular": {
|
"angular": {
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
define([
|
define([
|
||||||
'EventEmitter',
|
'EventEmitter',
|
||||||
'legacyRegistry',
|
'legacyRegistry',
|
||||||
'./api/api'
|
'./api/api',
|
||||||
], function (EventEmitter, legacyRegistry, api) {
|
'./api/objects/bundle'
|
||||||
|
], function (
|
||||||
|
EventEmitter,
|
||||||
|
legacyRegistry,
|
||||||
|
api
|
||||||
|
) {
|
||||||
function MCT() {
|
function MCT() {
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
this.legacyBundle = { extensions: {} };
|
this.legacyBundle = { extensions: {} };
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
define([
|
define([
|
||||||
'./Type'
|
'./Type',
|
||||||
|
'./objects/ObjectAPI'
|
||||||
], function (
|
], function (
|
||||||
Type
|
Type,
|
||||||
|
ObjectAPI
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
Type: Type
|
Type: Type,
|
||||||
|
Objects: ObjectAPI
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
70
src/api/objects/LegacyObjectAPIInterceptor.js
Normal file
70
src/api/objects/LegacyObjectAPIInterceptor.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
define([
|
||||||
|
'./object-utils',
|
||||||
|
'./ObjectAPI'
|
||||||
|
], function (
|
||||||
|
utils,
|
||||||
|
ObjectAPI
|
||||||
|
) {
|
||||||
|
function ObjectServiceProvider(objectService, instantiate) {
|
||||||
|
this.objectService = objectService;
|
||||||
|
this.instantiate = instantiate;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectServiceProvider.prototype.save = function (object) {
|
||||||
|
var key = object.key,
|
||||||
|
keyString = utils.makeKeyString(key),
|
||||||
|
newObject = this.instantiate(utils.toOldFormat(object), keyString);
|
||||||
|
|
||||||
|
return object.getCapability('persistence')
|
||||||
|
.persist()
|
||||||
|
.then(function () {
|
||||||
|
return utils.toNewFormat(object, key);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ObjectServiceProvider.prototype.delete = function (object) {
|
||||||
|
// TODO!
|
||||||
|
};
|
||||||
|
|
||||||
|
ObjectServiceProvider.prototype.get = function (key) {
|
||||||
|
var keyString = utils.makeKeyString(key);
|
||||||
|
return this.objectService.getObjects([keyString])
|
||||||
|
.then(function (results) {
|
||||||
|
var model = JSON.parse(JSON.stringify(results[keyString].getModel()));
|
||||||
|
return utils.toNewFormat(model, key);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Injects new object API as a decorator so that it hijacks all requests.
|
||||||
|
// Object providers implemented on new API should just work, old API should just work, many things may break.
|
||||||
|
function LegacyObjectAPIInterceptor(ROOTS, instantiate, objectService) {
|
||||||
|
this.getObjects = function (keys) {
|
||||||
|
var results = {},
|
||||||
|
promises = keys.map(function (keyString) {
|
||||||
|
var key = utils.parseKeyString(keyString);
|
||||||
|
return ObjectAPI.get(key)
|
||||||
|
.then(function (object) {
|
||||||
|
object = utils.toOldFormat(object)
|
||||||
|
results[keyString] = instantiate(object, keyString);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises)
|
||||||
|
.then(function () {
|
||||||
|
return results;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ObjectAPI._supersecretSetFallbackProvider(
|
||||||
|
new ObjectServiceProvider(objectService, instantiate)
|
||||||
|
);
|
||||||
|
|
||||||
|
ROOTS.forEach(function (r) {
|
||||||
|
ObjectAPI.addRoot(utils.parseKeyString(r.id));
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LegacyObjectAPIInterceptor;
|
||||||
|
});
|
92
src/api/objects/ObjectAPI.js
Normal file
92
src/api/objects/ObjectAPI.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
define([
|
||||||
|
'lodash',
|
||||||
|
'./object-utils'
|
||||||
|
], function (
|
||||||
|
_,
|
||||||
|
utils
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Object API. Intercepts the existing object API while also exposing
|
||||||
|
A new Object API.
|
||||||
|
|
||||||
|
MCT.objects.get('mine')
|
||||||
|
.then(function (root) {
|
||||||
|
console.log(root);
|
||||||
|
MCT.objects.getComposition(root)
|
||||||
|
.then(function (composition) {
|
||||||
|
console.log(composition)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Objects = {},
|
||||||
|
ROOT_REGISTRY = [],
|
||||||
|
PROVIDER_REGISTRY = {},
|
||||||
|
FALLBACK_PROVIDER;
|
||||||
|
|
||||||
|
Objects._supersecretSetFallbackProvider = function (p) {
|
||||||
|
FALLBACK_PROVIDER = p;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Root provider is hardcoded in; can't be skipped.
|
||||||
|
var RootProvider = {
|
||||||
|
'get': function () {
|
||||||
|
return Promise.resolve({
|
||||||
|
name: 'The root object',
|
||||||
|
type: 'root',
|
||||||
|
composition: ROOT_REGISTRY
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retrieve the provider for a given key.
|
||||||
|
function getProvider(key) {
|
||||||
|
if (key.identifier === 'ROOT') {
|
||||||
|
return RootProvider;
|
||||||
|
}
|
||||||
|
return PROVIDER_REGISTRY[key.namespace] || FALLBACK_PROVIDER;
|
||||||
|
};
|
||||||
|
|
||||||
|
Objects.addProvider = function (namespace, provider) {
|
||||||
|
PROVIDER_REGISTRY[namespace] = provider;
|
||||||
|
};
|
||||||
|
|
||||||
|
[
|
||||||
|
'save',
|
||||||
|
'delete',
|
||||||
|
'get'
|
||||||
|
].forEach(function (method) {
|
||||||
|
Objects[method] = function () {
|
||||||
|
var key = arguments[0],
|
||||||
|
provider = getProvider(key);
|
||||||
|
|
||||||
|
if (!provider) {
|
||||||
|
throw new Error('No Provider Matched');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!provider[method]) {
|
||||||
|
throw new Error('Provider does not support [' + method + '].');
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider[method].apply(provider, arguments);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
Objects.addRoot = function (key) {
|
||||||
|
ROOT_REGISTRY.unshift(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
Objects.removeRoot = function (key) {
|
||||||
|
ROOT_REGISTRY = ROOT_REGISTRY.filter(function (k) {
|
||||||
|
return (
|
||||||
|
k.identifier !== key.identifier ||
|
||||||
|
k.namespace !== key.namespace
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return Objects;
|
||||||
|
});
|
100
src/api/objects/README.md
Normal file
100
src/api/objects/README.md
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# Object API - Overview
|
||||||
|
|
||||||
|
The object API provides methods for fetching domain objects.
|
||||||
|
|
||||||
|
# Keys
|
||||||
|
Keys are a composite identifier that is used to create and persist objects. Ex:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
namespace: 'elastic',
|
||||||
|
identifier: 'myIdentifier'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In old MCT days, we called this an "id", and we encoded it in a single string.
|
||||||
|
The above key would encode into the identifier, `elastic:myIdentifier`.
|
||||||
|
|
||||||
|
When interacting with the API you will be dealing with key objects.
|
||||||
|
|
||||||
|
# Configuring the Object API
|
||||||
|
|
||||||
|
The following methods should be used before calling run. They allow you to
|
||||||
|
configure the persistence space of MCT.
|
||||||
|
|
||||||
|
* `MCT.objects.addRoot(key)` -- add a "ROOT" to Open MCT by specifying it's
|
||||||
|
key.
|
||||||
|
* `MCT.objects.removeRoot(key)` -- Remove a "ROOT" from Open MCT by key.
|
||||||
|
* `MCT.objects.addProvider(namespace, provider)` -- register an object provider
|
||||||
|
for a specific namespace. See below for documentation on the provider
|
||||||
|
interface.
|
||||||
|
|
||||||
|
# Using the object API
|
||||||
|
|
||||||
|
The object API provides methods for getting, saving, and deleting objects.
|
||||||
|
|
||||||
|
* MCT.objects.get(key) -> returns promise for an object
|
||||||
|
* MCT.objects.save(object) -> returns promise that is resolved when object
|
||||||
|
has been saved
|
||||||
|
* MCT.objects.delete(object) -> returns promise that is resolved when object has
|
||||||
|
been deleted
|
||||||
|
|
||||||
|
## Configuration Example: Adding a groot
|
||||||
|
|
||||||
|
The following example adds a new root object for groot and populates it with
|
||||||
|
some pieces of groot.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
|
||||||
|
var ROOT_KEY = {
|
||||||
|
namespace: 'groot',
|
||||||
|
identifier: 'groot'
|
||||||
|
};
|
||||||
|
|
||||||
|
var GROOT_ROOT = {
|
||||||
|
name: 'I am groot',
|
||||||
|
type: 'folder',
|
||||||
|
composition: [
|
||||||
|
{
|
||||||
|
namespace: 'groot',
|
||||||
|
identifier: 'arms'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: 'groot',
|
||||||
|
identifier: 'legs'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: 'groot',
|
||||||
|
identifier: 'torso'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var GrootProvider = {
|
||||||
|
get: function (key) {
|
||||||
|
if (key.identifier === 'groot') {
|
||||||
|
return Promise.resolve(GROOT_ROOT);
|
||||||
|
}
|
||||||
|
return Promise.resolve({
|
||||||
|
name: 'Groot\'s ' + key.identifier
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mct.Objects.addRoot(ROOT_KEY);
|
||||||
|
|
||||||
|
mct.Objects.addProvider('groot', GrootProvider);
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Making a custom provider:
|
||||||
|
|
||||||
|
All methods on the provider interface are optional, so you do not need
|
||||||
|
to modify them.
|
||||||
|
|
||||||
|
* `provider.get(key)` -> promise for a domain object.
|
||||||
|
* `provider.save(domainObject)` -> returns promise that is fulfilled when object
|
||||||
|
has been saved.
|
||||||
|
* `provider.delete(domainObject)` -> returns promise that is fulfilled when
|
||||||
|
object has been deleted.
|
||||||
|
|
||||||
|
|
49
src/api/objects/bundle.js
Normal file
49
src/api/objects/bundle.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
/*global define*/
|
||||||
|
|
||||||
|
define([
|
||||||
|
'./LegacyObjectAPIInterceptor',
|
||||||
|
'legacyRegistry'
|
||||||
|
], function (
|
||||||
|
LegacyObjectAPIInterceptor,
|
||||||
|
legacyRegistry
|
||||||
|
) {
|
||||||
|
legacyRegistry.register('src/api/objects', {
|
||||||
|
name: 'Object API',
|
||||||
|
description: 'The public Objects API',
|
||||||
|
extensions: {
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
provides: "objectService",
|
||||||
|
type: "decorator",
|
||||||
|
priority: "mandatory",
|
||||||
|
implementation: LegacyObjectAPIInterceptor,
|
||||||
|
depends: [
|
||||||
|
"roots[]",
|
||||||
|
"instantiate"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
67
src/api/objects/object-utils.js
Normal file
67
src/api/objects/object-utils.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
define([
|
||||||
|
|
||||||
|
], function (
|
||||||
|
|
||||||
|
) {
|
||||||
|
|
||||||
|
// take a key string and turn it into a key object
|
||||||
|
// 'scratch:root' ==> {namespace: 'scratch', identifier: 'root'}
|
||||||
|
var parseKeyString = function (key) {
|
||||||
|
if (typeof key === 'object') {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
var namespace = '',
|
||||||
|
identifier = key;
|
||||||
|
for (var i = 0, escaped = false, len=key.length; i < len; i++) {
|
||||||
|
if (key[i] === ":" && !escaped) {
|
||||||
|
namespace = key.slice(0, i);
|
||||||
|
identifier = key.slice(i + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
namespace: namespace,
|
||||||
|
identifier: identifier
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// take a key and turn it into a key string
|
||||||
|
// {namespace: 'scratch', identifier: 'root'} ==> 'scratch:root'
|
||||||
|
var makeKeyString = function (key) {
|
||||||
|
if (typeof key === 'string') {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
if (!key.namespace) {
|
||||||
|
return key.identifier;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
key.namespace.replace(':', '\\:'),
|
||||||
|
key.identifier.replace(':', '\\:')
|
||||||
|
].join(':');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts composition to use key strings instead of keys
|
||||||
|
var toOldFormat = function (model) {
|
||||||
|
delete model.key;
|
||||||
|
if (model.composition) {
|
||||||
|
model.composition = model.composition.map(makeKeyString);
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
// converts composition to use keys instead of key strings
|
||||||
|
var toNewFormat = function (model, key) {
|
||||||
|
model.key = key;
|
||||||
|
if (model.composition) {
|
||||||
|
model.composition = model.composition.map(parseKeyString);
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
toOldFormat: toOldFormat,
|
||||||
|
toNewFormat: toNewFormat,
|
||||||
|
makeKeyString: makeKeyString,
|
||||||
|
parseKeyString: parseKeyString
|
||||||
|
};
|
||||||
|
});
|
44
tutorials/grootprovider/groots.js
Normal file
44
tutorials/grootprovider/groots.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
define(function () {
|
||||||
|
return function grootPlugin(mct) {
|
||||||
|
var ROOT_KEY = {
|
||||||
|
namespace: 'groot',
|
||||||
|
identifier: 'groot'
|
||||||
|
};
|
||||||
|
|
||||||
|
var GROOT_ROOT = {
|
||||||
|
name: 'I am groot',
|
||||||
|
type: 'folder',
|
||||||
|
composition: [
|
||||||
|
{
|
||||||
|
namespace: 'groot',
|
||||||
|
identifier: 'arms'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: 'groot',
|
||||||
|
identifier: 'legs'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: 'groot',
|
||||||
|
identifier: 'torso'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var GrootProvider = {
|
||||||
|
get: function (key) {
|
||||||
|
if (key.identifier === 'groot') {
|
||||||
|
return Promise.resolve(GROOT_ROOT);
|
||||||
|
}
|
||||||
|
return Promise.resolve({
|
||||||
|
name: 'Groot\'s ' + key.identifier
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mct.Objects.addRoot(ROOT_KEY);
|
||||||
|
|
||||||
|
mct.Objects.addProvider('groot', GrootProvider);
|
||||||
|
|
||||||
|
return mct;
|
||||||
|
};
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user