Repository: falcon Updated Branches: refs/heads/master adf53c86f -> c4df0a5e9
http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/test/services/EntitySerializerSpec.js ---------------------------------------------------------------------- diff --git a/falcon-ui/app/test/services/EntitySerializerSpec.js b/falcon-ui/app/test/services/EntitySerializerSpec.js new file mode 100644 index 0000000..448fec9 --- /dev/null +++ b/falcon-ui/app/test/services/EntitySerializerSpec.js @@ -0,0 +1,1286 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +(function () { + 'use strict'; + + describe('EntitySerializer', function () { + var serializer; + + beforeEach(module('app.services.entity.serializer')); + + + beforeEach(inject(function(EntitySerializer) { + serializer = EntitySerializer; + })); + + describe('deserialize feed', function() { + + it('Should copy the general information', function() { + + var feedModel = { + feed: { + _xmlns: "uri:falcon:feed:0.1", + _name: 'FeedName', + _description: 'Feed Description' + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.name).toBe(feedModel.feed._name); + expect(feed.description).toBe(feedModel.feed._description); + expect(feed.xmlns).toBe(undefined); + }); + + it('Should copy tags', function() { + + var feedModel = { + feed: { + tags: 'owner=USMarketing,classification=Secure' + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.tags[0].key).toBe('owner'); + expect(feed.tags[0].value).toBe('USMarketing'); + expect(feed.tags[1].key).toBe('classification'); + expect(feed.tags[1].value).toBe('Secure'); + }); + + it('Should copy groups', function() { + + var feedModel = { + feed: { + groups: 'churnAnalysisDataPipeline,Group2,Group3' + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.groups).toBe(feedModel.feed.groups); + }); + + it('Should copy ACL', function() { + var feedModel = { + feed: { + ACL: {_owner: 'ambari-qa', _group: 'users', _permission: '0755' } + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.ACL.owner).toBe(feedModel.feed.ACL._owner); + expect(feed.ACL.group).toBe(feedModel.feed.ACL._group); + expect(feed.ACL.permission).toBe(feedModel.feed.ACL._permission); + }); + + it('Should copy Schema', function() { + var feedModel = { + feed: { + schema: {_location: '/location', _provider: 'provider'} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.schema.location).toBe(feedModel.feed.schema._location); + expect(feed.schema.provider).toBe(feedModel.feed.schema._provider); + }); + + it('Should copy frequency', function() { + var feedModel = { + feed: { + frequency: 'hours(20)' + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.frequency.unit).toBe('hours'); + expect(feed.frequency.quantity).toBe('20'); + }); + + it('Should copy late arrival', function() { + var feedModel = { + feed: { + "late-arrival": {"_cut-off": 'days(10)'} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.lateArrival.active).toBe(true); + expect(feed.lateArrival.cutOff.unit).toBe('days'); + expect(feed.lateArrival.cutOff.quantity).toBe('10'); + }); + + it('Should not copy late arrival when is not present', function() { + var feedModel = { + feed: {} + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.lateArrival.active).toBe(false); + expect(feed.lateArrival.cutOff.unit).toBe('hours'); + expect(feed.lateArrival.cutOff.quantity).toBe(null); + }); + + it('Should copy availabilityFlag', function() { + var feedModel = { + feed: { + availabilityFlag: 'Available' + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.availabilityFlag).toBe(feedModel.feed.availabilityFlag); + }); + + it('Should not copy availabilityFlag if not present in the xml', function() { + var feedModel = { + feed: {} + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.availabilityFlag).toBe(null); + }); + + it('Should copy custom properties', function() { + var feedModel = { + feed: { + properties: {property: [ + {_name: 'Prop1', _value: 'Value1'}, + {_name: 'Prop2', _value: 'Value2'} + ]} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.customProperties.length).toBe(3); + expect(feed.customProperties[1].key).toBe('Prop1'); + expect(feed.customProperties[1].value).toBe('Value1'); + expect(feed.customProperties[2].key).toBe('Prop2'); + expect(feed.customProperties[2].value).toBe('Value2'); + }); + + it('Should not copy falcon properties into the custom properties', function() { + var feedModel = { + feed: { + properties: {property: [ + {_name: 'queueName', _value: 'QueueName'}, + {_name: 'Prop1', _value: 'Value1'} + ]} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.customProperties.length).toBe(2); + expect(feed.customProperties[0].key).toBe(null); + expect(feed.customProperties[0].value).toBe(null); + expect(feed.customProperties[1].key).toBe('Prop1'); + expect(feed.customProperties[1].value).toBe('Value1'); + }); + + it('Should copy queueName properties into properties', function() { + var feedModel = { + feed: { + properties: {property: [ + {_name: 'queueName', _value: 'QueueName'}, + {_name: 'Prop1', _value: 'Value1'} + ]} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.properties.length).toBe(6); + expect(feed.properties[0].key).toBe('queueName'); + expect(feed.properties[0].value).toBe('QueueName'); + }); + + it('Should leave the default properties if no properties appear on the xml and copy the new ones', function() { + var feedModel = { + feed: { + properties: { + property: [ + {_name: 'jobPriority', _value: 'MEDIUM'} + ] + } + } + }; + + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.properties.length).toBe(6); + expect(feed.properties[0].key).toBe('queueName'); + expect(feed.properties[0].value).toBe('default'); + expect(feed.properties[1].key).toBe('jobPriority'); + expect(feed.properties[1].value).toBe('MEDIUM'); + }); + + it('Should copy timeout as a Frequency Object', function() { + var feedModel = { + feed: { + properties: {property: [ + {_name: 'queueName', _value: 'QueueName'}, + {_name: 'timeout', _value: 'days(4)'} + ]} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.properties.length).toBe(6); + expect(feed.properties[2].key).toBe('timeout'); + expect(feed.properties[2].value.quantity).toBe('4'); + expect(feed.properties[2].value.unit).toBe('days'); + }); + + it('Should copy file system locations', function() { + var feedModel = { + feed: { + locations: {location: [ + {_type: 'data', _path: '/none1'}, + {_type: 'stats', _path: '/none2'} + ]} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + var locations = feed.storage.fileSystem.locations; + + expect(feed.storage.fileSystem.active).toBe(true); + expect(locations.length).toBe(2); + expect(locations[0].type).toBe('data'); + expect(locations[0].path).toBe('/none1'); + expect(locations[1].type).toBe('stats'); + expect(locations[1].path).toBe('/none2'); + }); + + it('Should not copy file system locations if they are not defined and keep the defaults', function() { + var feedModel = { + feed: { + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + var locations = feed.storage.fileSystem.locations; + + expect(feed.storage.fileSystem.active).toBe(false); + expect(locations.length).toBe(3); + expect(locations[0].type).toBe('data'); + expect(locations[0].path).toBe('/'); + expect(locations[1].type).toBe('stats'); + expect(locations[1].path).toBe('/'); + expect(locations[2].type).toBe('meta'); + expect(locations[2].path).toBe('/'); + }); + + it('Should set file system active flag as false if there are no locations are', function() { + var feedModel = { + feed: {} + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.storage.fileSystem.active).toBe(false); + }); + + it('Should copy catalog uri', function() { + var feedModel = { + feed: { + "table": { + _uri : 'table:uri' + } + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.storage.catalog.active).toBe(true); + expect(feed.storage.catalog.catalogTable.uri).toBe('table:uri'); + }); + + it('Should not copy catalog uri if not present', function() { + var feedModel = { + feed: {} + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.storage.catalog.active).toBe(false); + expect(feed.storage.catalog.catalogTable.uri).toBe(null); + }); + + it('Should copy cluster name and type', function() { + var feedModel = { + feed: { + clusters: { + cluster: [{ + _name: 'ClusterOne', + _type: 'target' + }] + } + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.clusters.length).toBe(1); + expect(feed.clusters[0].name).toBe('ClusterOne'); + expect(feed.clusters[0].type).toBe('target'); + }); + + it('Should copy clusters and select the first source cluster', function() { + var feedModel = { + feed: { + clusters: { + cluster: [ + {_name: 'ClusterOne', _type: 'target'}, + {_name: 'ClusterTwo', _type: 'source'}, + {_name: 'ClusterThree', _type: 'target'}, + {_name: 'ClusterFour', _type: 'source'} + ] + } + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.clusters[0].selected).toBe(false); + expect(feed.clusters[1].selected).toBe(true); + expect(feed.clusters[2].selected).toBe(false); + expect(feed.clusters[3].selected).toBe(false); + + }); + + xit('Should copy validity', function() { + var feedModel = { + feed: { + clusters: {cluster: [{_name: 'ClusterOne', _type: 'target', + validity: { + _start: '2014-02-28T01:20Z', + _end: '2016-03-31T04:30Z' + } + }]} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.clusters[0].validity.start.date).toEqual(new Date(2014, 2, 28,0,0)); + expect(feed.clusters[0].validity.start.time).toEqual(newUtcTime(1, 20)); + expect(feed.clusters[0].validity.end.date).toEqual(newUtcDate(2016, 3, 31)); + expect(feed.clusters[0].validity.end.time).toEqual(newUtcTime(4, 30)); + + }); + + it('Should copy retention', function() { + var feedModel = { + feed: { + clusters: {cluster: [{_name: 'ClusterOne', _type: 'target', + retention: { + _limit: 'weeks(4)', + _action: 'delete' + } + }]} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.clusters[0].retention.quantity).toBe('4'); + expect(feed.clusters[0].retention.unit).toBe('weeks'); + expect(feed.clusters[0].retention.action).toBe('delete'); + }); + + it('Should copy clusters locations', function() { + var feedModel = { + feed: { + clusters: {cluster: [{_name: 'ClusterOne', _type: 'target', + locations: { + location: [ + {_type: 'stats', _path: '/path1'}, + {_type: 'data', _path: '/path2'}, + {_type: 'tmp', _path: '/path3'} + ]} + }]} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + var locations = feed.clusters[0].storage.fileSystem.locations; + + expect(feed.clusters[0].storage.fileSystem.active).toBe(true); + expect(locations.length).toBe(3); + expect(locations[0].type).toBe('stats'); + expect(locations[0].path).toBe('/path1'); + expect(locations[1].type).toBe('data'); + expect(locations[1].path).toBe('/path2'); + expect(locations[2].type).toBe('tmp'); + expect(locations[2].path).toBe('/path3'); + }); + + it('filesystem should be inactive if there are no locations', function() { + var feedModel = { + feed: { + clusters: {cluster: [{_name: 'ClusterOne', _type: 'target'}]} + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + var locations = feed.clusters[0].storage.fileSystem.locations; + + expect(feed.clusters[0].storage.fileSystem.active).toBe(false); + expect(locations.length).toBe(3); + }); + + it('Should copy catalog uri', function() { + var feedModel = { + feed: { + clusters: {cluster: [{_name: 'ClusterOne', _type: 'target', + "table": { + _uri : 'table:uri' + } + }]} + } + }; + + + var feed = serializer.preDeserialize(feedModel, 'feed'); + var catalogStorage = feed.clusters[0].storage.catalog; + + expect(catalogStorage.active).toBe(true); + expect(catalogStorage.catalogTable.uri).toBe('table:uri'); + }); + + it('Should set catalog storage as inactive when not present in the xml', function() { + var feedModel = { + feed: { + clusters: {cluster: [{_name: 'ClusterOne', _type: 'target'}]} + } + }; + + + var feed = serializer.preDeserialize(feedModel, 'feed'); + var catalogStorage = feed.clusters[0].storage.catalog; + + expect(catalogStorage.active).toBe(false); + expect(catalogStorage.catalogTable.uri).toBe(null); + }); + + it('Should copy timezone', function() { + var feedModel = { + feed: { + timezone: 'GMT+03:50' + } + }; + + var feed = serializer.preDeserialize(feedModel, 'feed'); + + expect(feed.timezone).toBe('GMT+03:50'); + }); + + }); + + describe('serialize feed into xml', function() { + + it('Should transform the basic properties', function () { + var feed = { + name: 'FeedName', + description: 'Feed Description', + groups: 'a,b,c' + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName' description='Feed Description'>" + + "<groups>a,b,c</groups>" + + "</feed>" + ); + + }); + + it('Should transform tags properly', function () { + var feed = {name: 'FeedName', + tags: [{key: 'key1', value: 'value1'}, {key: 'key2', value: 'value2'}, {key: null, value: 'value3'}] + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<tags>key1=value1,key2=value2</tags>" + + "</feed>" + ); + + }); + + it('Should transform ACL properly', function () { + var feed = {name: 'FeedName', + ACL: {owner: 'ambari-qa', group: 'users', permission: '0755'} + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<ACL owner='ambari-qa' group='users' permission='0755'/>" + + "</feed>" + ); + + }); + + it('Should add an ACL element even though the properties are empty', function () { + var feed = {name: 'FeedName', + ACL: {owner: null, group: null, permission: null} + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<ACL/>" + + "</feed>" + ); + + }); + + it('Should transform schema properly', function () { + var feed = {name: 'FeedName', + schema: {location: '/location', provider: 'none'} + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<schema location='/location' provider='none'/>" + + "</feed>" + ); + + }); + + it('Should add the schema element even though the properties are empty', function () { + var feed = {name: 'FeedName', + schema: {location: null, provider: null} + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<schema/>" + + "</feed>" + ); + + }); + + it('Should transform frequency properly', function () { + var feed = {name: 'FeedName', + frequency: {quantity: 4, unit: 'weeks'} + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<frequency>weeks(4)</frequency>" + + "</feed>" + ); + + }); + + it('Should transform late arrival properly when defined', function () { + var feed = {name: 'FeedName', + lateArrival: {active: true, cutOff: {quantity: 22, unit: 'hours'}} + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<late-arrival cut-off='hours(22)'/>" + + "</feed>" + ); + + }); + + it('Should not transform late arrival properly when quantity is not defined', function () { + var feed = {name: 'FeedName', + lateArrival: {active: false, cutOff: {quantity: null, unit: 'hours'}} + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'/>" + ); + + }); + + it('Should transform availability flag', function () { + var feed = {name: 'FeedName', + availabilityFlag: 'Available' + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<availabilityFlag>Available</availabilityFlag>" + + "</feed>" + ); + + }); + + it('Should transform timezone', function () { + var feed = {name: 'FeedName', + timezone: 'GMT+1:00' + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<timezone>GMT+1:00</timezone>" + + "</feed>" + ); + + }); + + it('Should transform queueName, jobPriority and timeout and custom properties', function () { + var feed = {name: 'FeedName', + properties: [ + {key: 'queueName', value: 'Queue'}, + {key: 'jobPriority', value: 'HIGH'}, + {key: 'timeout', value: {quantity: 7, unit: 'weeks'}} + ], + customProperties: [ + {key: 'custom1', value: 'value1'}, + {key: 'custom2', value: 'value2'} + ] + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<properties>" + + "<property name='queueName' value='Queue'></property>" + + "<property name='jobPriority' value='HIGH'></property>" + + "<property name='timeout' value='weeks(7)'></property>" + + "<property name='custom1' value='value1'></property>" + + "<property name='custom2' value='value2'></property>" + + "</properties>" + + "</feed>" + ); + + }); + + it('Should transform not add queueName nor timeout if they were not defined', function () { + var feed = {name: 'FeedName', + properties: [ + {key: 'queueName', value: null}, + {key: 'jobPriority', value: 'HIGH'}, + {key: 'timeout', value: {quantity: null, unit: 'weeks'}} + ] + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<properties>" + + "<property name='jobPriority' value='HIGH'></property>" + + "</properties>" + + "</feed>" + ); + + }); + + it('Should transform locations properly if file system storage is active', function () { + var feed = {name: 'FeedName', + storage: { + fileSystem: { + active: true, + locations: [ + {type: 'data', path: '/none1'}, + {type: 'stats', path: '/none2'}, + {type: 'meta', path: '/none3'} + ] + } + } + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<locations>" + + "<location type='data' path='/none1'></location>" + + "<location type='stats' path='/none2'></location>" + + "<location type='meta' path='/none3'></location>" + + "</locations>" + + "</feed>" + ); + + }); + + it('Should not transform locations properly if file system storage is not active', function () { + var feed = {name: 'FeedName', + storage: { + fileSystem: { + active: false, + locations: [ + {type: 'data', path: '/none1'}, + {type: 'stats', path: '/none2'}, + {type: 'meta', path: '/none3'} + ] + } + } + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'/>" + ); + + }); + + it('Should transform catalog properly if catalog storage is active', function () { + var feed = {name: 'FeedName', + storage: { + catalog: { + active: true, + catalogTable: {uri: '/none'} + } + } + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<table uri='/none'/>" + + "</feed>" + ); + + }); + + it('Should not transform catalog if catalog storage is not active', function () { + var feed = {name: 'FeedName', + storage: { + catalog: { + active: false, + catalogTable: {uri: '/none'} + } + } + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'/>" + ); + + }); + + xit('Should transform clusters', function () { + var feed = {name: 'FeedName', + storage: { + fileSystem: {active: true, locations: [ + {type: 'data', path: '/masterpath'} + ]}, + catalog: {active: true, catalogTable: {uri: '/masteruri'}} + }, + clusters: [ + { + name: 'primaryCluster', + type: 'source', + validity: {start: {date: newUtcDate(2014, 2, 28), time: newUtcTime(0,0)}, end: {date: newUtcDate(2016, 4, 1), time: newUtcTime(0,0)}}, + retention: {quantity: 2, unit: 'hours', action: 'delete'}, + storage: { + fileSystem: { + active: true, + locations: [ + {type: 'data', path: '/none1'}, + {type: 'stats', path: '/none2'}, + {type: 'meta', path: '/none3'} + ] + }, + catalog: { + active: false, + catalogTable: {uri: '/primaryuri'} + } + } + }, + { + name: 'secondaryCluster', + type: 'target', + validity: {start: {date: newUtcDate(2015, 2, 28), time: newUtcTime(0,0)}, end: {date: newUtcDate(2017, 4, 1), time: newUtcTime(0,0)}}, + retention: {quantity: 5, unit: 'weeks', action: 'archive'}, + storage: { + fileSystem: { + active: true, + locations: [ + {type: 'data', path: '/none4'}, + {type: 'stats', path: '/none5'}, + {type: 'meta', path: '/none6'} + ] + }, + catalog: { + active: true, + catalogTable: {uri: '/secondaryuri'} + } + } + } + ] + }; + + var xml = serializer.serialize(feed, 'feed'); + + expect(xml).toBe( + "<feed xmlns='uri:falcon:feed:0.1' name='FeedName'>" + + "<clusters>" + + "<cluster name='primaryCluster' type='source'>" + + "<validity start='2014-02-28T00:00Z' end='2016-04-01T00:00Z'/>" + + "<retention limit='hours(2)' action='delete'/>" + + "<locations>" + + "<location type='data' path='/none1'></location>" + + "<location type='stats' path='/none2'></location>" + + "<location type='meta' path='/none3'></location>" + + "</locations>" + + "<table uri='/primaryuri'/>" + + "</cluster>" + + "<cluster name='secondaryCluster' type='target'>" + + "<validity start='2015-02-28T00:00Z' end='2017-04-01T00:00Z'/>" + + "<retention limit='weeks(5)' action='archive'/>" + + "<locations>" + + "<location type='data' path='/none4'></location>" + + "<location type='stats' path='/none5'></location>" + + "<location type='meta' path='/none6'></location>" + + "</locations>" + + "<table uri='/secondaryuri'/>" + + "</cluster>" + + "</clusters>" + + "<locations>" + + "<location type='data' path='/masterpath'></location>" + + "</locations>" + + "<table uri='/masteruri'/>" + + "</feed>" + ); + + }); + + }); + + describe('deserialize process', function() { + + it('Should copy the general information', function() { + + var processModel = { + process: { + _xmlns: "uri:falcon:process:0.1", + _name: 'ProcessName' + } + }; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.name).toBe(processModel.process._name); + expect(process.xmlns).toBe(undefined); + }); + + it('Should copy tags', function() { + + var processModel = { + process: { + tags: 'owner=USMarketing,classification=Secure' + } + }; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.tags[0].key).toBe('owner'); + expect(process.tags[0].value).toBe('USMarketing'); + expect(process.tags[1].key).toBe('classification'); + expect(process.tags[1].value).toBe('Secure'); + }); + + it('Should copy workflow', function() { + + var processModel = { + process: {_xmlns: "uri:falcon:process:0.1", _name: 'ProcessName', + workflow: { + _name: 'emailCleanseWorkflow', + _version: '5.0', + _engine: 'pig', + _path: '/user/ambari-qa/falcon/demo/apps/pig/id.pig' + } + } + }; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.workflow.name).toBe(processModel.process.workflow._name); + expect(process.workflow.version).toBe(processModel.process.workflow._version); + expect(process.workflow.engine).toBe(processModel.process.workflow._engine); + expect(process.workflow.path).toBe(processModel.process.workflow._path); + + }); + + it('Should copy timezone', function() { + + var processModel = { + process: {_xmlns: "uri:falcon:process:0.1", _name: 'ProcessName', + timezone: 'GMT+1:00' + } + }; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.timezone).toBe(processModel.process.timezone); + }); + + it('Should copy frequency', function() { + var processModel = {process: { + frequency: 'hours(20)' + }}; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.frequency.unit).toBe('hours'); + expect(process.frequency.quantity).toBe('20'); + + }); + + it('Should copy parallel', function() { + var processModel = { process: { + parallel: 3 + }}; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.parallel).toBe(processModel.process.parallel); + + }); + + it('Should copy order', function() { + var processModel = { process: { + order: 'LIFO' + }}; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.order).toBe(processModel.process.order); + + }); + + it('Should copy retry', function() { + var processModel = { process: { + retry: { + _policy: 'periodic', _attempts: '3', _delay: 'minutes(15)' + } + }}; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.retry.policy).toBe(processModel.process.retry._policy); + expect(process.retry.attempts).toBe(processModel.process.retry._attempts); + expect(process.retry.delay.quantity).toBe('15'); + expect(process.retry.delay.unit).toBe('minutes'); + + }); + + xit('Should copy clusters', function() { + var processModel = { + process: { + clusters: {cluster: [{_name: 'ClusterOne', + validity: { + _start: '2014-02-28T01:20Z', + _end: '2016-03-31T04:30Z' + } + }]} + } + }; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.clusters[0].validity.start.date).toEqual(newUtcDate(2014, 2, 28)); + expect(process.clusters[0].validity.start.time).toEqual(newUtcTime(1, 20)); + expect(process.clusters[0].validity.end.date).toEqual(newUtcDate(2016, 3, 31)); + expect(process.clusters[0].validity.end.time).toEqual(newUtcTime(4, 30)); + + }); + + it('Should copy inputs', function() { + var processModel = { + process: { + inputs: {input: [ + {_name: 'input', _feed: 'rawEmailFeed', _start: 'now(0,0)', _end: 'now(0,0)' } + ]} + } + }; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.inputs[0].name).toEqual('input'); + expect(process.inputs[0].feed).toEqual('rawEmailFeed'); + expect(process.inputs[0].start).toEqual('now(0,0)'); + expect(process.inputs[0].end).toEqual('now(0,0)'); + }); + + it('Should copy outputs', function() { + var processModel = { + process: { + outputs: {output: [ + {_name: 'output', _feed: 'cleansedEmailFeed', _instance: 'now(0,0)' } + ]} + } + }; + + var process = serializer.preDeserialize(processModel, 'process'); + + expect(process.outputs[0].name).toEqual('output'); + expect(process.outputs[0].feed).toEqual('cleansedEmailFeed'); + expect(process.outputs[0].outputInstance).toEqual('now(0,0)'); + }); + + }); + + describe('serialize process into xml', function() { + + it('Should transform the basic properties', function () { + var process = { + name: 'ProcessName' + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'/>" + ); + }); + + it('Should transform tags properly', function () { + var process = {name: 'ProcessName', + tags: [{key: 'key1', value: 'value1'}, {key: 'key2', value: 'value2'}, {key: null, value: 'value3'}] + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'>" + + "<tags>key1=value1,key2=value2</tags>" + + "</process>" + ); + }); + + it('Should transform workflow properly', function () { + var process = {name: 'ProcessName', + workflow: { + name: 'emailCleanseWorkflow', + engine: 'pig', + version: '5.0', + path: '/user/ambari-qa/falcon/demo/apps/pig/id.pig' + } + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'>" + + "<workflow name='emailCleanseWorkflow' version='5.0' engine='pig' path='/user/ambari-qa/falcon/demo/apps/pig/id.pig'/>" + + "</process>" + ); + }); + + it('Should transform timezone', function () { + var process = {name: 'ProcessName', + timezone: 'GMT+1:00' + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'>" + + "<timezone>GMT+1:00</timezone>" + + "</process>" + ); + + }); + + it('Should transform frequency properly', function () { + var process = {name: 'ProcessName', + frequency: {quantity: 4, unit: 'weeks'} + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'>" + + "<frequency>weeks(4)</frequency>" + + "</process>" + ); + + }); + + it('Should transform parallel properly', function () { + var process = {name: 'ProcessName', + parallel: 11 + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'>" + + "<parallel>11</parallel>" + + "</process>" + ); + + }); + + it('Should transform order properly', function () { + var process = {name: 'ProcessName', + order: 'LIFO' + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'>" + + "<order>LIFO</order>" + + "</process>" + ); + + }); + + it('Should transform retry properly', function () { + var process = {name: 'ProcessName', + retry: { + policy: 'periodic', + delay: {quantity: 15, unit: 'minutes'}, + attempts: 3 + } + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'>" + + "<retry policy='periodic' delay='minutes(15)' attempts='3'/>" + + "</process>" + ); + }); + + xit('Should transform clusters', function () { + + var process = {name: 'ProcessName', + clusters: [ + { + name: 'primaryCluster', + validity: {start: {date: newUtcDate(2014, 2, 28), time: newUtcTime(0,0)}, end: {date: newUtcDate(2016, 4, 1), time: newUtcTime(0,0)}} + }, + { + name: 'secondaryCluster', + validity: {start: {date: newUtcDate(2015, 2, 28), time: newUtcTime(0,0)}, end: {date: newUtcDate(2017, 4, 1), time: newUtcTime(0,0)}} + } + ] + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'>" + + "<clusters>" + + "<cluster name='primaryCluster'>" + + "<validity start='2014-02-28T00:00Z' end='2016-04-01T00:00Z'/>" + + "</cluster>" + + "<cluster name='secondaryCluster'>" + + "<validity start='2015-02-28T00:00Z' end='2017-04-01T00:00Z'/>" + + "</cluster>" + + "</clusters>" + + "</process>" + ); + + }); + + it('Should transform inputs', function () { + + var process = {name: 'ProcessName', + inputs: [ + { name: 'input', feed: 'rawEmailFeed', start: 'now(0,0)', end: 'now(0,0)' } + ] + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'>" + + "<inputs>" + + "<input name='input' feed='rawEmailFeed' start='now(0,0)' end='now(0,0)'>" + + "</input>" + + "</inputs>" + + "</process>" + ); + + }); + + it('Should transform outputs', function () { + + var process = {name: 'ProcessName', + outputs: [ + { name: 'output', feed: 'cleansedEmailFeed', outputInstance: 'now(0,0)'} + ] + }; + + var xml = serializer.serialize(process, 'process'); + + expect(xml).toBe( + "<process xmlns='uri:falcon:process:0.1' name='ProcessName'>" + + "<outputs>" + + "<output name='output' feed='cleansedEmailFeed' instance='now(0,0)'>" + + "</output>" + + "</outputs>" + + "</process>" + ); + + }); + + }); + + function newUtcDate(year, month, day) { + return new Date(Date.UTC(year, month, day)) + } + + function newUtcTime(hours, minutes) { + return new Date(Date.UTC(1900, 1, 1, hours, minutes, 0)); + } + + }); +})(); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/test/services/FalconServiceSpec.js ---------------------------------------------------------------------- diff --git a/falcon-ui/app/test/services/FalconServiceSpec.js b/falcon-ui/app/test/services/FalconServiceSpec.js new file mode 100644 index 0000000..04f6b5a --- /dev/null +++ b/falcon-ui/app/test/services/FalconServiceSpec.js @@ -0,0 +1,77 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +(function () { + 'use strict'; + + describe('Falcon', function () { + var httpBackendMock; + var service; + var cookiesMock; + + beforeEach(module('ngCookies', 'app.services.falcon')); + + beforeEach(inject(function($httpBackend, Falcon) { + httpBackendMock = $httpBackend; + service = Falcon; + })); + + describe('initialize', function() { + it('Should set the object Falcon.responses', function() { + expect(service.responses).toEqual({ + display : true, + queue:[], + count: {pending: 0, success:0, error:0}, + multiRequest: {cluster:0, feed:0, process:0}, + listLoaded: {cluster:false, feed:false, process:false} + }); + }); + + }); + describe('.logRequest()', function() { + it('Should log the pending request', function() { + service.logRequest(); + expect(service.responses.count.pending).toEqual(1); + service.logRequest(); + service.logRequest(); + service.logRequest(); + expect(service.responses.count.pending).toEqual(4); + }); + + it('Should throw an error when the category does not exist', function() { + + }); + + }); + describe('Falcon.logResponse(type, messageObject, hide)', function() { + it('Should log resolve pending request', function() { + var responseOne = {"status":"SUCCEEDED","message":"default/TEST2(FEED) suspended successfully\n","requestId":"default/b3a31c93-23e0-450d-bb46-b3e1be0525ff\n"}; + service.logRequest(); + service.logRequest(); + service.logRequest(); + expect(service.responses.count.pending).toEqual(3); + service.logResponse('success', responseOne, "cluster"); + expect(service.responses.count.success).toEqual(1); + expect(service.responses.count.pending).toEqual(2); + + service.logResponse('success', responseOne, "cluster"); + service.logResponse('success', responseOne, "cluster"); + expect(service.responses.multiRequest.cluster).toEqual(-3); + }); + }); + }); +})(); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/test/services/JsonTransformerSpec.js ---------------------------------------------------------------------- diff --git a/falcon-ui/app/test/services/JsonTransformerSpec.js b/falcon-ui/app/test/services/JsonTransformerSpec.js new file mode 100644 index 0000000..80864f4 --- /dev/null +++ b/falcon-ui/app/test/services/JsonTransformerSpec.js @@ -0,0 +1,144 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +(function () { + 'use strict'; + + describe('JsonTransformerFactory', function () { + var httpBackendMock; + var factory; + + beforeEach(module('app.services.json.transformer')); + + beforeEach(inject(function($httpBackend, JsonTransformerFactory) { + httpBackendMock = $httpBackend; + factory = JsonTransformerFactory; + })); + + describe('Field transformation', function() { + + + it('Should create a new field transformation from source and target', function() { + var transformation = factory.transform('name', '_name'); + var source = {name: 'someName'}, target = {}; + + transformation.apply(source, target); + + expect(target).toEqual({_name: 'someName'}); + }); + + it('Should not add the element if it does not exist', function() { + var transformation = factory.transform('name', '_name'); + var source = {}, target = {}; + + transformation.apply(source, target); + + expect(target).toEqual({}); + }); + + it('Should not add the element if it is null', function() { + var transformation = factory.transform('name', '_name'); + var source = {name: null}, target = {}; + + transformation.apply(source, target); + + expect(target).toEqual({}); + }); + + it('Should create a new field transformation implying the target field', function() { + var transformation = factory.transform('name'); + var source = {name: 'someName'}, target = {}; + + transformation.apply(source, target); + + expect(target).toEqual({name: 'someName'}); + }); + + + it('Should create a new field transformation for a nested property', function() { + var transformation = factory.transform('nested.key', 'nested1.nested2.nested3.key'); + var source = {nested: {key: 'key', key2: 'key2'}}, target = {}; + + transformation.apply(source, target); + + expect(target).toEqual({nested1: {nested2: { nested3: {key: 'key'}}}}); + }); + + it('Should be able to apply many transformations one after the other', function() { + var transformation1 = factory.transform('nested.key', 'nested1.nested2.nested3.key'); + var transformation2 = factory.transform('nested.key2', 'nested1.nested2._key'); + var source = {nested: {key: 'key', key2: 'key2'}}, target = {}; + + transformation1.apply(source, target); + transformation2.apply(source, target); + + expect(target).toEqual({nested1: {nested2: { _key: 'key2', nested3: {key: 'key'}}}}); + }); + + it('Should work for arrays too', function() { + var transformation = factory.transform('nested.key', 'key'); + var source = {nested: {key: ['a', 'b']}}, target = {}; + + transformation.apply(source, target); + + expect(target).toEqual({key: ['a','b']}); + }); + + it('Should support custom mapping functions', function() { + var mapping = function (input) { + return input.key + '=' + input.value; + }; + var transformation = factory.transform('nested.value', 'nested1.nested2', mapping); + var source = {nested: {value: {key: 'key', value: 'value'}}}, target = {}; + + transformation.apply(source, target); + + expect(target).toEqual({nested1: {nested2: 'key=value'}}); + }); + + + it('Should be able to chain transformations', function() { + var source = {nested: {key: 'key', key2: 'key2'}}, target = {}; + + var transformation = factory + .transform('nested.key', 'nested1.nested2.nested3.key') + .transform('nested.key2', 'nested1.nested2._key') + .transform('nested.key2', 'nested1.nested2._key') + .transform('nested.key2', 'nested1.nested2._key'); + + transformation.apply(source, target); + + expect(target).toEqual({nested1: {nested2: { _key: 'key2', nested3: {key: 'key'}}}}); + }); + + it('Should be able transform arrays', function() { + var source = {nested: {list: [{key: 'key'}], key2: 'key2'}}, target = {}; + var nestedTransformation = factory.transform('key', '_key'); + var transformation = factory.transform('nested.list', 'nested1.nested2.list', function (list) { + return list.map(function(input) { return nestedTransformation.apply(input, {}); }); + }); + + + transformation.apply(source, target); + + expect(target).toEqual({nested1: {nested2: {list: [{_key: 'key'}]}}}); + }); + + + }); + }); +})(); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/test/services/ValdationServiceSpec.js ---------------------------------------------------------------------- diff --git a/falcon-ui/app/test/services/ValdationServiceSpec.js b/falcon-ui/app/test/services/ValdationServiceSpec.js new file mode 100644 index 0000000..6699892 --- /dev/null +++ b/falcon-ui/app/test/services/ValdationServiceSpec.js @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +(function () { + 'use strict'; + + describe('ValidationService', function () { + var validationService; + + beforeEach(module('app.services.validation')); + + beforeEach(inject(function (ValidationService) { + validationService = ValidationService; + })); + + describe('init', function () { + it('Should return a validation object service', function () { + expect(validationService).toBeDefined(); + }); + }); + describe('messages', function () { + it('Should return a messages validation object', function () { + expect(validationService.messages).toBeDefined(); + }); + }); + describe('patterns', function () { + it('Should return an object with the validation patterns', function () { + expect(validationService.patterns).toBeDefined(); + expect(validationService.patterns.name).toEqual(/^[a-zA-Z0-9]{1,39}$/); + + }); + }); + describe('nameAvailable', function () { + it('Should return a boolean with the name availability', function () { + expect(validationService.nameAvailable).toBe(true); + }); + }); + describe('displayValidations', function () { + it('Should return an object with the displays in false', function () { + expect(validationService.displayValidations).toEqual({show: false, nameShow: false}); + }); + }); + describe('acceptOnlyNumber', function () { + it('Should return an method to prevent default if no number typed', function () { + var isFunction = validationService.acceptOnlyNumber instanceof Function; + expect(isFunction).toBe(true); + }); + }); + describe('acceptNoSpaces', function () { + it('Should return a method prevent default if space typed', function () { + var isFunction = validationService.acceptNoSpaces instanceof Function; + expect(isFunction).toBe(true); + }); + }); + }); +}()); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/test/services/X2jsServiceSpec.js ---------------------------------------------------------------------- diff --git a/falcon-ui/app/test/services/X2jsServiceSpec.js b/falcon-ui/app/test/services/X2jsServiceSpec.js new file mode 100644 index 0000000..1d7621d --- /dev/null +++ b/falcon-ui/app/test/services/X2jsServiceSpec.js @@ -0,0 +1,128 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +(function () { + 'use strict'; + + describe('X2jsService', function () { + var x2jsService; + + beforeEach(module('app.services')); + + beforeEach(inject(function(X2jsService) { + x2jsService = X2jsService; + })); + + describe('prettifyXml', function() { + it('Should prettify the xml', function() { + var uglyXml = '<markup><child></child></markup>'; + + expect(x2jsService.prettifyXml(uglyXml)).toNotBe(null); + }); + }); + + describe('prettifyXml', function() { + it('Should prettify the xml', function() { + var uglyXml = '<markup><child></child></markup>'; + + expect(x2jsService.prettifyXml(uglyXml)).toNotBe(null); + }); + }); + + describe('Arrays configuration', function() { + it('Should convert feed.properties.property as an array when only one element', function() { + + var xml = + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + + '<feed name="feedOne" description="feedOneDescription" xmlns="uri:falcon:feed:0.1">' + + '<properties>' + + '<property name="jobPriority" value="VERY_LOW"/>' + + '</properties>' + + '</feed>'; + + var wrapper = x2jsService.xml_str2json(xml); + + expect(wrapper.feed.properties.property).toEqual( + [{_name: 'jobPriority', _value: 'VERY_LOW'}] + ); + + }); + + + + it('Should convert feed.locations.location as an array when only one element', function() { + + var xml = + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + + '<feed name="feedOne" description="feedOneDescription" xmlns="uri:falcon:feed:0.1">' + + '<locations>' + + '<location type="data" path="/path"/>' + + '</locations>' + + '</feed>'; + + var wrapper = x2jsService.xml_str2json(xml); + + expect(wrapper.feed.locations.location).toEqual( + [{_type: 'data', _path: '/path'}] + ); + + }); + + it('Should convert feed.clusters.cluster as an array when only one element', function() { + + var xml = + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + + '<feed name="feedOne" description="feedOneDescription" xmlns="uri:falcon:feed:0.1">' + + '<clusters>' + + '<cluster name="ClusterName" type="source"/>' + + '</clusters>' + + '</feed>'; + + var wrapper = x2jsService.xml_str2json(xml); + + expect(wrapper.feed.clusters.cluster).toEqual( + [{_name: 'ClusterName', _type: 'source'}] + ); + + }); + + it('Should convert feed.clusters.cluster.locations.location as an array when only one element', function() { + + var xml = + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + + '<feed name="feedOne" description="feedOneDescription" xmlns="uri:falcon:feed:0.1">' + + '<clusters>' + + '<cluster name="ClusterName" type="source">' + + '<locations>' + + '<location type="data" path="/path" />' + + '</locations>' + + '</cluster>' + + '</clusters>' + + '</feed>'; + + var wrapper = x2jsService.xml_str2json(xml); + + expect(wrapper.feed.clusters.cluster[0].locations.location).toEqual( + [{_type: 'data', _path: '/path'}] + ); + + }); + + }); + + }); +})(); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/bower.json ---------------------------------------------------------------------- diff --git a/falcon-ui/bower.json b/falcon-ui/bower.json new file mode 100644 index 0000000..d924150 --- /dev/null +++ b/falcon-ui/bower.json @@ -0,0 +1,18 @@ +{ + "name": "falcon-ui", + "version": "1.0.0", + "homepage": "empty", + "authors": [ + "Apache Foundation" + ], + "description": "falcon-ui", + "main": "app/index.html", + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "app/js/lib", + "app/test" + ] +} http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/express-data/mockData.js ---------------------------------------------------------------------- diff --git a/falcon-ui/express-data/mockData.js b/falcon-ui/express-data/mockData.js new file mode 100644 index 0000000..cdd783c --- /dev/null +++ b/falcon-ui/express-data/mockData.js @@ -0,0 +1,204 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +(function () { + 'use strict'; + + function findByNameInList(type, name) { + var i; + for (i = 0; i < entitiesList[type].entity.length; i++) { + if (entitiesList[type].entity[i]["name"] === name) { + return i; + } + } + } + + var entitiesList = { + cluster : { + "entity":[ + {"type":"cluster","name":"completeCluster","status":"SUBMITTED","list":{"tag":["uruguay=mvd","argentina=bsas","mexico=mex", "usa=was"]}}, + {"type":"cluster","name":"primaryCluster","status":"SUBMITTED"} + ] + }, + feed: { + "entity":[ + {"type":"FEED","name":"feedOne","status":"SUBMITTED","list":{"tag":["externalSystem=USWestEmailServers","classification=secure"]}}, + {"type":"FEED","name":"feedTwo","status":"RUNNING","list":{"tag":["owner=USMarketing","classification=Secure","externalSource=USProdEmailServers","externalTarget=BITools"]}} + ] + }, + process:{"entity":[ + {"type":"process","name":"processOne","status":"SUBMITTED","list":{"tag":["pipeline=churnAnalysisDataPipeline","owner=ETLGroup","montevideo=mvd"]}}, + {"type":"process","name":"processTwo","status":"SUBMITTED","list":{"tag":["pipeline=churnAnalysisDataPipeline","owner=ETLGroup","externalSystem=USWestEmailServers"]}}]} + }, + definitions = { + CLUSTER: { + completeCluster : '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + + '<cluster name="completeCluster" description="desc" colo="colo" xmlns="uri:falcon:cluster:0.1">' + + '<tags>uruguay=mvd,argentina=bsas</tags>' + + '<interfaces>' + + '<interface type="readonly" endpoint="hftp://sandbox.hortonworks.com:50070" version="2.2.0"/>' + + '<interface type="write" endpoint="hdfs://sandbox.hortonworks.com:8020" version="2.2.0"/>' + + '<interface type="execute" endpoint="sandbox.hortonworks.com:8050" version="2.2.0"/>' + + '<interface type="workflow" endpoint="http://sandbox.hortonworks.com:11000/oozie/" version="4.0.0"/>' + + '<interface type="messaging" endpoint="tcp://sandbox.hortonworks.com:61616?daemon=true" version="5.1.6"/>' + + '<interface type="registry" endpoint="thrift://localhost:9083" version="0.11.0"/>' + + '</interfaces>' + + '<locations>' + + '<location name="staging" path="/apps/falcon/backupCluster/staging"/>' + + '<location name="temp" path="/tmp"/>' + + '<location name="working" path="/apps/falcon/backupCluster/working"/>' + + '</locations>' + + '<ACL owner="ambari-qa" group="users" permission="0755"/>' + + '<properties>' + + '<property name="this" value="property"/>' + + '</properties>' + + '</cluster>', + primaryCluster: '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + + '<cluster name="primaryCluster" description="oregonHadoopCluster" colo="USWestOregon" xmlns="uri:falcon:cluster:0.1">' + + '<interfaces>' + + '<interface type="readonly" endpoint="hftp://sandbox.hortonworks.com:50070" version="2.2.0"/>' + + '<interface type="write" endpoint="hdfs://sandbox.hortonworks.com:8020" version="2.2.0"/>' + + '<interface type="execute" endpoint="sandbox.hortonworks.com:8050" version="2.2.0"/>' + + '<interface type="workflow" endpoint="http://sandbox.hortonworks.com:11000/oozie/" version="4.0.0"/>' + + '<interface type="messaging" endpoint="tcp://sandbox.hortonworks.com:61616?daemon=true" version="5.1.6"/>' + + '</interfaces>' + + '<locations>' + + '<location name="staging" path="/apps/falcon/primaryCluster/staging"/>' + + '<location name="temp" path="/tmp"/>' + + '<location name="working" path="/apps/falcon/primaryCluster/working"/>' + + '</locations>' + + '</cluster>' + }, + FEED: { + feedOne: '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + + '<feed name="feedOne" description="Raw customer email feed" xmlns="uri:falcon:feed:0.1">' + + '<tags>externalSystem=USWestEmailServers,classification=secure</tags>' + + '<frequency>hours(1)</frequency>' + + '<timezone>UTC</timezone>' + + '<late-arrival cut-off="hours(4)"/>' + + '<clusters>' + + '<cluster name="primaryCluster" type="source">' + + '<validity start="2014-02-28T00:00Z" end="2016-04-01T00:00Z"/>' + + '<retention limit="days(90)" action="delete"/>' + + '<locations>' + + '<location type="data" path="/"/>' + + '<location type="stats" path="/"/>' + + '<location type="meta" path="/"/>' + + '</locations>' + + '</cluster>' + + '</clusters>' + + '<locations>' + + '<location type="data" path="hdfs://sandbox.hortonworks.com:8020/"/>' + + '<location type="stats" path="/none"/>' + + '<location type="meta" path="/none"/>' + + '</locations>' + + '<ACL owner="ambari-qa" group="users" permission="0755"/>' + + '<schema location="/none" provider="none"/>' + + '<properties>' + + '<property name="queueName" value="default"/>' + + '<property name="jobPriority" value="NORMAL"/>' + + '<property name="timeout" value="hours(1)"/>' + + '<property name="parallel" value="3"/>' + + '<property name="maxMaps" value="8"/>' + + '<property name="mapBandwidthKB" value="1024"/>' + + '</properties>' + + '</feed>', + feedTwo: '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + + '<feed name="feedTwo" description="Cleansed customer emails" xmlns="uri:falcon:feed:0.1">' + + '<tags>owner=USMarketing,classification=Secure,externalSource=USProdEmailServers,externalTarget=BITools</tags>' + + '<groups>churnAnalysisDataPipeline</groups>' + + '<frequency>hours(1)</frequency>' + + '<timezone>UTC</timezone>' + + '<clusters>' + + '<cluster name="primaryCluster" type="source">' + + '<validity start="2014-02-28T00:00Z" end="2016-04-01T00:00Z"/>' + + '<retention limit="days(90)" action="delete"/>' + + '<locations>' + + '<location type="data" path="/user/ambari-qa/falcon/demo/primary/processed/enron/${YEAR}-${MONTH}-${DAY}-${HOUR}"/>' + + '</locations>' + + '</cluster>' + + '<cluster name="backupCluster" type="target">' + + '<validity start="2014-02-28T00:00Z" end="2016-04-01T00:00Z"/>' + + '<retention limit="months(36)" action="delete"/>' + + '<locations>' + + '<location type="data" path="/falcon/demo/bcp/processed/enron/${YEAR}-${MONTH}-${DAY}-${HOUR}"/>' + + '</locations>' + + '</cluster>' + + '</clusters>' + + '<locations>' + + '<location type="data" path="/user/ambari-qa/falcon/demo/processed/enron/${YEAR}-${MONTH}-${DAY}-${HOUR}"/>' + + '</locations>' + + '<ACL owner="ambari-qa" group="users" permission="0755"/>' + + '<schema location="/none" provider="none"/>' + + '<properties>' + + '<property name="queueName" value="default"/>' + + '<property name="jobPriority" value="NORMAL"/>' + + '<property name="timeout" value="hours(1)"/>' + + '<property name="parallel" value="3"/>' + + '<property name="maxMaps" value="8"/>' + + '<property name="mapBandwidthKB" value="1024"/>' + + '</properties>' + + '</feed>' + }, + PROCESS: { + processOne: '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + + '<process name="processOne" xmlns="uri:falcon:process:0.1">' + + '<tags>pipeline=churnAnalysisDataPipeline,owner=ETLGroup,montevideo=mvd</tags>' + + '<clusters>' + + '<cluster name="primaryCluster">' + + '<validity start="2014-02-28T00:00Z" end="2016-04-01T00:00Z"/>' + + '</cluster>' + + '</clusters>' + + '<parallel>1</parallel>' + + '<order>FIFO</order>' + + '<frequency>hours(1)</frequency>' + + '<timezone>GMT-12:00</timezone>' + + '<inputs>' + + '<input name="input" feed="rawEmailFeed" start="now(0,0)" end="now(0,0)"/>' + + '</inputs>' + + '<outputs>' + + '<output name="output" feed="cleansedEmailFeed" instance="now(0,0)"/>' + + '</outputs>' + + '<workflow name="emailCleanseWorkflow2" version="pig-0.10.0" engine="pig" path="/user/ambari-qa/falcon/demo/apps/pig/id.pig"/>' + + '<retry policy="periodic" delay="minutes(15)" attempts="3"/>' + + '</process>', + processTwo: '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + + '<process name="processTwo" xmlns="uri:falcon:process:0.1">' + + '<tags>pipeline=churnAnalysisDataPipeline,owner=ETLGroup,externalSystem=USWestEmailServers</tags>' + + '<clusters>' + + '<cluster name="primaryCluster">' + + '<validity start="2014-02-28T00:00Z" end="2016-03-31T00:00Z"/>' + + '</cluster>' + + '</clusters>' + + '<parallel>1</parallel>' + + '<order>FIFO</order>' + + '<frequency>hours(1)</frequency>' + + '<timezone>UTC</timezone>' + + '<outputs>' + + '<output name="output" feed="rawEmailFeed" instance="now(0,0)"/>' + + '</outputs>' + + '<workflow name="emailIngestWorkflow" version="2.0.0" engine="oozie" path="/user/ambari-qa/falcon/demo/apps/ingest/fs"/>' + + '<retry policy="periodic" delay="minutes(15)" attempts="3"/>' + + '</process>' + } + }; + + exports.findByNameInList = findByNameInList; + exports.entitiesList = entitiesList; + exports.definitions = definitions; + +})(); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/karma.conf.js ---------------------------------------------------------------------- diff --git a/falcon-ui/karma.conf.js b/falcon-ui/karma.conf.js new file mode 100644 index 0000000..11189e8 --- /dev/null +++ b/falcon-ui/karma.conf.js @@ -0,0 +1,81 @@ +// Karma configuration +// Generated on Wed Sep 24 2014 21:15:41 GMT-0500 (CDT) + +module.exports = function(config) { + config.set({ + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine'], + + + // list of files / patterns to load in the browser + files: [ + 'app/js/lib/angular.min.js', + 'app/js/lib/angular-cookies.min.js', + 'app/js/lib/angular-messages.min.js', + 'app/js/lib/angular-mocks.js', + 'app/js/lib/jquery-1.11.1.min.js', + 'app/js/lib/d3.min.js', + 'app/js/lib/ui-bootstrap-tpls-0.11.0.min.js', + 'app/js/lib/uirouter.min.js', + 'app/js/lib/xml2json.min.js', + 'app/js/controllers/**/*-module.js', + 'app/js/controllers/**/*.js', + 'app/js/services/**/*.js', + 'app/js/directives/**/*.js', + 'app/js/app.js', + 'app/test/**/*Spec.js' + ], + + + + + // list of files to exclude + exclude: [ + ], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + }, + + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress'], + + + // web server port + port: 9876, + + + // enable / disable colors in the output (reporters and logs) + colors: true, + + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['PhantomJS'], + + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false + }); +}; http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/package.json ---------------------------------------------------------------------- diff --git a/falcon-ui/package.json b/falcon-ui/package.json new file mode 100644 index 0000000..dda4855 --- /dev/null +++ b/falcon-ui/package.json @@ -0,0 +1,38 @@ +{ + "name": "FalconUI", + "version": "0.0.1", + "description": "UI to communicate with Apache Falcon", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "author": "Apache Foundation", + "license": "ISC", + "devDependencies": { + "angular-mocks": "^1.2.22", + "body-parser": "~1.0.2", + "bower": "^1.3.12", + "express": "~4.0.0", + "file": "~0.2.2", + "grunt": "~0.4.4", + "grunt-cli": "~0.1.9", + "grunt-contrib-clean": "^0.6.0", + "grunt-contrib-copy": "~0.5.0", + "grunt-contrib-concat": "^0.5.0", + "grunt-contrib-csslint": "~0.2.0", + "grunt-contrib-jshint": "~0.10.0", + "grunt-contrib-less": "~0.11.0", + "grunt-contrib-uglify": "~0.4.0", + "grunt-contrib-watch": "~0.6.1", + "grunt-datauri": "~0.4.0", + "grunt-express-server": "~0.4.13", + "grunt-karma": "^0.9.0", + "grunt-scp": "^0.1.7", + "jasmine": "^2.0.1", + "karma": "^0.12.23", + "karma-jasmine": "^0.1.5", + "karma-phantomjs-launcher": "^0.1.4", + "phantomjs": "^1.9.10", + "protractor": "^1.3.1" + } +} http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/pom.xml ---------------------------------------------------------------------- diff --git a/falcon-ui/pom.xml b/falcon-ui/pom.xml new file mode 100644 index 0000000..8853937 --- /dev/null +++ b/falcon-ui/pom.xml @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.falcon</groupId> + <artifactId>falcon-main</artifactId> + <version>0.7-SNAPSHOT</version> + </parent> + <artifactId>falcon-ui</artifactId> + <packaging>pom</packaging> + <name>Apache Falcon UI</name> + <description>Apache Falcon UI Application</description> + <build> + <plugins> + + <plugin> + <groupId>com.github.eirslett</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>0.0.14</version> + <executions> + + <execution> + <id>install node and npm</id> + <goals> + <goal>install-node-and-npm</goal> + </goals> + <configuration> + <nodeVersion>v0.10.30</nodeVersion> + <npmVersion>1.4.3</npmVersion> + </configuration> + </execution> + + <execution> + <id>npm install</id> + <goals> + <goal>npm</goal> + </goals> + <configuration> + <arguments>install</arguments> + </configuration> + </execution> + + <execution> + <id>grunt build</id> + <goals> + <goal>grunt</goal> + </goals> + <configuration> + <arguments>build</arguments> + </configuration> + </execution> + + <execution> + <id>grunt test</id> + <goals> + <goal>grunt</goal> + </goals> + <configuration> + <arguments>test</arguments> + </configuration> + </execution> + + </executions> + </plugin> + + <plugin> + <groupId>org.apache.rat</groupId> + <artifactId>apache-rat-plugin</artifactId> + <configuration> + <excludes> + <exclude>app/js/lib/**</exclude> + <exclude>app/css/bootstrap/**</exclude> + <exclude>app/test/lib/**</exclude> + <exclude>app/css/fonts/**</exclude> + <exclude>app/config/loginData.js</exclude> + <exclude>dist/**</exclude> + <exclude>node/**</exclude> + <exclude>node_modules/**</exclude> + <exclude>package.json</exclude> + <exclude>bower.json</exclude> + <exclude>karma.conf.js</exclude> + <exclude>bower.json</exclude> + <exclude>app/css/main.css</exclude> + <exclude>app/test/e2e/protractor.js</exclude> + </excludes> + </configuration> + </plugin> + + </plugins> + </build> +</project> http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/server.js ---------------------------------------------------------------------- diff --git a/falcon-ui/server.js b/falcon-ui/server.js new file mode 100644 index 0000000..422bcf9 --- /dev/null +++ b/falcon-ui/server.js @@ -0,0 +1,143 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +(function () { + "use strict"; + + var bodyParser = require('body-parser'), + express = require('express'), + mockData = require('./express-data/mockData.js'), + server = express(), + PORT = 3000; + + server.use('/', express.static(__dirname + '/dist')); + server.use(bodyParser()); + server.use(function (req, res, next) { + if (req.is('text/*')) { + req.text = ''; + req.setEncoding('utf8'); + req.on('data', function (chunk) { req.text += chunk; }); + req.on('end', next); + } else { + next(); + } + }); + + server.get('/api/entities/list/:type', function (req, res) { + var type = req.params.type; + res.json(mockData.entitiesList[type]); + }); + + server.get('/api/entities/definition/:type/:name', function(req, res) { + var type = req.params.type.toUpperCase(), + name = req.params.name; + if (mockData.definitions[type][name]) { + res.send(200, mockData.definitions[type][name]); + } else { + res.send(404, "not found"); + } + }); + + server.post('/api/entities/submit/:type', function (req, res) { + var type = req.params.type.toUpperCase(), + text = req.text, + name, + indexInArray, + responseSuccessMessage, + responseFailedMessage, + initialIndex = text.indexOf("name") + 6, + finalIndex = getFinalIndexOfName(), + i; + function getFinalIndexOfName () { + for (i = initialIndex; i < text.length; i++) { + if (text[i] === '"' || text[i] === "'") { + return i; + } + } + } + name = text.slice(initialIndex, finalIndex); + responseSuccessMessage = {"status": "SUCCEEDED", "message": "default/successful (" + type + ") " + name + "\n\n","requestId":"default/546cbe05-2cb3-4e5c-8e7a-b1559d866c99\n"}; + responseFailedMessage = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><result><status>FAILED</status><message>(' + type + ') '+ name +' already registered with configuration store. Can\'t be submitted again. Try removing before submitting.</message><requestId>586fffcd-10c1-4975-8dda-4b34a712f2f4</requestId></result>'; + + if (!mockData.definitions[type][name]) { + mockData.definitions[type][name] = text; + mockData.entitiesList[type.toLowerCase()].entity.push( + {"type": type, "name": name, "status": "SUBMITTED"} + ); + res.send(200, responseSuccessMessage); + } else { + res.send(404, responseFailedMessage); + } + }); + + server.post('/api/entities/schedule/:type/:name', function (req, res) { + var type = req.params.type.toLowerCase(), + name = req.params.name, + indexInArray = mockData.findByNameInList(type, name), + responseMessage = { + "status": "SUCCEEDED", + "message": "default/" + name + "(" + type + ") scheduled successfully\n", + "requestId": "default/546cbe05-2cb3-4e5c-8e7a-b1559d866c99\n" + }; + mockData.entitiesList[type].entity[indexInArray].status = "RUNNING"; + res.json(200, responseMessage); + }); + + server.post('/api/entities/suspend/:type/:name', function (req, res) { + var type = req.params.type.toLowerCase(), + name = req.params.name, + indexInArray = mockData.findByNameInList(type, name), + responseMessage = { + "status": "SUCCEEDED", + "message": "default/" + name + "(" + type + ") suspended successfully\n", + "requestId": "default/546cbe05-2cb3-4e5c-8e7a-b1559d866c99\n" + }; + mockData.entitiesList[type].entity[indexInArray].status = "SUSPENDED"; + res.json(200, responseMessage); + }); + + server.post('/api/entities/resume/:type/:name', function (req, res) { + var type = req.params.type.toLowerCase(), + name = req.params.name, + indexInArray = mockData.findByNameInList(type, name), + responseMessage = { + "status": "SUCCEEDED", + "message": "default/" + name + "(" + type + ") resumed successfully\n", + "requestId": "default/546cbe05-2cb3-4e5c-8e7a-b1559d866c99\n" + }; + mockData.entitiesList[type].entity[indexInArray].status = "RUNNING"; + res.json(200, responseMessage); + }); + + server.delete('/api/entities/delete/:type/:name', function (req, res) { + var type = req.params.type, + name = req.params.name, + responseMessage = { + "status": "SUCCEEDED", + "message": "falcon/default/" + name + "(" + type + ")removed successfully (KILLED in ENGINE)\n\n", + "requestId": "falcon/default/13015853-8e40-4923-9d32-6d01053c31c6\n\n" + }, + indexInArray = mockData.findByNameInList(type, name); + mockData.entitiesList[type].entity.splice(indexInArray, 1); + res.json(200, responseMessage); + }); + + server.listen(PORT, function () { + console.log('Dev server listening on port ' + PORT); + }); + +}()); http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index d290ebf..ee743c5 100644 --- a/pom.xml +++ b/pom.xml @@ -371,6 +371,7 @@ </profiles> <modules> + <module>falcon-ui</module> <module>checkstyle</module> <module>build-tools</module> <module>client</module> http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/webapp/pom.xml ---------------------------------------------------------------------- diff --git a/webapp/pom.xml b/webapp/pom.xml index 556b171..063d42c 100644 --- a/webapp/pom.xml +++ b/webapp/pom.xml @@ -44,6 +44,10 @@ <packagingExcludes>WEB-INF/classes/deploy.properties</packagingExcludes> <webResources> <resource> + <directory>../falcon-ui/dist</directory> + <targetPath>public</targetPath> + </resource> + <resource> <directory>src/main/webapp/WEB-INF/distributed</directory> <targetPath>WEB-INF</targetPath> </resource> @@ -241,6 +245,10 @@ <directory>../html5-ui</directory> </resource> <resource> + <directory>../falcon-ui/dist</directory> + <targetPath>public</targetPath> + </resource> + <resource> <directory>src/main/webapp/WEB-INF/embedded</directory> <targetPath>WEB-INF</targetPath> </resource> @@ -479,6 +487,34 @@ </executions> </plugin> + <plugin> + <groupId>org.apache.rat</groupId> + <artifactId>apache-rat-plugin</artifactId> + <configuration> + <excludes> + <exclude>*.txt</exclude> + <exclude>**/*.txt</exclude> + <exclude>.git/**</exclude> + <exclude>**/.idea/**</exclude> + <exclude>**/*.twiki</exclude> + <exclude>**/*.iml</exclude> + <exclude>**/target/**</exclude> + <exclude>**/activemq-data/**</exclude> + <exclude>**/build/**</exclude> + <exclude>**/*.patch</exclude> + <exclude>derby.log</exclude> + <exclude>**/logs/**</exclude> + <exclude>**/.classpath</exclude> + <exclude>**/.project</exclude> + <exclude>**/.settings/**</exclude> + <exclude>**/test-output/**</exclude> + <exclude>**/data.txt</exclude> + <exclude>**/maven-eclipse.xml</exclude> + <exclude>**/.externalToolBuilders/**</exclude> + </excludes> + </configuration> + </plugin> + </plugins> </build>
