/**
* A description
*
* @created August 27th, 2015
* @author Luke Chavers <luke@c2cschools.com>
* @updated September 01th, 2015
* @author Prashant Tyagi <prashanttyagi@zapbuild.com>
* @copyright 2015 C2C Schools, LLC. All rights reserved.
*/
"use strict";
var Aag = require( "c2cs-aag-client" );
var fs = require( "fs" );
var Promise = require( "bluebird" );
var _ = require( "underscore" );
var dir = require( "node-dir" );
var path = require( "path" );
var apiStack = [];
var aagConfig;
// Load AWS Settings
aagConfig = {
accessKeyId : process.env.AWS_ACCESS_KEY || "AKIAIRA267B3CP6IIGUA",
secretAccessKey : process.env.AWS_SECRET_KEY || "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",
region : process.env.AWS_REGION || "us-east-1"
//apiId : process.env.AWS_API_ID || "893szowwmb",
//apiRootResource : process.env.AWS_ROOT_RESOURCE || "u455nq7412"
};
function checkDirExist ( dir ) {
var stats = fs.lstatSync( dir );
if( stats.isDirectory() ) {
return true;
}
return false;
}
/**
* Entry Point For publish api's to AAG
*
* @param {Object} object
* @param {Object} context
* @param {function} callback This callback function is passed from different stages
* @returns {void}
*/
module.exports = function ( object, context, cb ) {
//validations
if( !object.api ){
cb( new Error( "API ID is required." ), true );
} else {
aagConfig.apiId = object.api;
}
if( !object.configRoot ){
object.configRoot = "apis/spec/";
context.out( "Error: configRoot is not defined, default is 'apis/spec' " + '\n', 'warn' );
}
if( !object.publish ){
object.publish = "v3";
context.out( "Error: Publish Stage is not defined, default is 'v3' " + '\n', 'warn' );
}
//APIs path
var spath = object.configRoot + object.publish;
var dirPath = path.join( context.dataDir, spath );
//Set config
Aag.aagConfig = aagConfig;
//Check if provided path exist
var exist = checkDirExist( dirPath );
if(!exist) {
cb( new Error( "Provided path doesn't exist: " + spath ), true );
}
//read all subdirectories
dir.subdirs( dirPath, function( err, subdirs ) {
if ( err ) {
cb( err, true );
}
//Fetch all api resources first and store them into stack
Aag.getApiResources( object.api ).then( function( res ) {
if( Array.isArray( res._embedded.item ) ) { //check if result is object or array
var j = 0;
_.each( res._embedded.item, function( value, index ){ //if array, means muliple records here
apiStack[ value.path ] = value.id; //add it in apiStack stack
j++;
if( j == res._embedded.item.length ) { //if end of loop
resourceLoop( object, context, cb ); //traverse through all resources
}
});
} else {
apiStack[ res._embedded.item.path ] = res._embedded.item.id; //only root resource exist, add it in apiStack stack
resourceLoop( object, context, cb ); //traverse through all resources
}
}).catch( function( err ) {
context.out( "Error: While getting all resources from given API ID : " + object.api + "\n", "error" );
cb( new Error( "Error: While getting all resources from given API ID : " + object.api ), true );
});
var i = 0;
var subdirCount = subdirs.length; //no. of subdirectories
//iterate through all sub directories
function resourceLoop( object, context, cb, i ) {
if( typeof i === "undefined" ) {
i = 0;
}
var configFile = subdirs[i] + "/endpoint-config.json";
var templateFile = subdirs[i] + "/response-mapping-template.txt";
var configData = "";
var templateData = "";
//check if config file exist
if ( fs.existsSync( configFile ) ) {
configData = JSON.parse( fs.readFileSync( configFile, "utf-8" ));
}
//check if template file exist
if ( fs.existsSync( templateFile ) ) {
templateData = fs.readFileSync( templateFile, "utf-8" );
}
//So now we have all data to enter an API point
if( i < subdirCount ) { //check if end of loop
if( configData ) { //if config data present
if( apiStack[configData.resourcePath] ) { //check if api endpoint exist
//ALready exist, remove it first
Aag.deleteResource( apiStack[configData.resourcePath] ).then( function( res ) {
//remove resource from apiStach array also
var index = apiStack.indexOf( apiStack[configData.resourcePath] );
if ( index > -1 ) {
apiStack.splice( index, 1 );
}
addApi( object, context, cb, configData, templateData ).then( function( res ) { // add api endpoint
context.out( "Endpoint added successfully: " + configData.resourcePath + "\n", "stdout" );
resourceLoop( object, context, cb, i+1 ); //move to next resource
}).catch( function( err ) {
context.out( "Endpoint insertion failed: " + configData.resourcePath + "\n " + err + "\n", "error" );
resourceLoop( object, context, cb, i+1 ); //move to next resource
});
});
} else {
//not exist, add new
addApi( object, context, cb, configData, templateData ).then( function( res ) { // add api endpoint
context.out( "Endpoint added successfully: " + configData.resourcePath + "\n", "stdout" );
resourceLoop( object, context, cb, i+1 ); //move to next resource
}).catch( function( err ) {
context.out( "Endpoint insertion failed: " + configData.resourcePath + "\n " + err + "\n", "error" );
resourceLoop( object, context, cb, i+1 ); //move to next resource
});
}
} else {
resourceLoop( object, context, cb, i+1 ); //move to next resource
}
} else {
//API Deployment at the end
Aag.createDeployment( object.publish ).then( function( res ) {
context.out( "\n" + "Deployment of API's on AWS API Gateway is successful " + "\n", "message" );
cb( null, true );
}).catch( function( err ) {
context.out( "\n" + "Deployment of API's on AWS API Gateway is failed: " + "\n " + err + "\n", "error" );
cb( err, true );
});
apiStack = [];
}
};
});
};
/*
* Get resource array
*
* Example : splitResource( "/SanctioningBody/{sanctioningBodyId}/SanctioningCategories" )
* Output :
* [
* "/SanctioningBody",
* "/SanctioningBody/{sanctioningBodyId}",
* "/SanctioningBody/{sanctioningBodyId}/SanctioningCategories"
* ]
*/
function splitResource( resource ) {
var arr = resource.split("/");
var resources = [];
for( var i = 1; i < arr.length; i++ ) {
if( i != 1 ) {
resources.push( resources[i-2] + "/" + arr[i] );
} else {
resources.push( "/" + arr[i] );
}
}
return resources;
}
/*
* Add all parent resources of provided resource, if not present
*
*/
function addAllResourcePoints( resources, count, i, callback ) {
if( typeof i === "undefined" ) {
i = 0;
}
if( i < count ) { //check if end of loop
if( !apiStack[resources[i]] ) {
var resourceId = "";
//Check for parent resource ID
if( i == 0 ) {
resourceId = apiStack['/']; //set root id as parent resourceId
} else {
resourceId = apiStack[resources[i-1]]; //set previous record id as parent resourceId
}
var last = resources[i].substring(resources[i].lastIndexOf("/") + 1, resources[i].length);
// 1) createResource( { "pathPart" : "test" }, "ID of parent resopurce" );
var a = Aag.createResource( { "pathPart" : last }, resourceId ).then( function(res){
//set that resource into stack
apiStack[resources[i]] = res.id;
addAllResourcePoints( resources, count, i+1, callback );
});
} else {
addAllResourcePoints( resources, count, i+1, callback );
}
} else {
//loop end
if( apiStack[resources[i-1]] ) { //pass resourceId of provided resource
callback( apiStack[resources[i-1]], false );
} else {
callback( "", new Error( "Resource Id not found." ) );
}
}
}
/*
* Add api endpoint
*
*
*/
function addApi( object, context, cb, configData, templateData ) {
var resourceId = apiStack["/"]; //set resourceId as root resourceId
var resource = configData.resourcePath;
return new Promise( function( resolve, reject ) {
var resources = splitResource(resource);
var count = resources.length;
//1) Add api resource and enter them into stack
addAllResourcePoints( resources, count, 0, function( res, err ){
if( err ) {
cb( err, true );
}
resourceId = res;
var requestData = {
"authorizationType" : "",
"apiKeyRequired" : true,
"requestParameters" : configData.requestMethodParameters
}
// 2) Create resource method
Aag.createResourceMethod( configData.method, requestData, resourceId ).then( function( res ){
return res;
}).then( function( res ) {
// 3) Create resource method response model
return Aag.attachMethodResponseModel( configData.method, configData.responseModel, resourceId, "200" ).then( function( res ){
return res;
});
}).then( function( res ) {
var requestData = {
"type" : configData.type,
"httpMethod" : configData.method,
"uri" : configData.endpointUrl,
"requestParameters" : configData.requestIntegrationParameters
}
// 4) Create resource method integration
return Aag.addMethodIntegration( configData.method, requestData, resourceId ).then( function( res ){
return res;
});
}).then( function( res ) {
//5) Attach Integration model for resource method
return Aag.attachIntegrationResponseModel( configData.method, resourceId, "200" ).then( function( res ){
return res;
});
}).then( function( res ) {
//6) Attach Integration response model for 200
if( templateData ) {
return Aag.attachIntegrationResponseTemplate( configData.method, resourceId, templateData, "200" ).then( function( res ){
resolve( res );
});
} else {
resolve( true );
}
}).catch( function( err ) {
reject( err );
});
});
});
}