{
  "{d}test": {
    "{f}exportoptions.json": {
      "passwordStyle": "SKIP",
      "apiOptionStyle": "EMIT_ALL",
      "authTokenStyle": "SKIP_AUTO",
      "skipUrlFragmentWrapping": false,
      "libraryStyle": "EMIT_ALL",
      "schemaCacheStyle": "DO_NOT_EMIT",
      "filters": {
        "key": [
          "libraries"
        ]
      }
    },
    "{d}libraries": {
      "{f}ReadMe.md": "This folder contains definitions for libraries and whether they are used.\n",
      "{f}SystemLibraryUsage.json": [],
      "{f}AdmAuth-1.0.json": {
        "name": "AdmAuth",
        "version": "1.0",
        "isUsedByProject": false,
        "title": "AdminAuthProvider",
        "description": "AuthProvider for Developer Access (not used, example only)",
        "groupName": "newlib",
        "docsURL": "",
        "referenceURL": ""
      },
      "{f}AdmAuth-1.0.js": "// Custom authentication provider, for admin user authentication (access to API Creator).\n// Authenticates hard coded users; in real-life, you might authenticate against LDAP, AD, etc.\n\n// At its core, an Authorization Provider is a JavaScript (create) function\n// that returns an object (see end) containing these 4 functions:\n//   configure: function(values) {...},\n// authenticate(payload) {...},\n// getConfigInfo: function() {...},\n// getLoginInfo: function() {...}\n\n\nout = java.lang.System.out;\n\n// register this create function to LAC.\n// as above, it returns an object containing the 4 functions noted above.\nfunction create() {\n\n    var result = {};  // returned to LAC, containing the 4 functions noted above\n    var configSetup = {\n        keyLifetimeMinutes : 60\n    };\n    var allRoles = ['System administrator', 'Account admin', 'Data admin', 'Data designer'];\n\n\n    // This function is called by API Creator when the user enters a value\n    // for the parameters specified by getConfigInfo and clicks Save.\n    // Returns configuration values which API Creator will save into Admin DB;\n    // these are exported in the json config file\n    result.configure = function configure(myConfig) {\n        configSetup.keyLifetimeMinutes = myConfig.keyLifetimeMinutes || 60;\n    };\n\n\n    // Main crux of Auth Provider - called by API Server on post to @authenticate, to return list of Roles\n    // NOTE: the function configure must be called first - this will validate the user/pw\n\n    // Eg, you might with to query LDAP, Active Directory etc, to empower corporate users\n    // to use Live API Creator to create database.\n    // You will also need to compute the proper admin roles explained in the product documentation\n    // under Admin Authentication Providers\n\n    // The argument passed in will contain whatever values were provided to the @authentication service.\n    // If the caller is well-behaved, this should correspond to the parameters described by getLoginInfo,\n    // but you should not depend on that.\n\n    // This function must return an object containing just an error message if the authentication failed.\n    // If the authentication succeeded, then it must return an object with the following properties:\n    result.authenticate = function authenticate(payload) {\n\n        out.println(\"Admin Authentication called...\");\n\n        var roles = [];\n        var errorMsg = \"Sorry, you are not authorized\";\n        var resetPasswordURL = null;\n        var forgotPasswordURL = null;\n        var myUserData = [];\n        var autResponse = errorMsg;\n\n        // these hard-coded users are in lieu of actually doing a query against LDAP, AD etc.\n        if (payload.username == 'admin' || payload.username == 'demo' || payload.username == 'sa' || payload.username == 'boris') {\n            out.println(\"Admin Authentication - default admin/demo/sa user - good to go..\");\n            roles = allRoles; // || HARD CODED FOR DEMO (we even ignore the pwd)\n            myUserData = {accountIdent:'1000'};\n            if (payload.username == 'demo') {\n                myUserData = {accountIdent:'1'};\n                out.println(\"... with SA account visibility..\");\n            }\n            autResponse = {\n                errorMessage : null,\n                roleNames : roles,\n                userIdentifier : payload.username,\n                keyExpiration : new Date(+new Date()\n                        + (+configSetup.keyLifetimeMinutes) * 60 * 1000),\n                userData : myUserData,\n                userInfo : myUserData,\n                lastLogin : {\n                    datetime : null,\n                    ipAddress : null\n                }\n            };\n            out.println(\"Admin Authentication successful - returning..\" + JSON.stringify(autResponse));\n            return autResponse;\n        } else {\n            out.println(\"Admin Authentication FAILED!\");\n            return {\n                errorMessage : errorMsg,\n                roleNames : roles,\n                userIdentifier : payload.username,\n                keyExpiration : new Date(+new Date()\n                        + (+configSetup.keyLifetimeMinutes) * 60 * 1000),\n                userData : myUserData,\n                userInfo : myUserData,\n                lastLogin : {\n                    datetime : null,\n                    ipAddress : null\n                }\n            };\n        }\n    };\n\n\n    // FUNCTION getLoginInfo is used to create the login dialog - DO NOT CHANGE\n    // This function is called by API Server when a client needs to know what kind of information is required for authentication.\n    // Basically, this describes what the login dialog should look like (assuming the client is an interactive application).\n    result.getLoginInfo = function getLoginInfo() {\n        return {\n            fields : [\n                    {\n                        name : \"username\",\n                        display : \"Username\",\n                        description : \"Enter your First Name\",\n                        type : \"text\",\n                        length : 40,\n                        helpURL : \"http://liveapicreator.ca.com\"\n                    },\n                    {\n                        name : \"password\",\n                        display : \"Password\",\n                        description : \"Enter your Last Name as Password\",\n                        type : \"password\",\n                        length : 40,\n                        helpURL : \"http://liveapicreator.ca.com/\"\n                    } ],\n            links : [\n            ]\n        };\n    };\n\n    result.getConfigInfo = function getConfigInfo() {\n        return {\n            current : {\n                \"keyLifetimeMinutes\" : configSetup.keyLifetimeMinutes\n            },\n            fields : [\n                {\n                name : \"keyLifetimeMinutes\",\n                display : \"API Key Lifetime (Minutes)\",\n                type : \"number\",\n                length : 8,\n                helpURL : \"http://www.liveapicreator.ca.com\"\n            } ],\n            links : []\n        };\n    }\n\n    // returns object containing the 4 functions that define a Custom Authentication Provider:\n    //   getConfigInfo: function() {...},  configure: function(values) {...}, getLoginInfo: function() {...}, authenticate:\n\n    // returns the 4 functions\n    return result;\n}\n",
      "{f}b2bB2B-1.0.json": {
        "name": "b2bB2B",
        "version": "1.0",
        "isUsedByProject": false,
        "title": "B2BLib",
        "description": "Various B2B services (invoked from JavaScript Logic)",
        "groupName": "newlib",
        "docsURL": "",
        "referenceURL": ""
      },
      "{f}b2bB2B-1.0.js": "var B2B = {};  // a common JavaScript technique to name-scope shared functions\n//TODO: employ arrow functions when fully supported by Nashorn\n\nB2B.transformToWebHook = function transformToWebHook(aLogicContext, aResourceName, aTargetUrl) {\n    aLogicContext.logDebug(\"*** B2B.transformToWebHook *** using: \" + aResourceName + \", to: \" + aTargetUrl);\n    var resourceURL = aTargetUrl; // \"http://localhost:8080/rest/default/b2bderbypavlov/v1/SalesReports\";\n\n    // custom resource provides name mapping\n    // readiness lab: uncomment this  =====>\n    // aLogicContext.logDebug(\"getting aLogicContext.getTableDefinition()\");\n    var metaTable = aLogicContext.getTableDefinition();\n    aLogicContext.logDebug(\"*** transformToWebHook ***  metaTable: \" + metaTable);\n    var options = {sysfilter: \"equal(OrderID:\" + aLogicContext.getCurrentState().OrderID + \")\"\n            ,\"sysfilter..SupplierAlert.Order_DetailsList.Product.Supplier\": \"equal(CompanyName: '\" + \"Pavlova, Ltd.\" + \"')\" };\n    var resourceGetResponse = SysUtility.getResource(aResourceName, options);  // FIXME\n\n    // system console output\n    aLogicContext.logDebug(\"B2B.transformToWebHook posting getResponse: \" + JSON.stringify(resourceGetResponse).substring(1, 20) + \"...\");\n    aLogicContext.logDebug(\"B2B.transformToWebHook to URL: \" + resourceURL);\n\n    if (resourceURL === null || resourceURL === \"\") {\n        out = java.lang.System.out;\n        out.println(\"WebHook URL is null/empty - this not posted:\\n\" + (JSON.stringify(resourceGetResponse)).substring(1, 4) + \"...\");\n    }\n    else {\n        var settings = { headers: { Authorization: \"CALiveAPICreator supplier:1\" }};  // FIXME\n        var postResponse = SysUtility.restPost(resourceURL, null, settings, resourceGetResponse);\n\n        // API Creator log output\n        // log.debug('ok, using re-usable solution');\n        if (JSON.parse(postResponse).statusCode !== 201) {\n            throw \"B2B.transformToWebHook unexpected post response: \" + postResponse;\n        }\n    }\n\n    return null;\n};\n\n\nB2B.sendToWebHook = function sendToWebHook(aPostRequest, aTargetUrl) {\n    resourceURL = aTargetUrl;\n    var tryIt = true;\n    if (tryIt === true) {\n        if (resourceURL === null || resourceURL === \"\") {\n            out = java.lang.System.out;\n            out.println(\"WebHook URL is null/empty - this not posted:\\n\" + (JSON.stringify(aPostRequest)).substring(1, 20) + \"...\");\n        }\n        else {\n            var settings = { headers: { Authorization: \"CALiveAPICreator supplier:1\" }};  // FIXME\n            var postResponse = SysUtility.restPost(resourceURL, null, settings, aPostRequest);\n\n            // API Creator log output\n            // log.debug('ok, using re-usable solution');\n            if (JSON.parse(postResponse).statusCode !== 201) {\n                throw \"B2B.sendToWebHook unexpected post response: \" + postResponse;\n            }\n        }\n    }\n    return null;\n};\n\n\n// you can save state in logicContext.userProperties, including complex objects such as Maps\n\nB2B.putPropertyMap = function putPropertyMap(logicContext, propertyName, key, value) {\n    var RestRequest = Java.type('com.kahuna.server.rest.RestRequest');\n    var req = RestRequest.getCurrentRestRequest();\n    logicContext.logDebug(\"*** B2B.putPropertyMap - propertyName: \" + propertyName + \", key: \" + key + \", value: \" + value + \", on req: \" + req);\n    var property = req.getUserProperties().get(propertyName);   // userProperties to maintain state in transaction\n    if (property === null) {\n        property = new java.util.HashMap();\n    }\n    property.put(key, value);\n    req.setUserProperty(propertyName, property);\n};\n\n\n//approach for global (static) properties (dynamic properties can be saved in req.getUserProperties().get)\n//(alternative: store them in a 1 row table, edit with Data Explorer, accessors here)\n\nB2B.supplierURL = function supplierURL(req) {\n    var resultURL = req.localFullBaseURL.replace(\"nw\",\"pavlov\");\n    out.println(\"B2B.supplierURL returns: \" + resultURL);\n    return resultURL;\n};\n\n\n//returns a sample order, for testing\n\nB2B.sampleOrder = function sampleOrder() {\n    var newPartnerOrderJson =\n    {\n        \"CustomerNumber\": \"VINET\",\n        \"Items\": [\n            {\n            \"Product\": {\n                \"ProductName\": \"Pavlova\"\n            },\n            \"Quantity\": 1\n            }, {\n                \"Product\": {\n                    \"ProductName\": \"Uncle Bob's Organic Dried Pears\"\n                },\n                \"Quantity\": 2\n            }, {\n                \"Product\": {\n                    \"ProductName\": \"Tofu\"\n                },\n                \"Quantity\": 3\n            }, {\n                \"Product\": {\n                    \"ProductName\": \"Ikura\"\n                },\n                \"Quantity\": 4\n            }, {\n                \"Product\": {\n                    \"ProductName\": \"Konbu\"\n                },\n                \"Quantity\": 5\n            }, {\n                \"Product\": {\n                    \"ProductName\": \"Alice Mutton\"\n                },\n                \"Quantity\": 1\n            }\n        ],\n        \"Shipper\": {\n            \"CompanyName\": \"Federal Shipping\"\n        }\n    };\n\n    return newPartnerOrderJson;\n};\n\n\n// send email\n\nB2B.sendEmail = function sendEmail() {\n    var result = {};\n    var msg = \"error\";\n    var configSetup = {\n        to: \"to\",\n        from: \"from\",\n        title: \"title\",\n        text: \"text\"\n    };\n\n    result.configure = function configure(myconfig) {\n        configSetup.to = myconfig.to || \"to\";\n        configSetup.from = myconfig.from || \"from\";\n        configSetup.title = myconfig.title || \"title\";\n        configSetup.text = myconfig.text || \"text\";\n    };\n\n    result.send = function send() {\n        try {\n            // call my mail interface here\n            msg = \"Send email (stub) title: \" + configSetup.title + \" to: \" + configSetup.to + \", from: \" + configSetup.from + \" body text: \" + configSetup.text;\n        }\n        catch (e) {\n            return e;\n        }\n        out.println(\"B2B.sendMail returns: \" + msg);\n        return msg;\n    };\n\n    return result;\n}\n\n\n// minor debug helper, to prevent NPE in logging anObject.toString\n// where anObject is null\n\nB2B.db = function db(anObject) {\n    var result = \"null\";\n    if (anObject !== null) {\n        result = anObject.toString();\n    }\n    return result;\n};\n\n\n// copies like-named attributes from logicContext's row -> targetRow,\n// ignoring attributes part of pKey\n\nB2B.copyAttributes = function copyAttributes(logicContext, targetRow) {\n\n    sourceRow = logicContext.getCurrentState();\n\n    var sourceMetaEntity = sourceRow.getMetaEntity();\n    var targetMetaEntity = targetRow.getMetaEntity();\n\n    logicContext.touch(targetRow);\n    var debugMoved = [];\n\n    for each (var eachProp in sourceMetaEntity.getProperties()) {\n      if ( eachProp.isAttribute() ) {\n        var targetMetaProp = targetMetaEntity.getPropertyByName(eachProp.name);\n        // NB: cannot use hasOwnProperty (Java obj, not JS)\n        if (null !== targetMetaProp) {\n            if (targetMetaProp.isInPrimaryKey() === true) {\n                // logicContext.logDebug(\"copyAttributes -  skipping eachProp since in pKey: \" + eachProp.name);\n            }\n            else {\n                var propValue = sourceRow[eachProp.name];\n                targetRow[eachProp.name] = propValue;\n                debugMoved.push(eachProp.name);\n            }\n        }\n        else {\n            // logicContext.logDebug(\"copyAttributes - skipping eachProp since not in target: \" + eachProp.name);\n        }\n      }\n    }\n    logicContext.logDebug(\"copyAttributes - moved: [\" + debugMoved + \"]\");\n    logicContext.update(targetRow);\n};\n",
      "{f}ConfLib-1.0.json": {
        "name": "ConfLib",
        "version": "1.0",
        "isUsedByProject": false,
        "title": "ConfLib",
        "description": "Reuse (constant strings, etc)",
        "groupName": "newlib",
        "docsURL": null,
        "referenceURL": null
      },
      "{f}ConfLib-1.0.js": "var ConfigLib = {    // preferably, load values from property files, as in Conference - Management\n    confOfferURLFragment: \"conf-offer\",\n    confManagementURLFragment: \"conf-management\",\n    confManagementURL: 'http://localhost:8080/rest/default/conf-management/v1/ProcessCharges',\n    confManagementAuth: { headers: { Authorization: \"CALiveAPICreator AcctgToken:1\" }},  // see Auth Tokens screen\n    confOfferAuth: { headers: { Authorization: \"CALiveAPICreator AdminKey:1\" }}\n    \n};\n",
      "{f}ConfManagementLib-1.0.json": {
        "name": "ConfManagementLib",
        "version": "1.0",
        "isUsedByProject": false,
        "title": "ConfManagementLib",
        "description": "This is a JavaScript library",
        "groupName": "newlib",
        "docsURL": null,
        "referenceURL": null
      },
      "{f}ConfManagementLib-1.0.js": "var Config = {  // a common technique for name-spacing in JavaScipt\n    created: new Date(),\n    save: function save(aMkt) {\n        Config.settings = aMkt;\n        Config.modified = new Date();\n        // print(\"ConfManagementLib Config'd: \" + JSON.stringify(Config));\n    }\n};\n\nprint(\"\\nConfig loaded: \" + JSON.stringify(Config) + \"\\n\");\n",
      "{f}CustomAuth-1.0.json": {
        "name": "CustomAuth",
        "version": "1.0",
        "isUsedByProject": false,
        "title": "B2BAuthProvider",
        "description": "AuthProvider using REST on Employees Table (requires install)",
        "groupName": "newlib",
        "docsURL": "",
        "referenceURL": ""
      },
      "{f}CustomAuth-1.0.js": "// Custom authentication provider, uploaded in B2B install script.\n// Authenticates using RESTful service, configured to be employees table in Northwind-B2B.\n\n// At its core, an Authorization Provider is a JavaScript function that returns an object (see end) containing these 4 functions:\n//   getConfigInfo: function() {...},  configure: function(values) {...}, getLoginInfo: function() {...}, authenticate:\n//\n\nout = java.lang.System.out;\n\nfunction create() {\n\n    var result = {};\n    var configSetup = {\n        logonApiKey : '',\n        loginBaseURL : '',\n        loginGroupURL : '',\n        keyLifetimeMinutes : 60\n    };\n\n\n    // This function is called by API Creator when the user enters a value\n    // for the parameters specified by getConfigInfo and clicks Save.\n    // Returns configuration values which API Creator will save into Admin DB;\n    // these are exported in the json config file\n    result.configure = function configure(myConfig) {\n        configSetup.logonApiKey = myConfig.logonApiKey || 'demo_full';  // supply, or default\n        configSetup.loginBaseURL = myConfig.loginBaseURL || 'http://localhost:8080/rest/default/v1/...';\n        configSetup.loginGroupURL = myConfig.loginGroupURL || 'http://localhost:8080/rest/default/v1/...';\n        configSetup.keyLifetimeMinutes = myConfig.keyLifetimeMinutes || 60;\n    };\n\n\n    // Main crux of Auth Provider - called by API Server on post to @authenticate, to return list of Roles\n    // NOTE: the function configure must be called first - this will validate the user/pw\n\n    // The argument passed in will contain whatever values were provided to the @authentication service.\n    // If the caller is well-behaved, this should correspond to the parameters described by getLoginInfo,\n    // but you should not depend on that.\n\n    // This function must return an object containing just an error message if the authentication failed.\n    // If the authentication succeeded, then it must return an object with the following properties:\n    result.authenticate = function authenticate(payload) {\n\n        out.println(\"Authentication called...\");\n\n        var roles = [];\n        var errorMsg = null;\n        var resetPasswordURL = null;\n        var forgotPasswordURL = null;\n        var myUserData = [];\n        var params = null;\n        var settings = {\n            headers : {\n                'Authorization' : 'CALiveAPICreator ' + configSetup.logonApiKey + ':1'\n            }\n        };\n\n        try {\n            if (payload.username == 'admin' || payload.username == 'demo') {\n                out.println(\"Authentication - default admin/demo/ user - good to go..\");\n                // out.println(\"Lab test OK...\");  // uncomment this for Readiness Lab\n                roles = ['Full access']; // || HARD CODED FOR DEMO (we even ignore the pwd)\n                errorMsg = null; // authorized successfully\n            }\n            else if (payload.username == 'pavlov' ||  payload.username == 'Pavlov') {\n                out.println(\"Authentication - Pavlov - role is Supplier, with Global ID===7\");\n                roles = ['Supplier']; // Permission's Row Filter uses the following Global\n                myUserData = {ID: '7', AnotherParm: 'like this'};  //  reference like this: \"SupplierID\" = @{ID}\n                errorMsg = null; // authorized successfully\n            }\n            else {\n                // GET this JSON request to determine if username and password is valid\n                // if so, return an array of role names (here just 'Full Access')\n                // and, to simplify typing in demos, will allow the default Password1\n                var pwd = payload.password;\n                if (payload.username == \"Janet\" && payload.password == \"Password1\") {\n                    pwd = \"Leverling\";\n                }\n                var loginAttemptURL = configSetup.loginBaseURL\n                    + \"?sysfilter=equal(FirstName:'\"+ payload.username\n                    + \"')&sysfilter=equal(LastName:'\" + pwd\n                    + \"')\";\n                out.println(\"Authentication - finding [\" + payload.username + \".\" + pwd + \"]\");\n                out.println(\"... via Rest URL: \" + loginAttemptURL);\n                out.println(\"... using settings: \" + JSON.stringify(settings));\n                var loginAttempt = SysUtility.restGet(loginAttemptURL, params, settings);\n                var groups = JSON.parse(loginAttempt);\n                // out.println(JSON.stringify(groups, null, 2));\n\n                if (groups.hasOwnProperty('errorMessage')) {\n                    out.println(\"...errorMessage found in loginAttempt: \" + loginAttempt);\n                    errorMsg = groups.errorMessage;\n                }\n                else {\n                    // change the field name below .name to the name of your\n                    // roleName column\n                    errorMsg = 'Username ' + payload.username + ' not found with last name as password';\n                    for ( var row in groups) {\n                        roles = ['Full access']; // || HARD CODED FOR DEMO\n                        // roles.push(groups[row].Region);\n                        // myUserData.push(groups[row].Region)\n                        errorMsg = null; // if one role is found then we are good to return\n                    }\n                }\n                if (errorMsg != null) {\n                    out.println(\"...get failed to find this user, loginAttempt: \" + loginAttempt);\n                }\n            }\n        }\n        catch (e) {\n            errorMsg = e.message;\n        }\n\n        var autResponse = {\n            errorMessage : errorMsg,\n            roleNames : roles,\n            userIdentifier : payload.username,\n            keyExpiration : new Date(+new Date()\n                    + (+configSetup.keyLifetimeMinutes) * 60 * 1000),\n            userData : myUserData,\n            lastLogin : {\n                datetime : null,\n                ipAddress : null\n            }\n        };\n        return autResponse;\n    };\n\n\n    // FUNCTION getAllGroups is used to map all available groups for existing application -\n    // unused in this example, provided for illustration purposes only...\n    result.getAllGroups = function getAllGroups() {\n        var roles = [];\n        var errorMsg = null;\n        var params = null;\n        var settings = {\n            headers : {\n                'Authorization' : 'CALiveAPICreator ' + configSetup.logonApiKey + ':1'\n            }\n        };\n\n        try {\n            var loginAttemptURL = configSetup.loginGroupURL; // no filter needed- get all roles?\n            var groupsResponse = SysUtility.restGet(loginAttemptURL, params,\n                    settings);\n            var groups = JSON.parse(groupsResponse);\n            if (groups.hasOwnProperty('errorMessage')) {\n                errorMsg = groups.errorMessage;\n            }\n            else {\n                // change the .name to refrelect the name of your roles returned\n                // in the JSON object\n                for ( var row in groups) {\n                    roles.push(groups[row].name);\n                }\n            }\n        }\n        catch (e) {\n            errorMsg = e.message;\n        }\n\n        var autResponse = {\n            errorMessage : errorMsg,\n            roleNames : roles\n        };\n\n        return autResponse;\n    };\n\n\n    // FUNCTION getLoginInfo is used to create the login dialog - DO NOT CHANGE\n    // This function is called by API Server when a client needs to know what kind of information is required for authentication.\n    // Basically, this describes what the login dialog should look like (assuming the client is an interactive application).\n    result.getLoginInfo = function getLoginInfo() {\n        return {\n            fields : [\n                    {\n                        name : \"username\",\n                        display : \"Username\",\n                        description : \"Enter your First Name\",\n                        type : \"text\",\n                        length : 40,\n                        helpURL : \"http://liveapicreator.ca.com\"\n                    },\n                    {\n                        name : \"password\",\n                        display : \"Password\",\n                        description : \"Enter your Last Name as Password\",\n                        type : \"password\",\n                        length : 40,\n                        helpURL : \"http://liveapicreator.ca.com/\"\n                    } ],\n            links : [\n\n            ]\n        };\n    };\n\n    result.getConfigInfo = function getConfigInfo() {\n        return {\n            current : {\n                \"keyLifetimeMinutes\" : configSetup.keyLifetimeMinutes,\n                \"logonApiKey\" :        configSetup.logonApiKey,\n                \"loginBaseURL\" :       configSetup.loginBaseURL,\n                \"loginGroupURL\" :      configSetup.loginGroupURL\n            },\n            fields : [ {\n                name : \"logonApiKey\",\n                display : \"logonApiKey\",\n                type : \"text\",\n                length : 60,\n                helpURL : \"\"\n            }, {\n                name : \"loginBaseURL\",\n                display : \"loginBaseURL\",\n                type : \"text\",\n                length : 120,\n                helpURL : \"\"\n            }, {\n                name : \"loginGroupURL\",\n                display : \"loginGroupURL\",\n                type : \"text\",\n                length : 120,\n                helpURL : \"\"\n            }, {\n                name : \"keyLifetimeMinutes\",\n                display : \"API Key Lifetime (Minutes)\",\n                type : \"number\",\n                length : 8,\n                helpURL : \"http://www.liveapicreator.ca.com\"\n            } ],\n            links : []\n        };\n    };\n\n    // returns object containing the 4 functions that define a Custom Authentication Provider:\n    //   getConfigInfo: function() {...},  configure: function(values) {...}, getLoginInfo: function() {...}, authenticate:\n\n    return result;  // returns the 4 func\n}\n",
      "{f}insertActions-1.0.json": {
        "name": "insertActions",
        "version": "1.0",
        "isUsedByProject": false,
        "title": "InsertActionsLib",
        "description": "Inject @metadata Action tags (e.g., Lookup), for Request Events",
        "groupName": "newlib",
        "docsURL": "",
        "referenceURL": ""
      },
      "{f}insertActions-1.0.js": "var insertActions = {};  // a common JavaScript technique to name-scope shared functions\n\n\n// internal routine to fix path expression from jsonPath (another library)\n\nvar fixPathObject = function fixPathObject(path,jsonObj) {\n    var index = path;//'[\\'Items\\'][0][\\'Product\\']';\n    var res = index.split(\"]\");\n    res.splice(res.length-1,1);\n    var target = jsonObj;\n    for (var i = 0; i < res.length; i++) {\n        var replacement = res[i].replace(\"[\",\"\").replace(\"'\",\"\").replace(\"'\",\"\");\n        target = target[replacement];\n    }\n    return target;\n};\n\n\n// internal routine to create metadata tag\n\nvar getMetadataTag = function getMetadataTag(anAction) {\n    var result = null;\n    if (anAction === \"LOOKUP\") {\n        result = { \"action\": \"LOOKUP\" };\n    }\n    else if (anAction === \"MERGE_INSERT\") {\n        result = { \"action\": \"MERGE_INSERT\" };\n    }\n    else {\n        throw \"insertAction tag must be LOOKUP or MERGE_INSERT\";\n    }\n    return result;\n};\n\n\n// internal routine to insert the tag (iff it does not already exist)\n\nvar insertActionTag = function insertActionTag(aTarget, aTag) {\n    if (typeof aTarget[\"@metadata\"] !== \"undefined\") {\n        // log.debug(dbTitle + \" *** metadata tag already exists, no action taken\");\n    }\n    else {\n        aTarget[\"@metadata\"] = getMetadataTag(aTag);\n    }\n};\n\nfunction db(aString) {\n    log.debug(aString);\n    print (aString);\n}\n\n\n/* insert metadata actions tags for resource into json string, (e.g., LOOKUP) iff they don't already exist\n   returns json string of altered request\n   \n   example from B2B:  PartnerOrder has res.extendedProperties: {\n  \"InsertActions\": [\n    {\n      \"path\": \"$..Product\",\n      \"insertActions\": \"LOOKUP\"\n    },\n    {\n      \"path\": \"Shipper\",\n      \"insertActions\": \"LOOKUP\"\n    }\n  ]\n}\n    json = insertActions.insertActions(req, json, actions);  // API Server processes this...\n*/\n\ninsertActions.insertActionsForResource = function insertActionsForResource(json, actions) {\n    var dbTitle = \"insertActions.InsertActionsForResource: \";\n    // db(dbTitle + \"running with actions: \" + JSON.stringify(actions) + \", json -->\\n\" + json);\n    if (json === null) {\n        return json;\n    }\n    var jsonObj = null; // hold off on parse, until we are sure this resource is relevant\n\n    // for (let eachAction of actions) { -- syntax not supported\n    for each (var eachAction in actions) {\n        if (jsonObj === null) {\n            jsonObj = JSON.parse(json);\n        }\n        if (eachAction.path === \"\") {\n            insertActionTag(jsonObj, eachAction.insertActions);  // root\n        }\n        else {\n            var paths = jsonPath(jsonObj, eachAction.path, {resultType:\"PATH\"});\n            for each (var eachPath in paths) {       // perform insertion for eachPath\n                var target = fixPathObject(eachPath.substring(1), jsonObj);\n                insertActionTag(target, eachAction.insertActions);\n            }\n        }\n    }\n\n    if (jsonObj === null) {\n        return json;\n    }\n    else {\n        return JSON.stringify(jsonObj);\n    }\n};\n\n\n// insert metadata action tags into json string, (e.g., LOOKUP) iff they don't already exist\n// actions array is {resource-name, path-for-insert, \"INSERT\" || \"MERGE_INSERT\"}\n// returns json string of altered request\n// example from B2B:\n//    var actions = [\n//      {resource: \"PartnerOrder\", path: \"$..Product\", insertActions: \"LOOKUP\"},\n//      {resource: \"PartnerOrder\", path: \"Shipper\",    insertActions: \"LOOKUP\"}\n//    ];\n//    json = insertActions.insertActions(req, json, actions);  // API Server processes this...\n\ninsertActions.insertActions = function insertActions(req, json, actions) {\n    var dbTitle = \"InsertActions: \";\n    if (json === null) {\n        db(dbTitle + \"null request object - no action\");\n        return json;\n    }\n    var jsonObj = null; // hold off on parse, until we are sure this resource is relevant\n\n    // for (let eachAction of actions) { -- syntax not supported\n    for each (var eachAction in actions) {\n        if (req.resourceName === eachAction.resource) {\n            if (jsonObj === null) {\n                jsonObj = JSON.parse(json);\n            }\n            if (eachAction.path === \"\") {\n                insertActionTag(jsonObj, eachAction.insertActions);  // root\n            }\n            else {\n                var paths = jsonPath(jsonObj, eachAction.path, {resultType:\"PATH\"});\n                log.debug(dbTitle + \"paths: \" + paths);  // ==>  [ $['Items'][0]['Product'] ...]\n                for each (var eachPath in paths) {       // perform insertion for eachPath\n                    var target = fixPathObject(eachPath.substring(1), jsonObj);\n                    insertActionTag(target, eachAction.insertActions);\n                }\n            }\n        }\n    }\n\n    if (jsonObj === null) {\n        db(dbTitle + \"no change\");\n        return json;\n    }\n    else {\n        db(dbTitle + \"transformed to: \" + JSON.stringify(jsonObj));\n        return JSON.stringify(jsonObj);\n    }\n};\n",
      "{f}jsonpath-0.8.0.json": {
        "name": "jsonpath",
        "version": "0.8.0",
        "isUsedByProject": false,
        "title": "JsonPathLib",
        "description": "Used by InsertActionsLib",
        "groupName": "newlib",
        "docsURL": "",
        "referenceURL": ""
      },
      "{f}jsonpath-0.8.0.js": "/* JSONPath 0.8.0 - XPath for JSON\n\n *\n\n * Copyright (c) 2007 Stefan Goessner (goessner.net)\n\n * Licensed under the MIT (MIT-LICENSE.txt) licence.\n\n */\n\nfunction jsonPath(obj, expr, arg) {\n\n   var P = {\n\n      resultType: arg && arg.resultType || \"VALUE\",\n\n      result: [],\n\n      normalize: function(expr) {\n\n         var subx = [];\n\n         return expr.replace(/[\\['](\\??\\(.*?\\))[\\]']/g, function($0,$1){return \"[#\"+(subx.push($1)-1)+\"]\";})\n\n                    .replace(/'?\\.'?|\\['?/g, \";\")\n\n                    .replace(/;;;|;;/g, \";..;\")\n\n                    .replace(/;$|'?\\]|'$/g, \"\")\n\n                    .replace(/#([0-9]+)/g, function($0,$1){return subx[$1];});\n\n      },\n\n      asPath: function(path) {\n\n         var x = path.split(\";\"), p = \"$\";\n\n         for (var i=1,n=x.length; i<n; i++)\n\n            p += /^[0-9*]+$/.test(x[i]) ? (\"[\"+x[i]+\"]\") : (\"['\"+x[i]+\"']\");\n\n         return p;\n\n      },\n\n      store: function(p, v) {\n\n         if (p) P.result[P.result.length] = P.resultType == \"PATH\" ? P.asPath(p) : v;\n\n         return !!p;\n\n      },\n\n      trace: function(expr, val, path) {\n\n         if (expr) {\n\n            var x = expr.split(\";\"), loc = x.shift();\n\n            x = x.join(\";\");\n\n            if (val && val.hasOwnProperty(loc))\n\n               P.trace(x, val[loc], path + \";\" + loc);\n\n            else if (loc === \"*\")\n\n               P.walk(loc, x, val, path, function(m,l,x,v,p) { P.trace(m+\";\"+x,v,p); });\n\n            else if (loc === \"..\") {\n\n               P.trace(x, val, path);\n\n               P.walk(loc, x, val, path, function(m,l,x,v,p) { typeof v[m] === \"object\" && P.trace(\"..;\"+x,v[m],p+\";\"+m); });\n\n            }\n\n            else if (/,/.test(loc)) { // [name1,name2,...]\n\n               for (var s=loc.split(/'?,'?/),i=0,n=s.length; i<n; i++)\n\n                  P.trace(s[i]+\";\"+x, val, path);\n\n            }\n\n            else if (/^\\(.*?\\)$/.test(loc)) // [(expr)]\n\n               P.trace(P.eval(loc, val, path.substr(path.lastIndexOf(\";\")+1))+\";\"+x, val, path);\n\n            else if (/^\\?\\(.*?\\)$/.test(loc)) // [?(expr)]\n\n               P.walk(loc, x, val, path, function(m,l,x,v,p) { if (P.eval(l.replace(/^\\?\\((.*?)\\)$/,\"$1\"),v[m],m)) P.trace(m+\";\"+x,v,p); });\n\n            else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) // [start:end:step]  phyton slice syntax\n\n               P.slice(loc, x, val, path);\n\n         }\n\n         else\n\n            P.store(path, val);\n\n      },\n\n      walk: function(loc, expr, val, path, f) {\n\n         if (val instanceof Array) {\n\n            for (var i=0,n=val.length; i<n; i++)\n\n               if (i in val)\n\n                  f(i,loc,expr,val,path);\n\n         }\n\n         else if (typeof val === \"object\") {\n\n            for (var m in val)\n\n               if (val.hasOwnProperty(m))\n\n                  f(m,loc,expr,val,path);\n\n         }\n\n      },\n\n      slice: function(loc, expr, val, path) {\n\n         if (val instanceof Array) {\n\n            var len=val.length, start=0, end=len, step=1;\n\n            loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, function($0,$1,$2,$3){start=parseInt($1||start);end=parseInt($2||end);step=parseInt($3||step);});\n\n            start = (start < 0) ? Math.max(0,start+len) : Math.min(len,start);\n\n            end   = (end < 0)   ? Math.max(0,end+len)   : Math.min(len,end);\n\n            for (var i=start; i<end; i+=step)\n\n               P.trace(i+\";\"+expr, val, path);\n\n         }\n\n      },\n\n      eval: function(x, _v, _vname) {\n\n         try { return $ && _v && eval(x.replace(/@/g, \"_v\")); }\n\n         catch(e) { throw new SyntaxError(\"jsonPath: \" + e.message + \": \" + x.replace(/@/g, \"_v\").replace(/\\^/g, \"_a\")); }\n\n      }\n\n   };\n\n\n\n   var $ = obj;\n\n   if (expr && obj && (P.resultType == \"VALUE\" || P.resultType == \"PATH\")) {\n\n      P.trace(P.normalize(expr).replace(/^\\$;/,\"\"), obj, \"$\");\n\n      return P.result.length ? P.result : false;\n\n   }\n\n}\n"
    }
  }
}