Storyboard API Interface and basic project management
Here I add three major components: Firstly, the API abstractions (resources) that drive our consumption of the storyboard API. Secondly, a series of API mock interceptors that trigger when HTTP requests are made and simulate the existence of the storyboard API. Lastly, a basic UI for project creation, listing, and management. Change-Id: Idbce8252237b0f9fbb9dd2330b952f9a6432c694
This commit is contained in:
parent
e9c34dec57
commit
32283d03cc
@ -24,7 +24,7 @@
|
||||
"browser": true,
|
||||
"esnext": true,
|
||||
"bitwise": true,
|
||||
"camelcase": true,
|
||||
"camelcase": false,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"immed": true,
|
||||
@ -56,6 +56,8 @@
|
||||
"inject": false,
|
||||
"it": false,
|
||||
"spyOn": false,
|
||||
"runs": false,
|
||||
"waitsFor": false,
|
||||
|
||||
// functional test constants
|
||||
"browser": false,
|
||||
|
16
bower.json
16
bower.json
@ -4,19 +4,19 @@
|
||||
"dependencies": {
|
||||
"jquery": "2.0.3",
|
||||
"font-awesome": "4.0",
|
||||
"angular": "1.2.5",
|
||||
"angular-resource": "1.2.5",
|
||||
"angular-cookies": "1.2.5",
|
||||
"angular-sanitize": "1.2.5",
|
||||
"angular": "1.2.9",
|
||||
"angular-resource": "1.2.9",
|
||||
"angular-cookies": "1.2.9",
|
||||
"angular-sanitize": "1.2.9",
|
||||
"bootstrap": "3.0.0",
|
||||
"angular-ui-router": "0.2.0",
|
||||
"angular-ui-router": "0.2.7",
|
||||
"angular-translate": "1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "1.2.5",
|
||||
"angular-scenario": "1.2.5"
|
||||
"angular-mocks": "1.2.9",
|
||||
"angular-scenario": "1.2.9"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "1.2.5"
|
||||
"angular": "1.2.9"
|
||||
}
|
||||
}
|
||||
|
142
src/app/projects/controllers/project_detail_controller.js
Normal file
142
src/app/projects/controllers/project_detail_controller.js
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* Project detail & manipulation controller. Usable for any view that wants to
|
||||
* view, edit, or delete a project, though views don't have to use all the
|
||||
* functions therein. Includes flags for busy time, error responses and more.
|
||||
*
|
||||
* This controller assumes that the $stateParams object is both injectable and
|
||||
* contains an ":id" property that indicates which project should be loaded. At
|
||||
* the moment it will only set a 'isLoading' flag to indicate that data is
|
||||
* loading. If loading the data is anticipated to take longer than 3 seconds,
|
||||
* this will need to be updated to display a sane progress.
|
||||
*
|
||||
* Do not allow loading of this (or any) controller to take longer than 10
|
||||
* seconds. 3 is preferable.
|
||||
*/
|
||||
angular.module('sb.projects').controller('ProjectDetailController',
|
||||
function ($scope, $state, $stateParams, Project) {
|
||||
'use strict';
|
||||
|
||||
// Parse the ID
|
||||
var id = $stateParams.hasOwnProperty('id') ?
|
||||
parseInt($stateParams.id, 10) :
|
||||
null;
|
||||
|
||||
/**
|
||||
* The project we're manipulating right now.
|
||||
*
|
||||
* @type Project
|
||||
*/
|
||||
$scope.project = {};
|
||||
|
||||
/**
|
||||
* UI flag for when we're initially loading the view.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
$scope.isLoading = true;
|
||||
|
||||
/**
|
||||
* UI view for when a change is round-tripping to the server.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
$scope.isUpdating = false;
|
||||
|
||||
/**
|
||||
* Any error objects returned from the services.
|
||||
*
|
||||
* @type {{}}
|
||||
*/
|
||||
$scope.error = {};
|
||||
|
||||
/**
|
||||
* Generic service error handler. Assigns errors to the view's scope,
|
||||
* and unsets our flags.
|
||||
*/
|
||||
function handleServiceError(error) {
|
||||
// We've encountered an error.
|
||||
$scope.error = error;
|
||||
$scope.isLoading = false;
|
||||
$scope.isUpdating = false;
|
||||
}
|
||||
|
||||
|
||||
// Sanity check, do we actually have an ID? (zero is falsy)
|
||||
if (!id && id !== 0) {
|
||||
// We should never reach this, however that logic lives outside
|
||||
// of this controller which could be unknowningly refactored.
|
||||
$scope.error = {
|
||||
error: true,
|
||||
error_code: 404,
|
||||
error_message: 'You did not provide a valid ID.'
|
||||
};
|
||||
$scope.isLoading = false;
|
||||
} else {
|
||||
// We've got an ID, so let's load it...
|
||||
Project.read(
|
||||
{'id': id},
|
||||
function (result) {
|
||||
// We've got a result, assign it to the view and unset our
|
||||
// loading flag.
|
||||
$scope.project = result;
|
||||
$scope.isLoading = false;
|
||||
},
|
||||
handleServiceError
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope method, invoke this when you want to update the project.
|
||||
*/
|
||||
$scope.update = function () {
|
||||
// Set our progress flags and clear previous error conditions.
|
||||
$scope.isUpdating = true;
|
||||
$scope.error = {};
|
||||
|
||||
// Invoke the save method and wait for results.
|
||||
$scope.project.$update(
|
||||
function () {
|
||||
// Unset our loading flag and navigate to the detail view.
|
||||
$scope.isUpdating = false;
|
||||
$state.go('project.detail', {id: $scope.project.id});
|
||||
},
|
||||
handleServiceError
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Scope method, invoke this when you'd like to delete this project.
|
||||
*/
|
||||
$scope.remove = function () {
|
||||
// Set our progress flags and clear previous error conditions.
|
||||
$scope.isUpdating = true;
|
||||
$scope.error = {};
|
||||
|
||||
// Try to delete.
|
||||
$scope.project.$delete(
|
||||
function () {
|
||||
// The deletion was successful, so head back to the list
|
||||
// view.
|
||||
$scope.isUpdating = false;
|
||||
$state.go('project.list');
|
||||
},
|
||||
handleServiceError
|
||||
);
|
||||
};
|
||||
});
|
@ -15,14 +15,48 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Storyboard project submodule handles most activity surrounding the
|
||||
* creation and management of projects.
|
||||
* The project list controller handles discovery for all projects, including
|
||||
* search. Note that it is assumed that we implemented a search (inclusive),
|
||||
* rather than a browse (exclusive) approach.
|
||||
*/
|
||||
angular.module('sb.projects').controller('ProjectListController',
|
||||
function ($scope) {
|
||||
function ($scope, Project) {
|
||||
'use strict';
|
||||
|
||||
$scope.search = function () {
|
||||
// Variables and methods available to the template...
|
||||
$scope.projects = [];
|
||||
$scope.searchQuery = '';
|
||||
$scope.isSearching = false;
|
||||
|
||||
/**
|
||||
* The search method.
|
||||
*/
|
||||
$scope.search = function () {
|
||||
// Clear the scope and set the progress flag.
|
||||
$scope.error = {};
|
||||
$scope.isSearching = true;
|
||||
$scope.projects = [];
|
||||
|
||||
// Execute the project query.
|
||||
Project.search(
|
||||
// Enable this once the API's there, mocks don't support
|
||||
// searches yet
|
||||
{/* q: $scope.searchQuery || '' */},
|
||||
function (result) {
|
||||
// Successful search results, apply the results to the
|
||||
// scope and unset our progress flag.
|
||||
$scope.projects = result;
|
||||
$scope.isSearching = false;
|
||||
},
|
||||
function (error) {
|
||||
// Error search results, show the error in the UI and
|
||||
// unset our progress flag.
|
||||
$scope.error = error;
|
||||
$scope.isSearching = false;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// Initialize the view with a default search.
|
||||
$scope.search();
|
||||
});
|
||||
|
54
src/app/projects/controllers/project_new_controller.js
Normal file
54
src/app/projects/controllers/project_new_controller.js
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* View controller for the new project form. Includes an intermediary 'saving'
|
||||
* flag as well as room for an error response (though until we get a real API
|
||||
* that'll be a bit tricky to test).
|
||||
*/
|
||||
angular.module('sb.projects').controller('ProjectNewController',
|
||||
function ($scope, $state, Project) {
|
||||
'use strict';
|
||||
|
||||
// View parameters.
|
||||
$scope.newProject = new Project();
|
||||
$scope.isCreating = false;
|
||||
$scope.error = {};
|
||||
|
||||
/**
|
||||
* Submits the newly created project. If an error response is received,
|
||||
* assigns it to the view and unsets various flags. The template
|
||||
* should know how to handle it.
|
||||
*/
|
||||
$scope.createProject = function () {
|
||||
|
||||
// Clear everything and set the progress flag...
|
||||
$scope.isCreating = true;
|
||||
$scope.error = {};
|
||||
|
||||
$scope.newProject.$create(
|
||||
function () {
|
||||
// Success!
|
||||
$state.go('project.list');
|
||||
},
|
||||
function (error) {
|
||||
// Error received. Ho hum.
|
||||
$scope.isCreating = false;
|
||||
$scope.error = error;
|
||||
}
|
||||
);
|
||||
};
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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
|
||||
@ -17,10 +17,8 @@
|
||||
/**
|
||||
* The Storyboard project submodule handles most activity surrounding the
|
||||
* creation and management of projects.
|
||||
*
|
||||
* @author Michael Krotscheck
|
||||
*/
|
||||
angular.module('sb.projects', ['ui.router', 'sb.services'])
|
||||
angular.module('sb.projects', ['ui.router', 'sb.services', 'sb.util'])
|
||||
.config(function ($stateProvider, $urlRouterProvider) {
|
||||
'use strict';
|
||||
|
||||
@ -36,6 +34,22 @@ angular.module('sb.projects', ['ui.router', 'sb.services'])
|
||||
})
|
||||
.state('project.list', {
|
||||
url: '/list',
|
||||
templateUrl: 'app/templates/project/provider.html'
|
||||
templateUrl: 'app/templates/project/list.html',
|
||||
controller: 'ProjectListController'
|
||||
})
|
||||
.state('project.edit', {
|
||||
url: '/{id:[0-9]+}/edit',
|
||||
templateUrl: 'app/templates/project/edit.html',
|
||||
controller: 'ProjectDetailController'
|
||||
})
|
||||
.state('project.detail', {
|
||||
url: '/{id:[0-9]+}',
|
||||
templateUrl: 'app/templates/project/detail.html',
|
||||
controller: 'ProjectDetailController'
|
||||
})
|
||||
.state('project.new', {
|
||||
url: '/new',
|
||||
templateUrl: 'app/templates/project/new.html',
|
||||
controller: 'ProjectNewController'
|
||||
});
|
||||
});
|
||||
|
@ -51,8 +51,9 @@ angular.module('sb.services')
|
||||
* Handle a fail response.
|
||||
*/
|
||||
responseError: function (response) {
|
||||
|
||||
if (!!response) {
|
||||
sendEvent(response.status, response.body);
|
||||
sendEvent(response.status, response.data);
|
||||
}
|
||||
|
||||
return $q.reject(response);
|
||||
|
@ -16,30 +16,44 @@
|
||||
|
||||
/**
|
||||
* Mock resource responses for the AuthProvider resource.
|
||||
*
|
||||
* @author Michael Krotscheck
|
||||
*/
|
||||
|
||||
angular.module('sb.services')
|
||||
.run(function ($httpBackend, $injector) {
|
||||
.run(function (mock) {
|
||||
'use strict';
|
||||
$httpBackend = $injector.get('$httpBackend');
|
||||
|
||||
var authProviders = [
|
||||
{
|
||||
'id': 1,
|
||||
'id': 0,
|
||||
'type': 'openid',
|
||||
'title': 'OpenID',
|
||||
'url': 'https://www.google.com/prediscovered' +
|
||||
'/redirection/url',
|
||||
'url': 'https://login.launchpad.net/+openid',
|
||||
'params': {
|
||||
'list': 'of',
|
||||
'additional': 'parameters',
|
||||
'required': 'for.this.provider'
|
||||
'openid.ns': 'http://specs.openid.net/auth/2.0',
|
||||
'openid.claimed_id': 'http://specs.openid.net/auth' +
|
||||
'/2.0/identifier_select',
|
||||
'openid.identity': 'http://specs.openid.net/auth' +
|
||||
'/2.0/identifier_select',
|
||||
// 'openid.return_to': 'https://review.openstack.org/
|
||||
// openid?gerrit.mode=SIGN_IN&gerrit.token=%2Fq%2Fstatus%3Aopen%2Cn%2Cz',
|
||||
// 'openid.realm': 'https://review.openstack.org/',
|
||||
'openid.assoc_handle': '{HMAC-SHA256}{52c79079}{z+v4vA==}',
|
||||
'openid.mode': 'checkid_setup',
|
||||
'openid.ns.sreg': 'http://openid.net/sreg/1.0',
|
||||
'openid.sreg.required': 'fullname,email',
|
||||
'openid.ns.ext2': 'http://openid.net/srv/ax/1.0',
|
||||
'openid.ext2.mode': 'fetch_request',
|
||||
'openid.ext2.type.FirstName': 'http://schema.openid.net/' +
|
||||
'namePerson/first',
|
||||
'openid.ext2.type.LastName': 'http://schema.openid.net/' +
|
||||
'namePerson/last',
|
||||
'openid.ext2.type.Email': 'http://schema.openid.net/' +
|
||||
'contact/email',
|
||||
'openid.ext2.required': 'FirstName,LastName,Email'
|
||||
}
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'id': 1,
|
||||
'type': 'openid_connect',
|
||||
'title': 'OpenID Connect',
|
||||
'url': 'https://www.google.com/prediscovered' +
|
||||
@ -51,7 +65,7 @@ angular.module('sb.services')
|
||||
}
|
||||
},
|
||||
{
|
||||
'id': 3,
|
||||
'id': 2,
|
||||
'type': 'ldap',
|
||||
'title': 'LDAP',
|
||||
'url': 'https://www.google.com/prediscovered' +
|
||||
@ -64,20 +78,9 @@ angular.module('sb.services')
|
||||
}
|
||||
];
|
||||
|
||||
$httpBackend.when('GET', '/api/v1/auth/provider')
|
||||
.respond(
|
||||
{
|
||||
total: 1,
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
results: authProviders
|
||||
}
|
||||
);
|
||||
mock.api('/api/v1/auth/provider',
|
||||
'/api/v1/auth/provider/:id',
|
||||
'id',
|
||||
authProviders);
|
||||
|
||||
$httpBackend.when('GET', '/api/v1/auth/provider/1')
|
||||
.respond(authProviders[0]);
|
||||
$httpBackend.when('GET', '/api/v1/auth/provider/2')
|
||||
.respond(authProviders[1]);
|
||||
$httpBackend.when('GET', '/api/v1/auth/provider/3')
|
||||
.respond(authProviders[2]);
|
||||
});
|
163
src/app/services/mock/mock_service.js
Normal file
163
src/app/services/mock/mock_service.js
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This service creates an automatic CRUD mock based on provided
|
||||
* urls and data sets.
|
||||
*
|
||||
* TODO(krotscheck): Once we have a real API, we can remove this.
|
||||
*/
|
||||
|
||||
angular.module('sb.services')
|
||||
.factory('mock', function ($log, $urlMatcherFactory, $httpBackend) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* URL matcher factory generator, used for testing API urls for
|
||||
* mocking.
|
||||
*
|
||||
* @param testUrl
|
||||
*/
|
||||
function matchUrl(testUrl) {
|
||||
var urlMatcher = $urlMatcherFactory.compile(testUrl);
|
||||
|
||||
return {
|
||||
test: function (url) {
|
||||
return urlMatcher.exec(url);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that extracts the array index from the default data
|
||||
* passed. Necessary because to simulate our mock list, we're splicing
|
||||
* all the time, so the actual indexes of the array may not match the
|
||||
* ID's of the items therein.
|
||||
*/
|
||||
function getIndexById(defaultData, idParamName, id) {
|
||||
for (var i = 0; i < defaultData.length; i++) {
|
||||
var item = defaultData[i];
|
||||
if (item[idParamName] === id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* This method mocks an entire RESTful api endpoint.
|
||||
*/
|
||||
api: function (searchUrl, crudUrl, crudIdParamName, defaultData) {
|
||||
this.search(searchUrl, defaultData);
|
||||
this.crud(searchUrl, crudUrl, crudIdParamName, defaultData);
|
||||
},
|
||||
|
||||
/**
|
||||
* This method creates a mock search service for the passed URL and
|
||||
* the provided data hash.
|
||||
*/
|
||||
search: function (searchUrl, defaultData) {
|
||||
$httpBackend.when('GET', matchUrl(searchUrl))
|
||||
.respond(function (method, url) {
|
||||
$log.info('[mock] ' + method + ' ' + url);
|
||||
return [200, {
|
||||
total: defaultData.length,
|
||||
offset: 0,
|
||||
limit: defaultData.length,
|
||||
results: defaultData
|
||||
}];
|
||||
}
|
||||
);
|
||||
},
|
||||
/**
|
||||
* This method creates a mock CRUD service for the passed URL,
|
||||
* ID parameter name, and data hash
|
||||
*/
|
||||
crud: function (createUrl, crudUrl, idParamName, defaultData) {
|
||||
var crudMatcher = matchUrl(crudUrl);
|
||||
var createMatcher = matchUrl(createUrl);
|
||||
|
||||
/**
|
||||
* Mock responder for a POST action. Extracts the ID from the
|
||||
* last item in our default data array and increments it, then
|
||||
* adds another item with that same ID.
|
||||
*/
|
||||
var createResolver = function (method, url, body) {
|
||||
$log.info('[mock] ' + method + ' ' + url);
|
||||
|
||||
body = JSON.parse(body);
|
||||
var now = Math.round(new Date().getTime() / 1000);
|
||||
body.id = defaultData[defaultData.length - 1].id + 1;
|
||||
// jshint -W106
|
||||
body.created_at = now;
|
||||
body.updated_at = now;
|
||||
// jshint +W106
|
||||
defaultData[body.id] = body;
|
||||
console.warn(defaultData);
|
||||
return [201, body];
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock responder for Get/Update/Delete. Given an existing ID,
|
||||
* extracts the data from that location and either just sends
|
||||
* it back, or manipulates it as requested.
|
||||
*/
|
||||
var rudResolver = function (method, url, body) {
|
||||
$log.info('[mock] ' + method + ' ' + url);
|
||||
|
||||
if (!!body) {
|
||||
body = JSON.parse(body);
|
||||
}
|
||||
|
||||
var id = parseInt(crudMatcher.test(url).id);
|
||||
var idx = getIndexById(defaultData, idParamName, id);
|
||||
var now = Math.round(new Date().getTime() / 1000);
|
||||
|
||||
if (idx === false) {
|
||||
return [404];
|
||||
}
|
||||
|
||||
// Temporarily disable the camelcase JSHint rule.
|
||||
// jshint -W106
|
||||
switch (method) {
|
||||
case 'GET':
|
||||
return [200, defaultData[idx]];
|
||||
case 'PUT':
|
||||
body.id = id;
|
||||
body.updated_at = now;
|
||||
defaultData[idx] = body;
|
||||
return [200, defaultData[idx]];
|
||||
case 'DELETE':
|
||||
defaultData.splice(idx, 1);
|
||||
return [200];
|
||||
}
|
||||
// Re-enable camelcase check.
|
||||
// jshint +W106
|
||||
};
|
||||
|
||||
$httpBackend.when('POST', createMatcher)
|
||||
.respond(createResolver);
|
||||
$httpBackend.when('GET', crudMatcher)
|
||||
.respond(rudResolver);
|
||||
$httpBackend.when('PUT', crudMatcher)
|
||||
.respond(rudResolver);
|
||||
$httpBackend.when('DELETE', crudMatcher)
|
||||
.respond(rudResolver);
|
||||
}
|
||||
};
|
||||
});
|
58
src/app/services/mock/project_mock.js
Normal file
58
src/app/services/mock/project_mock.js
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mock resource responses for the Project resource.
|
||||
*/
|
||||
|
||||
angular.module('sb.services')
|
||||
.run(function (mock) {
|
||||
'use strict';
|
||||
|
||||
var projects = [
|
||||
{
|
||||
'id': 0,
|
||||
'created_at': 12000000,
|
||||
'updated_at': 12000000,
|
||||
'name': 'Test Project 1',
|
||||
'description': 'Let\'s make orange juice',
|
||||
'team_id': null
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'created_at': 12000000,
|
||||
'updated_at': 12000000,
|
||||
'name': 'Test Project 2',
|
||||
'description': 'Let\'s make apple juice',
|
||||
'team_id': null
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'created_at': 12000000,
|
||||
'updated_at': 12000000,
|
||||
'name': 'Test Project 3',
|
||||
'description': 'Let\'s make lemonade',
|
||||
'team_id': null
|
||||
}
|
||||
];
|
||||
|
||||
mock.api('/api/v1/projects',
|
||||
'/api/v1/projects/:id',
|
||||
'id',
|
||||
projects);
|
||||
}
|
||||
)
|
||||
;
|
34
src/app/services/mock/task_mock.js
Normal file
34
src/app/services/mock/task_mock.js
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mock resource responses for the Task resource.
|
||||
*/
|
||||
|
||||
angular.module('sb.services')
|
||||
.run(function (mock) {
|
||||
'use strict';
|
||||
|
||||
var tasks = [
|
||||
];
|
||||
|
||||
mock.api('/api/v1/tasks',
|
||||
'/api/v1/tasks/:id',
|
||||
'id',
|
||||
tasks);
|
||||
}
|
||||
)
|
||||
;
|
34
src/app/services/mock/team.js
Normal file
34
src/app/services/mock/team.js
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mock resource responses for the Team resource.
|
||||
*/
|
||||
|
||||
angular.module('sb.services')
|
||||
.run(function (mock) {
|
||||
'use strict';
|
||||
|
||||
var team = [
|
||||
];
|
||||
|
||||
mock.api('/api/v1/teams',
|
||||
'/api/v1/teams/:id',
|
||||
'id',
|
||||
team);
|
||||
}
|
||||
)
|
||||
;
|
34
src/app/services/mock/user_mock.js
Normal file
34
src/app/services/mock/user_mock.js
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mock resource responses for the User resource.
|
||||
*/
|
||||
|
||||
angular.module('sb.services')
|
||||
.run(function (mock) {
|
||||
'use strict';
|
||||
|
||||
var users = [
|
||||
];
|
||||
|
||||
mock.api('/api/v1/users',
|
||||
'/api/v1/users/:id',
|
||||
'id',
|
||||
users);
|
||||
}
|
||||
)
|
||||
;
|
@ -21,4 +21,5 @@
|
||||
*
|
||||
* @author Michael Krotscheck
|
||||
*/
|
||||
angular.module('sb.services', ['ngResource', 'ngCookies', 'ngMockE2E']);
|
||||
angular.module('sb.services', ['ngResource', 'ngCookies', 'ngMock',
|
||||
'ui.router']);
|
54
src/app/services/provider/storyboard_api_signature.js
Normal file
54
src/app/services/provider/storyboard_api_signature.js
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* In lieu of extension, here we're injecting our common API signature that
|
||||
* can be reused by all of our services.
|
||||
*
|
||||
* @author Michael Krotscheck
|
||||
*/
|
||||
angular.module('sb.services')
|
||||
.factory('storyboardApiSignature', function () {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
'create': {
|
||||
method: 'POST'
|
||||
},
|
||||
'read': {
|
||||
method: 'GET',
|
||||
cache: false
|
||||
},
|
||||
'update': {
|
||||
method: 'PUT'
|
||||
},
|
||||
'delete': {
|
||||
method: 'DELETE'
|
||||
},
|
||||
'search': {
|
||||
method: 'GET',
|
||||
isArray: true,
|
||||
transformResponse: function (data) {
|
||||
if (data.error) {
|
||||
return data;
|
||||
} else {
|
||||
return data.results;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
@ -24,35 +24,10 @@
|
||||
*/
|
||||
|
||||
angular.module('sb.services').factory('AuthProvider',
|
||||
function ($resource, storyboardApiBase) {
|
||||
function ($resource, storyboardApiBase, storyboardApiSignature) {
|
||||
'use strict';
|
||||
|
||||
return $resource(storyboardApiBase + '/auth/provider/:id',
|
||||
{id: '@id'},
|
||||
{
|
||||
'create': {
|
||||
method: 'POST'
|
||||
},
|
||||
'get': {
|
||||
method: 'GET',
|
||||
cache: true
|
||||
},
|
||||
'save': {
|
||||
method: 'PUT'
|
||||
},
|
||||
'delete': {
|
||||
method: 'DELETE'
|
||||
},
|
||||
'query': {
|
||||
method: 'GET',
|
||||
isArray: true,
|
||||
transformResponse: function (data) {
|
||||
if (data.error) {
|
||||
return data;
|
||||
} else {
|
||||
return data.results;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
storyboardApiSignature);
|
||||
});
|
31
src/app/services/resource/project.js
Normal file
31
src/app/services/resource/project.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The angular resource abstraction that allows us to access projects and their
|
||||
* details.
|
||||
*
|
||||
* @see storyboardApiSignature
|
||||
* @author Michael Krotscheck
|
||||
*/
|
||||
angular.module('sb.services').factory('Project',
|
||||
function ($resource, storyboardApiBase, storyboardApiSignature) {
|
||||
'use strict';
|
||||
|
||||
return $resource(storyboardApiBase + '/projects/:id',
|
||||
{id: '@id'},
|
||||
storyboardApiSignature);
|
||||
});
|
50
src/app/services/resource/project_group.js
Normal file
50
src/app/services/resource/project_group.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The angular resource abstraction that allows us to access projects groups.
|
||||
*
|
||||
* @see storyboardApiSignature
|
||||
* @author Michael Krotscheck
|
||||
*/
|
||||
angular.module('sb.services').factory('ProjectGroup',
|
||||
function ($resource, storyboardApiBase, storyboardApiSignature) {
|
||||
'use strict';
|
||||
|
||||
return $resource(storyboardApiBase + '/project_groups/:id',
|
||||
{id: '@id'},
|
||||
storyboardApiSignature);
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
This is initial commit adding pecan/wsme framework.
|
||||
Example operations are:
|
||||
* GET /v1/project_groups
|
||||
* GET /v1/project_groups/<group_name>
|
||||
|
||||
* GET /v1/projects
|
||||
* GET /v1/projects/<project_name>
|
||||
|
||||
* GET /v1/teams
|
||||
* GET /v1/teams/<team_name>
|
||||
* POST /v1/teams
|
||||
|
||||
* GET /v1/users
|
||||
* GET /v1/users/<username>
|
||||
* POST /v1/users
|
||||
* PUT /v1/users/<username>
|
||||
*/
|
30
src/app/services/resource/task.js
Normal file
30
src/app/services/resource/task.js
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The angular resource abstraction that allows us to create and modify tasks.
|
||||
*
|
||||
* @see storyboardApiSignature
|
||||
* @author Michael Krotscheck
|
||||
*/
|
||||
angular.module('sb.services').factory('Task',
|
||||
function ($resource, storyboardApiBase, storyboardApiSignature) {
|
||||
'use strict';
|
||||
|
||||
return $resource(storyboardApiBase + '/tasks/:id',
|
||||
{id: '@id'},
|
||||
storyboardApiSignature);
|
||||
});
|
31
src/app/services/resource/team.js
Normal file
31
src/app/services/resource/team.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The angular resource abstraction that allows us to access teams and their
|
||||
* details.
|
||||
*
|
||||
* @see storyboardApiSignature
|
||||
* @author Michael Krotscheck
|
||||
*/
|
||||
angular.module('sb.services').factory('Team',
|
||||
function ($resource, storyboardApiBase, storyboardApiSignature) {
|
||||
'use strict';
|
||||
|
||||
return $resource(storyboardApiBase + '/teams/:id',
|
||||
{id: '@id'},
|
||||
storyboardApiSignature);
|
||||
});
|
31
src/app/services/resource/user.js
Normal file
31
src/app/services/resource/user.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The angular resource abstraction that allows us to search, access, and
|
||||
* modify users.
|
||||
*
|
||||
* @see storyboardApiSignature
|
||||
* @author Michael Krotscheck
|
||||
*/
|
||||
angular.module('sb.services').factory('User',
|
||||
function ($resource, storyboardApiBase, storyboardApiSignature) {
|
||||
'use strict';
|
||||
|
||||
return $resource(storyboardApiBase + '/users/:id',
|
||||
{id: '@id'},
|
||||
storyboardApiSignature);
|
||||
});
|
@ -18,6 +18,10 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h1>Login with {{authProvider.title}}</h1>
|
||||
<p class="lead">
|
||||
This feature requires the existence of a functioning API
|
||||
Authentication layer, and is therefore disabled.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
38
src/app/templates/project/detail.html
Normal file
38
src/app/templates/project/detail.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!--
|
||||
~ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
<div class="container" ng-show="isLoading">
|
||||
<div class="col-xs-12">
|
||||
<p class="text-center">
|
||||
<i class="fa fa-refresh fa-spin"></i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container" ng-hide="isLoading">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h1>{{project.name}}</h1>
|
||||
|
||||
<p>{{project.description}}</p>
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
Project Detail List TBD.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
84
src/app/templates/project/edit.html
Normal file
84
src/app/templates/project/edit.html
Normal file
@ -0,0 +1,84 @@
|
||||
<!--
|
||||
~ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
<div class="container" ng-show="isLoading">
|
||||
<div class="col-xs-12">
|
||||
<p class="text-center">
|
||||
<i class="fa fa-refresh fa-2x fa-spin"></i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container" ng-hide="isLoading">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h1>Project: {{project.name}}</h1>
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<form class="form-horizontal" role="form" name="projectForm">
|
||||
<div class="form-group">
|
||||
<label for="name" class="col-sm-2 control-label">
|
||||
Project Name:
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<input id="name"
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="project.name"
|
||||
required
|
||||
placeholder="Project Name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description"
|
||||
class="col-sm-2 control-label">
|
||||
Project Description
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea id="description"
|
||||
class="form-control"
|
||||
ng-model="project.description"
|
||||
required
|
||||
placeholder="A brief project description">
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="button"
|
||||
ng-click="update()"
|
||||
class="btn btn-primary"
|
||||
ng-disabled="!projectForm.$valid">
|
||||
Save Changes
|
||||
</button>
|
||||
<button ng-click="remove()"
|
||||
class="btn btn-danger">
|
||||
Delete Project
|
||||
</button>
|
||||
<a href="#!/project/list"
|
||||
class="btn btn-default">
|
||||
Cancel
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -16,28 +16,67 @@
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
|
||||
<div class="well sidebar-nav">
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">Projects</li>
|
||||
<li class="disabled"><a href="#">Search projects</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<!--/.well -->
|
||||
|
||||
<div class="col-sm-8 col-md-9">
|
||||
<h3 class="no-margin">
|
||||
<a href="#!/project/new" class="btn btn-default">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
Projects
|
||||
</h3>
|
||||
<br class="visible-xs"/>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<h3>Projects</h3>
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Title</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<div class="col-sm-4 col-md-3">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control"
|
||||
placeholder="Search Projects"
|
||||
ng-disabled="isSearching"
|
||||
ng-enter="search()"
|
||||
ng-model="searchQuery"/>
|
||||
<span class="input-group-btn">
|
||||
<button type="button" ng-click="search()"
|
||||
ng-disabled="isSearching"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-refresh fa-spin"
|
||||
ng-show="isSearching"></i>
|
||||
<i class="fa fa-search"
|
||||
ng-hide="isSearching"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div ng-show="isSearching">
|
||||
<hr/>
|
||||
<p class="text-center">
|
||||
<i class="fa fa-refresh fa-spin fa-lg"></i>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped table-hover table-responsive"
|
||||
ng-hide="isSearching">
|
||||
<tbody>
|
||||
<tr ng-repeat="project in projects">
|
||||
<td>
|
||||
<div class="pull-right">
|
||||
<a href="#!/project/{{project.id}}/edit">
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
</div>
|
||||
<a href="#!/project/{{project.id}}">
|
||||
<strong>{{project.name}}</strong>
|
||||
</a>
|
||||
<br/>
|
||||
|
||||
{{project.description}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
73
src/app/templates/project/new.html
Normal file
73
src/app/templates/project/new.html
Normal file
@ -0,0 +1,73 @@
|
||||
<!--
|
||||
~ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h1>Create a new project</h1>
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<form class="form-horizontal" role="form" name="projectForm">
|
||||
<div class="form-group">
|
||||
<label for="name" class="col-sm-2 control-label">
|
||||
Project Name:
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<input id="name"
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="newProject.name"
|
||||
required
|
||||
placeholder="Project Name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description"
|
||||
class="col-sm-2 control-label">
|
||||
Project Description
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea id="description"
|
||||
class="form-control"
|
||||
ng-model="newProject.description"
|
||||
required
|
||||
placeholder="A brief project description">
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="button"
|
||||
ng-click="createProject()"
|
||||
class="btn btn-primary"
|
||||
ng-disabled="!projectForm.$valid">
|
||||
Create project
|
||||
</button>
|
||||
<a href="#!/project/list"
|
||||
class="btn btn-default">
|
||||
Cancel
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
24
src/styles/bootstrap_addons.less
vendored
Normal file
24
src/styles/bootstrap_addons.less
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generic overrides and addons for bootstrap.
|
||||
*/
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
&.no-margin {
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
@import './font-awesome.less';
|
||||
// Custom variable overrides
|
||||
@import './bootstrap_variable_overrides.less';
|
||||
@import './bootstrap_addons.less';
|
||||
// Add our own custom icon font.
|
||||
@import './custom_font_icons.less';
|
||||
// Module specific styles
|
||||
|
@ -25,6 +25,5 @@ describe('Storyboard Login Routes', function () {
|
||||
it('should redirect /auth/provider to /auth/provider/list', function () {
|
||||
browser.get('http://localhost:9000/#!/auth/provider');
|
||||
expect(browser.getCurrentUrl()).toContain('#!/auth/provider/list');
|
||||
|
||||
});
|
||||
});
|
||||
|
86
test/unit/services/http/http_error_broadcaster_test.js
Normal file
86
test/unit/services/http/http_error_broadcaster_test.js
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This test suite ensures that HTTP response codes are successfully captured
|
||||
* and broadcast to the system
|
||||
*/
|
||||
describe('httpErrorBroadcaster', function () {
|
||||
'use strict';
|
||||
|
||||
var $rootScope, $httpBackend, $http, $resource, MockResource,
|
||||
storyboardApiBase;
|
||||
|
||||
var errorResponse = {
|
||||
error_code: 404,
|
||||
error_message: 'This is an error message'
|
||||
};
|
||||
|
||||
// Setup
|
||||
beforeEach(function () {
|
||||
// Load the module under test
|
||||
module('sb.services');
|
||||
|
||||
inject(function ($injector) {
|
||||
// Capture various providers for later use.
|
||||
$rootScope = $injector.get('$rootScope');
|
||||
$http = $injector.get('$http');
|
||||
$httpBackend = $injector.get('$httpBackend');
|
||||
$resource = $injector.get('$resource');
|
||||
MockResource = $resource('/foo/:id', {id: '@id'});
|
||||
storyboardApiBase = $injector.get('storyboardApiBase');
|
||||
});
|
||||
|
||||
// Start listening to the broadcast method.
|
||||
spyOn($rootScope, '$broadcast');
|
||||
});
|
||||
|
||||
// Teardown
|
||||
afterEach(function () {
|
||||
$httpBackend.verifyNoOutstandingExpectation();
|
||||
$httpBackend.verifyNoOutstandingRequest();
|
||||
});
|
||||
|
||||
|
||||
it('should capture events on the $rootScope', function () {
|
||||
|
||||
$httpBackend.when('GET', '/foo/99')
|
||||
.respond(553, JSON.stringify(errorResponse));
|
||||
|
||||
var complete = false;
|
||||
|
||||
runs(function () {
|
||||
MockResource.get({'id': 99},
|
||||
function () {
|
||||
complete = true;
|
||||
},
|
||||
function () {
|
||||
complete = true;
|
||||
});
|
||||
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return complete;
|
||||
}, 'query to complete', 5000);
|
||||
|
||||
runs(function () {
|
||||
expect($rootScope.$broadcast)
|
||||
.toHaveBeenCalledWith('http_553', errorResponse);
|
||||
});
|
||||
});
|
||||
});
|
59
test/unit/services/provider/storyboard_api_base_test.js
Normal file
59
test/unit/services/provider/storyboard_api_base_test.js
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This test suite verifies that our API base URI is detected and deferred
|
||||
* as expected.
|
||||
*/
|
||||
describe('storyboardApiBase', function () {
|
||||
'use strict';
|
||||
|
||||
it('should default to /api/v1', function () {
|
||||
module('sb.services');
|
||||
|
||||
inject(function (storyboardApiBase) {
|
||||
expect(storyboardApiBase).toEqual('/api/v1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should detect a value in window.ENV', function () {
|
||||
window.ENV = {
|
||||
storyboardApiBase: 'https://localhost:8080/api/v1'
|
||||
};
|
||||
|
||||
module('sb.services');
|
||||
|
||||
inject(function (storyboardApiBase) {
|
||||
expect(storyboardApiBase).toEqual('https://localhost:8080/api/v1');
|
||||
});
|
||||
|
||||
delete window.ENV;
|
||||
});
|
||||
|
||||
it('should defer to properties injected at the parent level.', function () {
|
||||
angular.module('testModule', ['sb.services'])
|
||||
.config(function ($provide) {
|
||||
$provide.constant('storyboardApiBase', 'spam.eggs.com');
|
||||
});
|
||||
|
||||
module('testModule');
|
||||
|
||||
inject(function (storyboardApiBase) {
|
||||
expect(storyboardApiBase).toEqual('spam.eggs.com');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
93
test/unit/services/provider/storyboard_api_signature_test.js
Normal file
93
test/unit/services/provider/storyboard_api_signature_test.js
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This test suite verifies that our default API request signature is
|
||||
* sane.
|
||||
*/
|
||||
describe('storyboardApiSignature', function () {
|
||||
'use strict';
|
||||
|
||||
beforeEach(module('sb.services'));
|
||||
|
||||
it('should exist', function () {
|
||||
inject(function (storyboardApiSignature) {
|
||||
expect(storyboardApiSignature).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should declare CRUD methods', function () {
|
||||
inject(function (storyboardApiSignature) {
|
||||
expect(storyboardApiSignature.create).toBeTruthy();
|
||||
expect(storyboardApiSignature.read).toBeTruthy();
|
||||
expect(storyboardApiSignature.update).toBeTruthy();
|
||||
expect(storyboardApiSignature.delete).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should declare a search method', function () {
|
||||
inject(function (storyboardApiSignature) {
|
||||
expect(storyboardApiSignature.search).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should use POST to create', function () {
|
||||
inject(function (storyboardApiSignature) {
|
||||
expect(storyboardApiSignature.create).toBeTruthy();
|
||||
expect(storyboardApiSignature.create.method).toEqual('POST');
|
||||
});
|
||||
});
|
||||
it('should use GET to read', function () {
|
||||
inject(function (storyboardApiSignature) {
|
||||
expect(storyboardApiSignature.read).toBeTruthy();
|
||||
expect(storyboardApiSignature.read.method).toEqual('GET');
|
||||
});
|
||||
});
|
||||
it('should use PUT to update', function () {
|
||||
inject(function (storyboardApiSignature) {
|
||||
expect(storyboardApiSignature.update).toBeTruthy();
|
||||
expect(storyboardApiSignature.update.method).toEqual('PUT');
|
||||
});
|
||||
});
|
||||
it('should use DELETE to delete', function () {
|
||||
inject(function (storyboardApiSignature) {
|
||||
expect(storyboardApiSignature.delete).toBeTruthy();
|
||||
expect(storyboardApiSignature.delete.method).toEqual('DELETE');
|
||||
});
|
||||
});
|
||||
it('should use GET to search', function () {
|
||||
inject(function (storyboardApiSignature) {
|
||||
expect(storyboardApiSignature.search).toBeTruthy();
|
||||
expect(storyboardApiSignature.search.method).toEqual('GET');
|
||||
});
|
||||
});
|
||||
|
||||
it('should properly construct a resource', function () {
|
||||
inject(function (storyboardApiSignature, $resource) {
|
||||
|
||||
var Resource = $resource('/path/:id',
|
||||
{id: '@id'},
|
||||
storyboardApiSignature);
|
||||
expect(Resource.search).toBeTruthy();
|
||||
expect(Resource.read).toBeTruthy();
|
||||
|
||||
var resourceInstance = new Resource();
|
||||
expect(resourceInstance.$create).toBeTruthy();
|
||||
expect(resourceInstance.$update).toBeTruthy();
|
||||
expect(resourceInstance.$delete).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user