ah-sdk-angular v0.1.3
ah-sdk-angular
Tool for auto-generating Angular $http services for Actionhero
The services are generated based entirely on the actionhero routing. (See Generation Details below)
Latest Changes
From version 0.1.2 to 0.1.3
Added the storagePrefix
option. Changed the ahAuth
service from a factory to a provider to allow
code manipulation of the storagePrefix
and tokenPrefix
options.
From version 0.1.1 to 0.1.2
Changed the grunt task to a multi task, allowing you to specify targets with unique options.
See the Install
section. You will no longer be able to send options directly to the grunt task.
From version 0.0.12 to 0.1.0
The generated clear cache methods have been updated in a manner that is not backwards compatible if you were sending in a cache object. The cache object now must be sent as the second argument with any GET parameters sent in an object as the first argument (this matches how other generated get methods worked already). Any clearCache* methods that were being called without arguments will not need to be updated and will not break. Any such methods that were sent non-cache object arguments may need to be updated.
Getting Started
The included grunt task requires Grunt ~0.4.2
Important!
It is required to update your settings under config.servers.web.httpHeaders['Access-Control-Allow-Headers']
to include Authorization
:
{
...
'Access-Control-Allow-Headers': 'Content-Type,Authorization'
}
It is recommended that you set config.servers.web.simpleRouting
to false
to avoid superfluous
route generation. It is not necessary though since methods are only generated from the routes config file.
{
...
'simpleRouting': false,
}
Both of these settings can be found in your actionhero/config/servers/web.js config file.
Install
If you haven't used Grunt before, be sure to check out the Getting Started guide, as it explains how to create a Gruntfile as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:
npm install ah-sdk-angular --save-dev
Once the plugin has been installed, it can be enabled inside your Gruntfile with these lines of JavaScript:
grunt.loadNpmTasks('ah-sdk-angular');
grunt.initConfig({
actionheroSDKAngular: {
client: {
options: {
output: 'directory/to/output/files/',
// Add extra options here.
}
},
// Example admin target.
admin: {
options: {
output: 'directory/to/output/files/',
filter: {
whitelist: ['admin']
}
// Add extra options here.
}
}
}
});
grunt.registerTask('angularSDK', ['actionheroSDKAngular:client']);
grunt.registerTask('adminSDK', ['actionheroSDKAngular:admin']);
Tasks and configuration
To run the grunt task
grunt actionheroSDKAngular
Options
Options available when generating the services file(s).
output
Type: string
*Required
The path to the directory in which generate the file(s). If serviceOutput is set, only the Angular module file will go here.
serviceOutput
Type: string|null
*Optional
Default: null
The path to the directory in which to generate the Angular service/factory files. Skipped if singleFile is true.
version
Type: String
*Optional
Default: 'latest'
The action version to use (can be either 'latest' or a number like 1.0 or 2.3). If the given version cannot be found, the latest version is used.
singleFile
Type: Boolean
*Optional
Default: false
If true, will generate a single file instead of one for each model.
wrap
Type: Boolean
*Optional
Default: false
If true, each file will be wrapped in a javascript function wrapper.
(function(window, angular, undefined) {
// GENERATED CODE HERE
})(window, window.angular);
modelPrepend
Type: String
*Optional
Default: null
A string that is prepended to each angular model name. If this option is set, the actual model will be upperCamelCased instead of lowerCamelCased, though the overall name will still be lowerCamelCased. For example: If set to 'sv', an 'auth' model will become 'svAuth' instead of 'auth'. This option cannot be set to 'ah' as this would cause conflicts with the other SDK services.
tokenPrepend
Type: String
*Optional
Default: null
A string that is prepended to your access token before it is sent for authentication.
For example, if you are using the passport-http-bearer
package, you will need to set
this to 'Bearer '.
storagePrefix
Type: String
*Optional
Default: null
A string that is prepended to the local/session storage when storing the access token. This can be used if you are generating an admin section of your site (for example) that needs different login credentials.
filter.whitelist
Type: Array.<string>
*Optional
Default: []
A list of keywords that must be defined in the sdkKeywords
option of the actionTemplate in order
for this action to be generated as a method in the model service.
The blacklist takes precedent over the whitelist.
filter: {
whitelist: ['admin']
}
// Will generate this action.
sdkKeywords: ['admin', 'expensive']
// Will not generate this action.
sdkKeywords: ['client']
filter.blacklist
Type: Array.<string>
*Optional
Default: []
A list of keywords that CAN NOT be defined in the sdkKeywords
option of the actionTemplate in order
for this action to be generated as a method in the model service.
The blacklist takes precedent over the whitelist.
filter: {
blacklist: ['admin']
}
// Will generate this action.
sdkKeywords: ['client', 'expensive']
// Will not generate this action.
sdkKeywords: ['client', 'admin']
moduleName
Type: String
*Optional
Default: 'ahServices'
The name of the generated angular module.
skipHttpGen
Type: Boolean
*Optional
Default: false
If true, the ahHttp
factory service will not be generated but still be used. This allows (and requires)
the user to write their own angular $http factory wrapper to suit their needs. This factory must return a
function that takes two config arguments and returns the results in whatever format you choose. The
first config argument is the generated $http config options, the second config argument are the
optional $http config options given by the user from the angular app.
// Example of a custom ahHttp factory (same as the generated one).
angular.module('myApp').factory('ahHttp', [ '$http', function ahHttpFactory($http) {
return function AHHttp(mainConfig, altConfig) {
if (!altConfig || typeof altConfig !== 'object') {
altConfig = {};
}
for (var i in mainConfig) {
if (mainConfig.hasOwnProperty(i)) {
altConfig[i] = mainConfig[i];
}
}
return $http(altConfig).then(function (response) {
return response.data;
});
};
}]);
skipUrlGen
Type: Boolean
*Optional
Default: false
If true, the apiUrlBase
constant will not be generated but still used. This allows (and requires)
the user to define their own apiUrlBase
constant elsewhere in the code.
addModuleDependency
Type: Array.<String>
*Optional
Default: null
Adds module names as dependencies of the generated module. This can be used when skipHttpGen
and/or
skipUrlGen
are used.
Generation Details
The tool scans the routes file and uses the paths to define models.
These models are defined using the first directory from the URL path
param or a defined sdkModel
param for each route.
The actions/methods defined for each model is taken from the second directory from the path
param or a defined sdkName
param.
You can also provide an optional sdkRoute
param that mimics the path
param but will be used instead of path
(sdkName
and sdkModel
will still override this.)
Example config/routes.js file:
{
get: [
// Auth Routes
{ path: '/auth/logout', action: 'authLogout', sdkName: 'logout' },
// User Routes
{ path: '/users/getPrivateData/:id', action: 'userGetPrivateData' }
],
post: [
// Auth Routes
{ path: '/auth/login', action: 'authLogin', sdkName: 'login', sdkModel: 'auth' },
// User Routes
{ path: '/users', action: 'userCreate', sdkName: 'create' }
]
}
In this example, 2 models will be created: Auth and Users. The Auth model will contain a login and logout method. The User model will contain a create and getPrivateData method.
Note that the http verbs are preserved in the $http calls, so Auth.login will do a POST call and Auth.logout will do a GET call.
Any routes under the all
verb will be ignored when generating the sdk.
(Note: If no sdkName
is given and no second folder exists, such as with the userCreate action above,
then a name will be generated based on the verb. Get=>find, post=>create, put=>update, delete=>delete, patch=>patch.)
The parameters for the generated methods will come from the actionhero action file's inputs.required and inputs.optional settings.
// If the authLogin action is defined as such.
exports.authLogin = {
inputs: {
'required': ['email', 'password'],
'optional': ['ttl']
}
...
};
// A function declaration such as this is generated.
function login(email, password, ttl) {
...
}
These parameters can be given to the method as individual arguments or by sending an object as the first argument.
If the actionTemplate has the sdkSingleParam
option set to true, then only the second example will be available.
Auth.login(email, password);
Auth.login({'email': email, 'password': password);
Each function does a $http call and returns a simple promise
with the returned data (the normal header and other info is stripped).
Auth.login(email, password).then(function (data) {}, function (err) {});
A bonus method getUrls
is generated for each model that returns an object with the url for each
action. Be aware of any :params in each url that may need to be replaced. You can use ahRouteHelper.parseRoute
to fill in these params if you like.
angular.module('myApp').controller('userController', ['Users', 'ahRouteHelper', function (Users, ahRouteHelper) {
var url = Users.getUrls().getPrivateData; // Returns '/users/getPrivateData/:id'.
ahRouteHelper.parseRoute(url, {id: 32}).url; // Returns '/users/getPrivateData/32'.
}
Routes config options
These are options that can be set in the route definitions in your actionhero routes config file (actionhero/config/routes.js).
sdkModel
Type: string
*Optional
Default: Parsed from the path
or sdkPath
param (/sdkModel/sdkName/other/routing/:id).
The model name to use when generating a service for this route.
{ path: '/anything/login', action: 'authLogin', sdkName: 'login', sdkModel: 'auth' }
// Generates an Auth model.
sdkName
Type: string
*Optional
Default: Parsed from the path
or sdkPath
param (/sdkModel/sdkName/other/routing/:id).
The name to use when generating a method for this route.
{ path: '/auth/anything/:id', action: 'authLogin', sdkName: 'login', sdkModel: 'auth' }
// Generates an Auth.login method.
sdkRoute
Type: string
*Optional
Default: null
Overrides the path
param when parsing the model and action names.
The sdkModel
and sdkName
options override anything parsed from this option.
{ path: '/something/anything/:id', action: 'authLogin', sdkRoute: '/auth/login' }
// Generates an Auth.login method.
sdkKeywords
Type: Array.<string>
*Optional
Default: null
Adds a keyword to this specific route instead of the action. This can be used to filter routes to an action based on the routing parameters.
{ path: '/something/anything', action: 'createSomething' }
{ path: '/something/anything/:adminParam', action: 'createSomething', sdkKeywords: ['admin'] }
// Here is an example where the admin sdk will take a param for an action but the non-admin wont
// for the same action.
ActionTemplate options
These are options that can be set in the action template when you are defining your actionhero actions.
sdkKeywords
Type: {Array.<string>}
*Optional
Default: null
A list of keywords that can help define the action. These can be used to filter types of actions when generating the services.
sdkSingleParam
Type: boolean
*Optional
Default: false
If true, the inputs.required
and inputs.optional
params will not be listed out as arguments of the method.
Instead, only a single param will be available that accepts the key/value object of params.
sdkClearCacheMethod
Type: boolean
*Optional
Default: true
for get methods, false
otherwise.
Generates a clear cache method for this action if true.
Using the generated services
- Add the ahServices.js file (and any other generated files) to your Angular App.
<script src="scripts/ahServices.js"></script>
(other generated files here)
- Add ahServices as a dependency of your module.
angular.module('myApp', [
...
'ahServices'
]);
- Simply inject your new models into anywhere you need them.
angular.module('myApp').controller('authController', ['Auth', 'ahAuth', function (Auth, ahAuth) {
$scope.login = function () {
Auth.login({'email': email, 'password': password}).then(function loginSuccess(user) {
ahAuth.login(user.token, user.id, $scope.rememberMe);
console.log('Login Success', user);
}, function loginError(err) {
console.log('Login Error', err);
});
};
}]);
- Use the ahAuth service to track Authentication. When provided an access token and user id, all subsequent $http calls will have the Authentication header automatically set to your user access token.
angular.module('myApp').controller('testController', ['ahAuth', 'Users', function (ahAuth, Users) {
var someToken = '1234';
var someUserId = 1;
// This would have happened in the login, not in the same controller...
ahAuth.login(someToken, someUserId, true);
// We will need 'id' in our inputs.required in the userGetPrivateData action.
Users.getPrivateData({id: ahAuth.getUserId()}).then(function (myPrivateData) {
console.log(myPrivateData);
});
// This will do a $http GET call to '/users/getPrivateData/1' with 'Authentication: 1234' in the headers.
}]);
Note: You will have to generate your own accessTokens, I use the uid2 package available through npm.
npm install uid2 --save
- When you logout the user, call ahAuth.logout() to clear the current session.
Auth.logout(function logoutSuccess() {
ahAuth.logout();
});
- When a 401 Unauthorized status is send in a response, the 'ahAuth:Failed' event is broadcast to the $rootScope. It is up to you to handle that event. Here is an example of how to handle it:
angular.module('myApp').run(function unauthorizedHandler($rootScope, $location, ahAuth) {
$rootScope.$on('ahAuth:Failed', function () {
ahAuth.logout();
$location.path('/login');
});
})
- To override the angular $http config options when calling the SDK, send the first argument as an object of parameters and the second argument as an object of config options. Note that any options that are explicitly set by the SDK call (method, url, etc) cannot be overridden.
Users.getPrivateData({
id: 1
}, {
cache: true
}).then(...);
- To override the angular $http config defaults for all SDK calls, use angular.module(...).run() or .config().
angular.module('myApp').config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.xsrfCookieName = 'ahXSRF';
$httpProvider.defaults.withCredentials = true;
}]);
/* OR */
angular.module('myApp').run(['$http', function ($http) {
$http.defaults.xsrfCookieName = 'ahXSRF';
$http.defaults.withCredentials = true;
}]);
- To update the api url for production vs development you can use
grunt-ng-constant
to have grunt generate anapiUrlBase
constant in a newahServiceConstants
app. Make sure to setskipUrlGen
totrue
andaddModuleDependency
to['ahServiceConstants']
(or whatever you named your constants module) in your gruntfile.
// Example grunt initConfig.
grunt.initConfig({
actionheroSDKAngular: {
client: {
options: {
output: 'directory/to/output/files/',
skipUrlGen: true,
addModuleDependency: ['ahServiceConstants']
// Add extra options here.
}
}
}
}
- Check the docblocks in the generated code for more help if you need it.