Compare commits

..

40 Commits

Author SHA1 Message Date
Pete Richards
45d4e96049 these are andrews time conductor fixes 2017-02-23 09:54:20 -08:00
Andrew Henry
c7cffdeb3b Merge pull request #1453 from nasa/restore-search-navigation
[Search] Allow navigation from results
2017-02-22 13:21:03 -08:00
Pete Richards
d45ae7908d [Search] Allow navigation from results
Fix search item so that navigation occurs after clicking on
a search result, while still properly preventing navigation
when required.

Related to #1366
2017-02-22 13:08:54 -08:00
Andrew Henry
c74fdb816f Merge pull request #1450 from nasa/fix-table-tests
Add hasCapability to mock
2017-02-21 18:14:18 -08:00
Pete Richards
40985a56c8 Add hasCapability to mock 2017-02-21 18:01:43 -08:00
Pete Richards
5c01f0be24 Merge branch 'open1077' into 1435-integration 2017-02-21 17:22:36 -08:00
Andrew Henry
f7ff5af60b Merge pull request #1448 from nasa/update-composition-policy
Update composition policy
2017-02-21 17:18:59 -08:00
Andrew Henry
2088fc52f3 Merge pull request #1446 from nasa/fix-orphan-navigation
Orphan check uses capability not model
2017-02-21 17:08:40 -08:00
Andrew Henry
db33ab143e Merge pull request #1445 from nasa/api-updates
Api updates
2017-02-21 17:04:45 -08:00
Pete Richards
8c77d4006a Pass options to support checks 2017-02-21 17:03:16 -08:00
Pete Richards
2fa567b98b [Table] Track by index, save the elements 2017-02-21 16:49:39 -08:00
Andrew Henry
e61f04663a Merge pull request #1444 from nasa/consistent-css-class
cssclass is now cssClass
2017-02-21 16:36:30 -08:00
Andrew Henry
4b905fa7d2 Merge pull request #1443 from nasa/remove-old-bundle-loading
Stop loading bundles.json
2017-02-21 16:32:26 -08:00
Andrew Henry
5e6e7f018a Merge pull request #1442 from nasa/dev-skip-optimize
[Build] Skip optimize in dev environment
2017-02-21 16:26:41 -08:00
Andrew Henry
53f56b430a Merge pull request #1449 from nasa/identifier-not-key
[API] Use proper key format
2017-02-21 16:24:17 -08:00
Pete Richards
50c934820c Merge pull request #1424 from nasa/open1382
[Tables] Do not persist column configuration for non-editable objects
2017-02-21 15:18:56 -08:00
Pete Richards
2a10a2cae2 Update specs to match composition policies 2017-02-21 15:14:35 -08:00
Pete Richards
65325b90fd Composition policy takes child instance
The composition policy now takes a child instance instead
of the child type, as in all cases we have access to the child
object.

This allows new-style objects to be contained by old-style objects.

Updated all composition policies to use standardized argument names
instead of `context` and `candidate`; this makes it easier to
understand.

Updated AddActionProvider to hardcode the object types supported.
2017-02-21 12:32:49 -08:00
Pete Richards
cfecc36ae6 Orphan check uses capability not model
Switch orphan checking to use capability instead of model.

This ensures that new-style composition providers work as intended.
2017-02-21 12:10:17 -08:00
Pete Richards
d9f8622459 [Telemetry] Update TelemetryProvider API
Based on feedback from tutorial sprint, update provider API,
formatter API, and legacy adapter code.

Providers can now implement separate checks for providing realtime
and historical data, and providers are not registered with a specific
strategy.  Strategy is instead intended to be an attribute in the
request options.

Removed unused code in the telemetry API and simplify limitEvaluators.
2017-02-21 11:51:32 -08:00
Pete Richards
8e13819e1e [API] composition providers receive new-style objects
Ensure that composition providers get new-style objects (with id
included) so that they can properly check for applicability.
2017-02-21 11:21:08 -08:00
Pete Richards
aaedf5d576 cssclass is now cssClass
Make property name consistent with standard camelCase naming.
2017-02-21 11:14:46 -08:00
Pete Richards
af9ffaf02d Stop loading bundles.json
Stop application from requesting bundles.json at first load.  This was
confusing to some external developers who would see an error in the
log and not know the cause.
2017-02-21 11:11:16 -08:00
Pete Richards
970acbd56e [Build] Skip optimize in dev environment
Skip optimize in dev environment to speed up project rebuilds.  Very helpful
when integration testing openmct.js build artifact.
2017-02-21 11:05:20 -08:00
Andrew Henry
46c7399867 Merge pull request #1410 from joshbaldwin/master
adding MCT name to README
2017-02-21 09:09:51 -08:00
Andrew Henry
9a6745635d Merge pull request #1431 from dhrubomoy/master
[Documentation] Fixed filename
2017-02-15 10:18:46 -08:00
Henry
34dc457aff [Tables] Restored telemetry datum field 'name'. Fixed bug with default sort not working 2017-02-10 15:39:39 -08:00
Henry
a3311e4c57 [Tables] Tests and style fixes 2017-02-10 14:22:30 -08:00
Henry
ef8efbd53d [Tables] Default UTC time system if available and none others defined 2017-02-10 14:22:28 -08:00
Henry
6cd99efbb9 [Tables] Added telemetry buffer so that subscription data is not discarded if it's beyond the end bounds 2017-02-10 14:22:28 -08:00
Henry
ae2b73a4f5 [Tables] Increase default table size 2017-02-10 14:22:28 -08:00
Henry
0c3ff82cfe [Table] Added ticking to combined historical/real-time table
Don't add duplicate telemetry data
2017-02-10 14:22:20 -08:00
Henry
50f303bbdc [Tables] limit digests to increase performance 2017-02-10 14:22:20 -08:00
Henry
2a4944d6ee [Tables] Refactoring for consolidation of historical and real-time tables
Added batch processing of large historical queries. #1077
2017-02-10 14:21:48 -08:00
Henry
3544caf4be [API] Observer path was accessing object key incorrectly 2017-02-10 14:21:47 -08:00
Henry
976333d7f7 [Tables] Support for subscriptions from new Telemetry API
Historical and real-time data flowing

Added formatting, and limits. Support telemetry objects themselves and not just composition of telemetry objects

Apply default time range if none supplied (15 minutes)
2017-02-10 14:21:26 -08:00
Henry
6d5530ba9c [Tables] Using new composition API to fetch all telemetry objects 2017-02-10 14:12:10 -08:00
Dhrubomoy Das Gupta
d3b4ad41c2 [Documentation] Fixed filename
Fixed file name "Platform.md" to "platform.md". "Platform.md" was giving a 404 error when clicked, in github and in the official site as well.
2017-02-09 20:54:52 -05:00
Henry
3d3baddd23 [Tables] Do not persist column configuration for non-editable objects 2017-02-02 15:44:36 -08:00
Josh Baldwin
9f9d28deef adding MCT name to README 2017-01-21 11:27:20 -05:00
43 changed files with 431 additions and 458 deletions

View File

@@ -1,6 +1,6 @@
# Open MCT [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
Open MCT is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
Please visit our [Official Site](https://nasa.github.io/openmct/) and [Getting Started Guide](https://nasa.github.io/openmct/getting-started/)

View File

@@ -131,7 +131,7 @@ Keeping that in mind, there are a few useful patterns supported by the
framework that are useful to keep in mind.
The specific service infrastructure provided by the platform is described
in the [Platform Architecture](Platform.md).
in the [Platform Architecture](platform.md).
## Extension Categories

View File

@@ -2261,10 +2261,7 @@ The platform understands the following policy categories (specifiable as the
* `action`: Determines whether or not a given action is allowable. The candidate
argument here is an Action; the context is its action context object.
* `composition`: Determines whether or not domain objects of a given type are
allowed to contain domain objects of another type. The candidate argument here
is the container's `Type`; the context argument is the `Type` of the object to be
contained.
* `composition`: Determines whether or not domain objects of a given type (first argument, `parentType`) can contain a given object (second argument, `child`).
* `view`: Determines whether or not a view is applicable for a domain object.
The candidate argument is the view's extension definition; the context argument
is the `DomainObject` to be viewed.

184
example/generator/bundle.js Normal file
View File

@@ -0,0 +1,184 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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([
"./src/SinewaveTelemetryProvider",
"./src/SinewaveLimitCapability",
"./src/SinewaveDeltaFormat",
'legacyRegistry'
], function (
SinewaveTelemetryProvider,
SinewaveLimitCapability,
SinewaveDeltaFormat,
legacyRegistry
) {
"use strict";
legacyRegistry.register("example/generator", {
"name": "Sine Wave Generator",
"description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
"extensions": {
"components": [
{
"implementation": SinewaveTelemetryProvider,
"type": "provider",
"provides": "telemetryService",
"depends": [
"$q",
"$timeout"
]
}
],
"capabilities": [
{
"key": "limit",
"implementation": SinewaveLimitCapability
}
],
"formats": [
{
"key": "example.delta",
"implementation": SinewaveDeltaFormat
}
],
"constants": [
{
"key": "TIME_CONDUCTOR_DOMAINS",
"value": [
{
"key": "time",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta"
}
],
"priority": -1
}
],
"types": [
{
"key": "generator",
"name": "Sine Wave Generator",
"cssClass": "icon-telemetry",
"description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
"priority": 10,
"features": "creation",
"model": {
"telemetry": {
"period": 10,
"amplitude": 1,
"offset": 0,
"dataRateInHz": 1
}
},
"telemetry": {
"source": "generator",
"domains": [
{
"key": "utc",
"name": "Time",
"format": "utc"
},
{
"key": "yesterday",
"name": "Yesterday",
"format": "utc"
},
{
"key": "delta",
"name": "Delta",
"format": "example.delta"
}
],
"ranges": [
{
"key": "sin",
"name": "Sine"
},
{
"key": "cos",
"name": "Cosine"
}
]
},
"properties": [
{
"name": "Period",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "period",
"required": true,
"property": [
"telemetry",
"period"
],
"pattern": "^\\d*(\\.\\d*)?$"
},
{
"name": "Amplitude",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "amplitude",
"required": true,
"property": [
"telemetry",
"amplitude"
],
"pattern": "^\\d*(\\.\\d*)?$"
},
{
"name": "Offset",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "offset",
"required": true,
"property": [
"telemetry",
"offset"
],
"pattern": "^\\d*(\\.\\d*)?$"
},
{
"name": "Data Rate (hz)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "dataRateInHz",
"required": true,
"property": [
"telemetry",
"dataRateInHz"
],
"pattern": "^\\d*(\\.\\d*)?$"
}
]
}
]
}
});
});

View File

@@ -41,10 +41,6 @@ define([
return domainObject.type === 'generator';
};
GeneratorProvider.prototype.supportsRequest =
GeneratorProvider.prototype.supportsSubscribe =
GeneratorProvider.prototype.canProvideTelemetry;
GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
var props = [
'amplitude',

View File

@@ -28,10 +28,11 @@
<script src="bower_components/requirejs/require.js">
</script>
<script>
require(['openmct'], function (openmct, generatorPlugin) {
require(['openmct'], function (openmct) {
[
'example/imagery',
'example/eventGenerator'
'example/eventGenerator',
'example/generator'
].forEach(
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
@@ -39,12 +40,11 @@
openmct.install(openmct.plugins.localStorage);
openmct.install(openmct.plugins.espresso);
openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.Conductor({
defaultTimeSystem: 'utc',
defaultTimespan: 30 * 60 * 1000,
showConductor: true
showConductor: false
}));
openmct.start();

View File

@@ -48,7 +48,7 @@ define([], function () {
parent.useCapability('composition')
.then(function (composees) {
var isOrphan = composees.every(function (c) {
return c.getId() !== domainObject.getId()
return c.getId() !== domainObject.getId();
});
if (isOrphan) {
parent.getCapability('action').perform('navigate');

View File

@@ -33,7 +33,7 @@ define([
mockContext,
mockActionCapability,
mockEditor,
testParentModel,
testParentComposition,
testId,
mockThrottledFns;
@@ -41,7 +41,6 @@ define([
testId = 'some-identifier';
mockThrottledFns = [];
testParentModel = {};
mockTopic = jasmine.createSpy('topic');
mockThrottle = jasmine.createSpy('throttle');
@@ -55,14 +54,12 @@ define([
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'getModel',
'hasCapability'
]);
mockParentObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'getModel',
'hasCapability'
'useCapability'
]);
mockContext = jasmine.createSpyObj('context', ['getParent']);
mockActionCapability = jasmine.createSpyObj('action', ['perform']);
@@ -75,9 +72,7 @@ define([
mockThrottledFns.push(mockThrottledFn);
return mockThrottledFn;
});
mockTopic.andCallFake(function (k) {
return k === 'mutation' && mockMutationTopic;
});
mockTopic.andReturn(mockMutationTopic);
mockDomainObject.getId.andReturn(testId);
mockDomainObject.getCapability.andCallFake(function (c) {
return {
@@ -88,12 +83,13 @@ define([
mockDomainObject.hasCapability.andCallFake(function (c) {
return !!mockDomainObject.getCapability(c);
});
mockParentObject.getModel.andReturn(testParentModel);
mockParentObject.getCapability.andCallFake(function (c) {
return {
action: mockActionCapability
}[c];
});
testParentComposition = [];
mockParentObject.useCapability.andReturn(Promise.resolve(testParentComposition));
mockContext.getParent.andReturn(mockParentObject);
mockNavigationService.getNavigation.andReturn(mockDomainObject);
mockEditor.isEditContextRoot.andReturn(false);
@@ -126,7 +122,9 @@ define([
var prefix = isOrphan ? "" : "non-";
describe("for " + prefix + "orphan objects", function () {
beforeEach(function () {
testParentModel.composition = isOrphan ? [] : [testId];
if (!isOrphan) {
testParentComposition.push(mockDomainObject);
}
});
[false, true].forEach(function (isEditRoot) {
@@ -136,13 +134,31 @@ define([
function itNavigatesAsExpected() {
if (isOrphan && !isEditRoot) {
it("navigates to the parent", function () {
expect(mockActionCapability.perform)
.toHaveBeenCalledWith('navigate');
var done = false;
waitsFor(function () {
return done;
});
setTimeout(function () {
done = true;
}, 5);
runs(function () {
expect(mockActionCapability.perform)
.toHaveBeenCalledWith('navigate');
});
});
} else {
it("does nothing", function () {
expect(mockActionCapability.perform)
.not.toHaveBeenCalled();
var done = false;
waitsFor(function () {
return done;
});
setTimeout(function () {
done = true;
}, 5);
runs(function () {
expect(mockActionCapability.perform)
.not.toHaveBeenCalled();
});
});
}
}
@@ -157,7 +173,6 @@ define([
mockNavigationService.addListener.mostRecentCall
.args[0](mockDomainObject);
});
itNavigatesAsExpected();
});

View File

@@ -54,8 +54,7 @@ define(
AddActionProvider.prototype.getActions = function (actionContext) {
var context = actionContext || {},
key = context.key,
destination = context.domainObject,
self = this;
destination = context.domainObject;
// We only provide Add actions, and we need a
// domain object to serve as the container for the
@@ -66,16 +65,16 @@ define(
}
// Introduce one create action per type
['timeline', 'activity'].map(function (type) {
return ['timeline', 'activity'].map(function (type) {
return new AddAction(
type,
this.typeService.getType(type),
destination,
context,
self.$q,
self.dialogService,
self.policyService
this.$q,
this.dialogService,
this.policyService
);
});
}, this);
};
return AddActionProvider;

View File

@@ -59,12 +59,12 @@ define(
domainObject = this.domainObject,
policyService = this.policyService;
function validateLocation(locatingObject) {
var locatingType = locatingObject &&
locatingObject.getCapability('type');
return locatingType && policyService.allow(
function validateLocation(parent) {
var parentType = parent &&
parent.getCapability('type');
return parentType && policyService.allow(
"composition",
locatingType,
parentType,
domainObject
);
}
@@ -118,7 +118,7 @@ define(
formModel = this.createModel(formValue);
formModel.location = parent.getId();
this.domainObject.useCapability("mutation", function (model) {
this.domainObject.useCapability("mutation", function () {
return formModel;
});
return this.domainObject;

View File

@@ -31,9 +31,7 @@ define(
var mockTypeService,
mockDialogService,
mockPolicyService,
mockCreationPolicy,
mockCompositionPolicy,
mockPolicyMap = {},
mockTypeMap,
mockTypes,
mockDomainObject,
mockQ,
@@ -55,49 +53,33 @@ define(
);
mockType.hasFeature.andReturn(true);
mockType.getName.andReturn(name);
mockType.getKey.andReturn(name);
return mockType;
}
beforeEach(function () {
mockTypeService = jasmine.createSpyObj(
"typeService",
["listTypes"]
);
mockDialogService = jasmine.createSpyObj(
"dialogService",
["getUserInput"]
);
mockPolicyService = jasmine.createSpyObj(
"policyService",
["allow"]
["getType"]
);
mockDialogService = {};
mockPolicyService = {};
mockDomainObject = {};
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getCapability"]
);
//Mocking getCapability because AddActionProvider uses the
// type capability of the destination object.
mockDomainObject.getCapability.andReturn({});
mockTypes = ["A", "B", "C"].map(createMockType);
mockTypes = [
"timeline",
"activity",
"other"
].map(createMockType);
mockTypeMap = {};
mockTypes.forEach(function (type) {
mockPolicyMap[type.getName()] = true;
mockTypeMap[type.getKey()] = type;
});
mockCreationPolicy = function (type) {
return mockPolicyMap[type.getName()];
};
mockCompositionPolicy = function () {
return true;
};
mockPolicyService.allow.andReturn(true);
mockTypeService.listTypes.andReturn(mockTypes);
mockTypeService.getType.andCallFake(function (key) {
return mockTypeMap[key];
});
provider = new AddActionProvider(
mockQ,
@@ -107,29 +89,16 @@ define(
);
});
it("checks for creatability", function () {
provider.getActions({
it("provides actions for timeline and activity", function () {
var actions = provider.getActions({
key: "add",
domainObject: mockDomainObject
});
expect(actions.length).toBe(2);
expect(actions[0].metadata.type).toBe('timeline');
expect(actions[1].metadata.type).toBe('activity');
// Make sure it was creation which was used to check
expect(mockPolicyService.allow)
.toHaveBeenCalledWith("creation", mockTypes[0]);
});
it("checks for composability of type", function () {
provider.getActions({
key: "add",
domainObject: mockDomainObject
});
expect(mockPolicyService.allow).toHaveBeenCalledWith(
"composition",
jasmine.any(Object),
jasmine.any(Object)
);
expect(mockDomainObject.getCapability).toHaveBeenCalledWith('type');
});
});
}

View File

@@ -175,7 +175,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith(
'composition',
mockOtherType,
mockType
mockDomainObject
);
});

View File

@@ -38,22 +38,20 @@ define(
function CompositionPolicy() {
}
CompositionPolicy.prototype.allow = function (candidate, context) {
var type = context.getCapability('type');
var typeKey = type.getKey();
var candidateDef = candidate.getDefinition();
// A candidate without containment rules can contain anything.
if (!candidateDef.contains) {
CompositionPolicy.prototype.allow = function (parentType, child) {
var parentDef = parentType.getDefinition();
// A parent without containment rules can contain anything.
if (!parentDef.contains) {
return true;
}
// If any containment rule matches context type, the candidate
// can contain this type.
return candidateDef.contains.some(function (c) {
return parentDef.contains.some(function (c) {
// Simple containment rules are supported typeKeys.
if (typeof c === 'string') {
return c === typeKey;
return c === child.getCapability('type').getKey();
}
// More complicated rules require context to have all specified
// capabilities.
@@ -61,7 +59,7 @@ define(
c.has = [c.has];
}
return c.has.every(function (capability) {
return context.hasCapability(capability);
return child.hasCapability(capability);
});
});
};

View File

@@ -79,7 +79,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith(
'composition',
mockTypes[0],
mockTypes[1]
mockDomainObjects[1]
);
});

View File

@@ -24,60 +24,94 @@ define(
["../src/CompositionPolicy"],
function (CompositionPolicy) {
describe("Composition policy", function () {
var mockInjector,
mockTypeService,
mockCapabilityService,
mockTypes,
var typeA,
typeB,
typeC,
mockChildObject,
policy;
beforeEach(function () {
mockInjector = jasmine.createSpyObj('$injector', ['get']);
mockTypeService = jasmine.createSpyObj(
'typeService',
['listTypes']
typeA = jasmine.createSpyObj(
'type A-- the particular kind',
['getKey', 'getDefinition']
);
mockCapabilityService = jasmine.createSpyObj(
'capabilityService',
['getCapabilities']
);
// Both types can only contain b, let's say
mockTypes = ['a', 'b'].map(function (type) {
var mockType = jasmine.createSpyObj(
'type-' + type,
['getKey', 'getDefinition', 'getInitialModel']
);
mockType.getKey.andReturn(type);
mockType.getDefinition.andReturn({
contains: ['b']
});
mockType.getInitialModel.andReturn({});
return mockType;
typeA.getKey.andReturn('a');
typeA.getDefinition.andReturn({
contains: ['a']
});
mockInjector.get.andCallFake(function (name) {
return {
typeService: mockTypeService,
capabilityService: mockCapabilityService
}[name];
typeB = jasmine.createSpyObj(
'type B-- anything goes',
['getKey', 'getDefinition']
);
typeB.getKey.andReturn('b');
typeB.getDefinition.andReturn({
contains: ['a', 'b']
});
mockTypeService.listTypes.andReturn(mockTypes);
mockCapabilityService.getCapabilities.andReturn({});
typeC = jasmine.createSpyObj(
'type C-- distinguishing and interested in telemetry',
['getKey', 'getDefinition']
);
typeC.getKey.andReturn('c');
typeC.getDefinition.andReturn({
contains: [{has: 'telemetry'}]
});
policy = new CompositionPolicy(mockInjector);
mockChildObject = jasmine.createSpyObj(
'childObject',
['getCapability', 'hasCapability']
);
policy = new CompositionPolicy();
});
// Test basic composition policy here; test more closely at
// the unit level in ContainmentTable for 'has' support, et al
it("enforces containment rules defined by types", function () {
expect(policy.allow(mockTypes[0], mockTypes[1]))
.toBeTruthy();
expect(policy.allow(mockTypes[1], mockTypes[1]))
.toBeTruthy();
expect(policy.allow(mockTypes[1], mockTypes[0]))
.toBeFalsy();
expect(policy.allow(mockTypes[0], mockTypes[0]))
.toBeFalsy();
describe('enforces simple containment rules', function () {
it('allows when type matches', function () {
mockChildObject.getCapability.andReturn(typeA);
expect(policy.allow(typeA, mockChildObject))
.toBeTruthy();
expect(policy.allow(typeB, mockChildObject))
.toBeTruthy();
mockChildObject.getCapability.andReturn(typeB);
expect(policy.allow(typeB, mockChildObject))
.toBeTruthy();
});
it('disallows when type doesn\'t match', function () {
mockChildObject.getCapability.andReturn(typeB);
expect(policy.allow(typeA, mockChildObject))
.toBeFalsy();
mockChildObject.getCapability.andReturn(typeC);
expect(policy.allow(typeA, mockChildObject))
.toBeFalsy();
});
});
describe('enforces capability-based containment rules', function () {
it('allows when object has capability', function () {
mockChildObject.hasCapability.andReturn(true);
expect(policy.allow(typeC, mockChildObject))
.toBeTruthy();
expect(mockChildObject.hasCapability)
.toHaveBeenCalledWith('telemetry');
});
it('skips when object doesn\'t have capability', function () {
mockChildObject.hasCapability.andReturn(false);
expect(policy.allow(typeC, mockChildObject))
.toBeFalsy();
expect(mockChildObject.hasCapability)
.toHaveBeenCalledWith('telemetry');
});
});
});

View File

@@ -44,16 +44,16 @@ define(
var model = parentObject && parentObject.getModel(),
composition = (model || {}).composition || [];
if (composition.indexOf(id) === -1) {
// $log.warn([
// "Attempted to contextualize",
// id,
// "in",
// parentObject && parentObject.getId(),
// "but that object does not contain",
// id,
// "in its composition.",
// "Unexpected behavior may follow."
// ].join(" "));
$log.warn([
"Attempted to contextualize",
id,
"in",
parentObject && parentObject.getId(),
"but that object does not contain",
id,
"in its composition.",
"Unexpected behavior may follow."
].join(" "));
}
}

View File

@@ -104,7 +104,7 @@ define(
expect(policyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object.capabilities.type
object
);
});

View File

@@ -114,7 +114,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object.capabilities.type
object
);
});

View File

@@ -124,7 +124,7 @@ define(
expect(policyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object.capabilities.type
object
);
});

View File

@@ -34,13 +34,14 @@ define(
function LayoutCompositionPolicy() {
}
LayoutCompositionPolicy.prototype.allow = function (candidate, context) {
var isFolderInLayout =
candidate &&
context &&
candidate.instanceOf('layout') &&
context.getCapability('type').instanceOf('folder');
return !isFolderInLayout;
LayoutCompositionPolicy.prototype.allow = function (parentType, child) {
if (parentType.instanceOf('layout') &&
child.getCapability('type').instanceOf('folder')) {
return false;
}
return true;
};
return LayoutCompositionPolicy;

View File

@@ -24,18 +24,25 @@ define(
["../src/LayoutCompositionPolicy"],
function (LayoutCompositionPolicy) {
describe("Layout's composition policy", function () {
var mockCandidate,
var mockChild,
mockCandidate,
mockContext,
candidateType,
contextType,
policy;
beforeEach(function () {
mockChild = jasmine.createSpyObj(
'childObject',
['getCapability']
);
mockCandidate =
jasmine.createSpyObj('candidateType', ['instanceOf']);
mockContext =
jasmine.createSpyObj('contextType', ['instanceOf']);
mockChild.getCapability.andReturn(mockContext);
mockCandidate.instanceOf.andCallFake(function (t) {
return t === candidateType;
});
@@ -49,19 +56,19 @@ define(
it("disallows folders in layouts", function () {
candidateType = 'layout';
contextType = 'folder';
expect(policy.allow(mockCandidate, mockContext)).toBe(false);
expect(policy.allow(mockCandidate, mockChild)).toBe(false);
});
it("does not disallow folders elsewhere", function () {
candidateType = 'nonlayout';
contextType = 'folder';
expect(policy.allow(mockCandidate, mockContext)).toBe(true);
expect(policy.allow(mockCandidate, mockChild)).toBe(true);
});
it("allows things other than folders in layouts", function () {
candidateType = 'layout';
contextType = 'nonfolder';
expect(policy.allow(mockCandidate, mockContext)).toBe(true);
expect(policy.allow(mockCandidate, mockChild)).toBe(true);
});
});

View File

@@ -60,7 +60,7 @@
</tr>
<tr ng-repeat-end
ng-style="{ top: visibleRow.offsetY + 'px' }"
ng-click="table.onRowClick($event, visibleRow.rowIndex) ">
ng-click="table.onRowClick($event, visibleRow.rowIndex)">
<td ng-repeat="header in displayHeaders"
ng-style="{
width: columnWidths[$index] + 'px',

View File

@@ -66,7 +66,8 @@ define(
mockDomainObject = jasmine.createSpyObj("domainObject", [
"getModel",
"getId",
"useCapability"
"useCapability",
"hasCapability"
]);
mockDomainObject.getModel.andReturn({});
mockDomainObject.getId.andReturn("mockId");

View File

@@ -166,13 +166,13 @@ define(
// Aggregators need other services to aggregate, otherwise they
// do nothing.
// if (!latest[service]) {
// return warn(
// aggregator,
// "aggregator",
// "No services to aggregate for"
// );
// }
if (!latest[service]) {
return warn(
aggregator,
"aggregator",
"No services to aggregate for"
);
}
dependencies = dependencies.concat([providerSetName]);
latest[service] = name;

View File

@@ -32,6 +32,7 @@ define(
mockPersistence,
mockDomainObject,
testModel,
testId,
decorator;
beforeEach(function () {
@@ -41,6 +42,7 @@ define(
['getCapabilities']
);
testModel = { someKey: "some value" };
testId = 'someId';
mockPersistence = jasmine.createSpyObj(
'persistence',
['persist', 'refresh']
@@ -67,9 +69,9 @@ define(
// QueuingPersistenceCapability itself, which has its own tests.
it("delegates to its wrapped service", function () {
decorator.getCapabilities(testModel);
decorator.getCapabilities(testModel, testId);
expect(mockCapabilityService.getCapabilities)
.toHaveBeenCalledWith(testModel);
.toHaveBeenCalledWith(testModel, testId);
});
it("wraps its persistence capability's constructor", function () {

View File

@@ -18,14 +18,14 @@
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.
-->
-->
<div class="search-result-item l-flex-row flex-elem grows"
ng-class="{selected: ngModel.selectedObject.getId() === domainObject.getId()}">
<mct-representation key="'label'"
mct-object="domainObject"
ng-model="ngModel"
ng-click="ngModel.selectedObject = domainObject"
ng-click="ngModel.allowSelection(domainObject) && ngModel.onSelection(domainObject)"
class="l-flex-row flex-elem grows">
</mct-representation>
</div>
</div>

View File

@@ -79,7 +79,6 @@ define([
"key": "telemetry",
"implementation": TelemetryCapability,
"depends": [
"openmct",
"$injector",
"$q",
"$log"

View File

@@ -24,12 +24,8 @@
* Module defining TelemetryCapability. Created by vwoeltje on 11/12/14.
*/
define(
[
'../../../src/api/objects/object-utils'
],
function (
objectUtils
) {
[],
function () {
var ZERO = function () {
return 0;
@@ -107,7 +103,7 @@ define(
* @implements {Capability}
* @constructor
*/
function TelemetryCapability(openmct, $injector, $q, $log, domainObject) {
function TelemetryCapability($injector, $q, $log, domainObject) {
// We could depend on telemetryService directly, but
// there isn't a platform implementation of this.
this.initializeTelemetryService = function () {
@@ -122,7 +118,7 @@ define(
}
};
this.openmct = openmct;
this.$q = $q;
this.$log = $log;
this.domainObject = domainObject;
@@ -164,20 +160,6 @@ define(
};
function asSeries(telemetry, defaultDomain, defaultRange) {
return {
getRangeValue: function (index, range) {
return telemetry[index][range || defaultRange];
},
getDomainValue: function (index, domain) {
return telemetry[index][domain || defaultDomain];
},
getPointCount: function () {
return telemetry.length;
}
}
}
/**
* Request telemetry data for this specific domain object.
* @param {TelemetryRequest} [request] parameters for this
@@ -187,21 +169,11 @@ define(
*/
TelemetryCapability.prototype.requestData = function requestTelemetry(request) {
// Bring in any defaults from the object model
var fullRequest = this.buildRequest(request || {});
var source = fullRequest.source;
var key = fullRequest.key;
var telemetryService = this.telemetryService ||
this.initializeTelemetryService(); // Lazy initialization
var domainObject = objectUtils.toNewFormat(this.domainObject.getModel(), this.domainObject.getId());
var telemetryAPI = this.openmct.telemetry;
var metadata = telemetryAPI.getMetadata(domainObject);
var defaultDomain = (metadata.valuesForHints(['domain'])[0] || {}).key;
var defaultRange = (metadata.valuesForHints(['range'])[0] || {}).key;
var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject)
=== telemetryAPI.legacyProvider;
var fullRequest = this.buildRequest(request || {}),
source = fullRequest.source,
key = fullRequest.key,
telemetryService = this.telemetryService ||
this.initializeTelemetryService(); // Lazy initialization
// Pull out the relevant field from the larger,
// structured response.
@@ -215,18 +187,11 @@ define(
return telemetryService.requestTelemetry([fullRequest]);
}
// TODO: Adapt request / options?
if (isLegacyProvider) {
// If a telemetryService is not available,
// getTelemetryService() should reject, and this should
// bubble through subsequent then calls.
return telemetryService &&
requestTelemetryFromService().then(getRelevantResponse);
} else {
return telemetryAPI.request(domainObject, fullRequest).then(function (telemetry) {
return asSeries(telemetry, defaultDomain, defaultRange);
});
}
// If a telemetryService is not available,
// getTelemetryService() should reject, and this should
// bubble through subsequent then calls.
return telemetryService &&
requestTelemetryFromService().then(getRelevantResponse);
};
/**
@@ -252,26 +217,12 @@ define(
* subscription request
*/
TelemetryCapability.prototype.subscribe = function subscribe(callback, request) {
var fullRequest = this.buildRequest(request || {});
var telemetryService = this.telemetryService ||
this.initializeTelemetryService(); // Lazy initialization
var domainObject = objectUtils.toNewFormat(this.domainObject.getModel(), this.domainObject.getId());
var telemetryAPI = this.openmct.telemetry;
var metadata = telemetryAPI.getMetadata(domainObject);
var defaultDomain = (metadata.valuesForHints(['domain'])[0] || {}).key;
var defaultRange = (metadata.valuesForHints(['range'])[0] || {}).key;
var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject)
=== telemetryAPI.legacyProvider;
function update(telemetry) {
callback(asSeries([telemetry], defaultDomain, defaultRange));
}
var fullRequest = this.buildRequest(request || {}),
telemetryService = this.telemetryService ||
this.initializeTelemetryService(); // Lazy initialization
// Unpack the relevant telemetry series
function updateLegacy(telemetries) {
function update(telemetries) {
var source = fullRequest.source,
key = fullRequest.key,
result = ((telemetries || {})[source] || {})[key];
@@ -280,13 +231,8 @@ define(
}
}
// Avoid a loop here...
if (isLegacyProvider) {
return telemetryService &&
telemetryService.subscribe(updateLegacy, [fullRequest]);
} else {
return telemetryAPI.subscribe(domainObject, update, fullRequest);
}
return telemetryService &&
telemetryService.subscribe(update, [fullRequest]);
};
/**

View File

@@ -26,14 +26,14 @@ define([], function () {
}
AdapterCompositionPolicy.prototype.allow = function (
containerType,
childObject
parentType,
child
) {
var containerObject = containerType.getInitialModel();
var container = parentType.getInitialModel();
return this.openmct.composition.checkPolicy(
containerObject,
childObject
container,
child
);
};

View File

@@ -146,7 +146,7 @@ define([
utils.toOldFormat(domainObject),
utils.makeKeyString(domainObject.identifier));
var limitEvaluator = oldObject.getCapability("limit");
if (!limitEvaluator) {
return;
}
@@ -159,8 +159,7 @@ define([
};
return function (openmct, instantiate) {
// Push onto the start of the default providers array so that it's
// always the last resort
// Legacy provider should always be the fallback.
var provider = new LegacyTelemetryProvider(openmct, instantiate);
openmct.telemetry.legacyProvider = provider;
openmct.telemetry.requestProviders.push(provider);

View File

@@ -176,8 +176,9 @@ define([
* @private
*/
TelemetryAPI.prototype.findSubscriptionProvider = function (domainObject) {
var args = Array.prototype.slice.apply(arguments);
function supportsDomainObject(provider) {
return provider.supportsSubscribe(domainObject);
return provider.supportsSubscribe.apply(provider, args);
}
return this.subscriptionProviders.filter(supportsDomainObject)[0];
@@ -187,8 +188,9 @@ define([
* @private
*/
TelemetryAPI.prototype.findRequestProvider = function (domainObject, options) {
var args = Array.prototype.slice.apply(arguments);
function supportsDomainObject(provider) {
return provider.supportsRequest(domainObject);
return provider.supportsRequest.apply(provider, args);
}
return this.requestProviders.filter(supportsDomainObject)[0];
@@ -330,8 +332,7 @@ define([
*/
TelemetryAPI.prototype.limitEvaluator = function () {
return this.legacyProvider.limitEvaluator.apply(this.legacyProvider, arguments);
}
};
return TelemetryAPI;
});

View File

@@ -105,6 +105,7 @@ define([
this.valueMetadatas = this.valueMetadatas.map(applyReasonableDefaults);
}
/**
* Get value metadata for a single key.
*/

View File

@@ -51,7 +51,7 @@ define(['./Type'], function (Type) {
* @method addType
* @memberof module:openmct.TypeRegistry#
*/
TypeRegistry.prototype.register = function (typeKey, typeDef) {
TypeRegistry.prototype.addType = function (typeKey, typeDef) {
this.types[typeKey] = new Type(typeDef);
};

View File

@@ -33,6 +33,7 @@ define([
'../example/export/bundle',
'../example/extensions/bundle',
'../example/forms/bundle',
'../example/generator/bundle',
'../example/identity/bundle',
'../example/imagery/bundle',
'../example/mobile/bundle',

View File

@@ -1,171 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT 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 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([
"./GeneratorProvider",
"./SinewaveLimitCapability",
"./SinewaveDeltaFormat"
], function (
GeneratorProvider,
SinewaveLimitCapability,
SinewaveDeltaFormat
) {
var legacyExtensions = {
"capabilities": [
{
"key": "limit",
"implementation": SinewaveLimitCapability
}
],
"formats": [
{
"key": "example.delta",
"implementation": SinewaveDeltaFormat
}
],
"constants": [
{
"key": "TIME_CONDUCTOR_DOMAINS",
"value": [
{
"key": "time",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta"
}
],
"priority": -1
}
]
}
return function(openmct){
//Register legacy extensions for things not yet supported by the new API
Object.keys(legacyExtensions).forEach(function (type){
var extensionsOfType = legacyExtensions[type];
extensionsOfType.forEach(function (extension) {
openmct.legacyExtension(type, extension)
})
});
openmct.types.register("generator", {
label: "Sine Wave Generator",
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
cssClass: "icon-telemetry",
creatable: true,
form: [
{
name: "Period",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "period",
required: true,
property: [
"telemetry",
"period"
],
pattern: "^\\d*(\\.\\d*)?$"
},
{
name: "Amplitude",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "amplitude",
required: true,
property: [
"telemetry",
"amplitude"
],
pattern: "^\\d*(\\.\\d*)?$"
},
{
name: "Offset",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "offset",
required: true,
property: [
"telemetry",
"offset"
],
pattern: "^\\d*(\\.\\d*)?$"
},
{
name: "Data Rate (hz)",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "dataRateInHz",
required: true,
property: [
"telemetry",
"dataRateInHz"
],
pattern: "^\\d*(\\.\\d*)?$"
}
],
initialize: function (object) {
object.telemetry = {
period: 10,
amplitude: 1,
offset: 0,
dataRateInHz: 1,
domains: [
{
key: "utc",
name: "Time",
format: "utc"
},
{
key: "yesterday",
name: "Yesterday",
format: "utc"
},
{
key: "delta",
name: "Delta",
format: "example.delta"
}
],
ranges: [
{
key: "sin",
name: "Sine"
},
{
key: "cos",
name: "Cosine"
}
]
};
}
});
openmct.telemetry.addProvider(new GeneratorProvider());
};
});

View File

@@ -22,12 +22,10 @@
define([
'lodash',
'../../platform/features/conductor/utcTimeSystem/src/UTCTimeSystem',
'./generator/plugin'
'../../platform/features/conductor/utcTimeSystem/src/UTCTimeSystem'
], function (
_,
UTCTimeSystem,
GeneratorPlugin
UTCTimeSystem
) {
var bundleMap = {
couchDB: 'platform/persistence/couch',
@@ -138,9 +136,5 @@ define([
};
};
plugins.Generator = function () {
return GeneratorPlugin;
};
return plugins;
});