npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

apigee-edge-js

v0.4.36

Published

nodejs library for the administration API for Apigee (Edge and X and hybrid).

Downloads

487

Readme

apigee-edge-js

Apache 2.0 Node Test LastCommit CommitActivity Downloads

A library of functions for administering Apigee from nodejs.

Do you want to automate the administration or management of Apigee from Nodejs? This library helps you do that.

Example:

To create a new developer in Apigee:

const apigeejs = require('apigee-edge-js'),
      apigee = apigeejs.apigee;

const options = {
    org : config.org,
    user: config.username,
    password: config.password
  };

apigee.connect(options)
  .then ( org => {
    const options = {
          developerEmail : "[email protected]",
          lastName : "Dimaggio",
          firstName : "Josephine",
          userName : "JD1"
        };

    return org.developers.create(options)
      .then( result => console.log('ok. developer: ' + JSON.stringify(result)) )
  })
  .catch ( error => console.log('error: ' + error) );

You can also tell the library to read credentials from .netrc:

const apigeejs = require('apigee-edge-js'),
      apigee = apigeejs.apigee;

let options = { org : config.org, netrc: true };
apigee.connect(options).then(...);

For customers who have SSO (SAML) enabled for their Apigee SaaS organization, you can obtain a token with a passcode. This requires that you first sign-in with the browser using the interactive experience, then visit https://zoneName.login.apigee.com/passcode to obtain a passcode. Then:

const apigeejs = require('apigee-edge-js'),
      apigee = apigeejs.apigee;

let options = { org : config.org, passcode: 'abcdefg', user: '[email protected]' };
apigee.connect(options).then(...);

For Apigee X or hybrid, obtain a token with gcloud auth print-access-token and then :

const apigeejs = require('apigee-edge-js'),
      apigee = apigeejs.apigee;

let options = { org : 'my-org', apigeex: true, token: 'kjkdjskjskjs.abcdefg' };
apigee.connect(options).then(...);

The methods on the various objects accept callbacks, and return promises. In code you write that uses this library, it's probably best if you choose one or the other. Promises are probably better, because they're more succinct. But some people prefer callbacks. Here's an example using old-school callbacks instead of ES6 promises:

const apigeejs = require('apigee-edge-js'),
      utility = apigeejs.utility,
      apigee = apigeejs.apigee;

const options = {
      mgmtServer: config.mgmtserver,
      org : config.org,
      user: config.username,
      password: config.password
    };

apigee.connect(options, function(e, org){
  if (e) {
    console.log(e);
    console.log(e.stack);
    process.exit(1);
  }

  const options = {
        developerEmail : "[email protected]",
        lastName : "Dimaggio",
        firstName : "Josephine",
        userName : "JD1"
      };

  org.developers.create(options, function(e, result){
    if (e) {
      utility.logWrite(JSON.stringify(e));
      process.exit(1);
    }
    utility.logWrite(sprintf('ok. developer: %s', JSON.stringify(result, null, 2)));
  });
});

Many more examples

There are numerous working examples at a companion repo: apigee-edge-js-examples.

This is not an official Google product

This library and the example tools included here are not an official Google product. Support is available on a best-effort basis via github or community.apigee.com . Pull requests are welcomed.

Using the Library

You do not need to clone this repo in order to use the library. Youc an just get the module via npm install:

npm install apigee-edge-js

The Object Model

To start, you call apigee.connect(). This will connect to an Apigee organization. If it is a SaaS organization, this method will try to find a stashed OAuth token and if not will get an OAuth token.

  • If you use callbacks, the callback will receive (e, org), where e is an error, possibly null, and org is an Organization object

  • If you use promises, the promise will resolve with the value of an Organization object

The organization object has the following members, each a hash with various child members as functions:

| member | functions | | -------------------- | ---------------------------------------------------------------- | | (self) | get, getProperties, addProperties, removeProperties, setConsumerSecretLength, setConsumerKeyLength | | environments | get, getVhosts, getVhost, createVhost, deleteVhost | | proxies | get, del, deploy, undeploy, import, export, getRevisions, getDeployments, getResourcesForRevision, getPoliciesForRevision, getProxyEndpoints | | caches | get, create, del | | kvms | get, create, put, del | | resourcefiles | get, create, update, del | | sharedflows | get, del, deploy, undeploy, import, export, getRevisions, getDeployments, getResourcesForRevision, getPoliciesForRevision | | flowhooks | get, put | | products | get, create, del | | developers | get, create, del,revoke, approve | | keystores | get, create, del, import key and cert, create references | | targetservers | get, create, del, disable, enable, update | | developerapps | get, create, del, revoke, approve, update | | appcredentials | find, add, del, revoke, approve, add/remove product, update attrs | | audits | get | | stats | get | | specs | get, getMeta, list, create, update, del | | companies | get, create | | companyapps | get, create | | companydevelopers | get | | maskconfigs | get, set, add/update, remove |

Each child function gets invoked as a function returning a promise: fn(options), or in old-school callback style: fn(options, callback) .

What is possible here?

As you can see from the function list above, pretty much all the basic stuff you want to do with Apigee administration is here. There are some gaps (for example around companies and companyapps); we can fill those in as need arises. (Pull requests welcomed)

You can examine the examples directory for some example code illustrating some practical possibilities. A few specific code examples are shown here.

Pull requests are welcomed, for the code or for examples.

One disclaimer:

  • The spec module wraps the /dapi API, which is at this moment undocumented and unsupported, and subject to change. It works today, but the spec module may stop functioning at any point. Use it at your own risk!

Pre-Requisites

Nodejs v10.15.1 or later. The library and tests use Promises, spread/rest operators, and other ES6+ things.

Examples

Export the latest revision of an API Proxy

using promises:

apigeeOrg.proxies.export({name:'proxyname'})
  .then ( result => {
    fs.writeFileSync(path.join('/Users/foo/export', result.filename), result.buffer);
    console.log('ok');
  })
  .catch( error => console.log(util.format(error)) );

In the case of an error, the catch() will get the Error object. There will be an additional member on the reason object: result. The result is the payload send back, if any.

using callbacks:

apigeeOrg.proxies.export({name:'proxyname'}, function(e,result) {
  if (e) {
    console.log("ERROR:\n" + JSON.stringify(e, null, 2));
    return;
  }
  fs.writeFileSync(path.join('/Users/foo/export', result.filename), result.buffer);
  console.log('ok');
});

Export a specific revision of an API Proxy

promises:

apigeeOrg.proxies.export({name:'proxyname', revision:3})
  .then ( result => {
    fs.writeFileSync(path.join('/Users/foo/export', result.filename), result.buffer);
    console.log('ok');
  });

callbacks:

apigeeOrg.proxies.export({name:'proxyname', revision:3}, function(e,result) {
  if (e) {
    console.log("ERROR:\n" + JSON.stringify(e, null, 2));
    return;
  }
  fs.writeFileSync(path.join('/Users/foo/export', result.filename), result.buffer);
  console.log('ok');
});

Import an API Proxy from a Directory

promises:

var options = {
      mgmtServer: mgmtserver,
      org : orgname,
      user: username,
      password:password
    };
apigee.connect(options)
  .then ( org =>
    org.proxies.import({name:opt.options.name, source:'/tmp/path/dir'})
      .then ( result =>
        console.log('import ok. %s name: %s r%d', term, result.name, result.revision) ) )
  .catch ( error => {
    console.log(util.format(error));
  });

callbacks:

var options = {
      mgmtServer: mgmtserver,
      org : orgname,
      user: username,
      password:password
    };
apigee.connect(options, function(e, org){
  if (e) {
    console.log(JSON.stringify(e, null, 2));
    process.exit(1);
  }

  org.proxies.import({name:opt.options.name, source:'/tmp/path/dir'}, function(e, result) {
    if (e) {
      console.log('error: ' + JSON.stringify(e, null, 2));
      if (result) { console.log(JSON.stringify(result, null, 2)); }
      process.exit(1);
    }
    console.log('import ok. %s name: %s r%d', term, result.name, result.revision);
  });

Deploy an API Proxy

var options = {
  name: 'proxy1',
  revision: 2,
  environment : 'test'
};

org.proxies.deploy(options)
  .then( result => console.log('deployment succeeded.') )
  .catch( error => console.log('deployment failed. ' + error) );

Get the latest revision of an API Proxy

org.proxies.getRevisions({name:'proxyname-here'})
  then( result => {
    console.log('revisions: ' + JSON.stringify(result)); // eg, [ "1", "2", "3"]
    var latestRevision = result[result.length-1];
     ...
  });

Get the latest revision of every API Proxy in an org

This uses an Array.reduce() with a series of promises, each of which appends an item to an array of results.

apigee.connect(options)
  .then( org => {
    common.logWrite('connected');
    org.proxies.get({})
      .then( items => {
        const reducer = (promise, proxyname) =>
          promise .then( accumulator =>
                         org.proxies
                           .get({ name: proxyname })
                           .then( ({revision}) => [ ...accumulator, {proxyname, revision:revision[revision.length-1]} ] )
                       );
        return items
            .reduce(reducer, Promise.resolve([]))
            .then( arrayOfResults => console.log(JSON.stringify(arrayOfResults)) );
      });
  })
  .catch( e => console.error(e) );

Count the number of revisions of every API Proxy in an org

Same approach as above.

apigee.connect(options)
  .then( org => {
    common.logWrite('connected');
    org.proxies.get({})
      .then( items => {
        const reducer = (promise, proxyname) =>
          promise .then( accumulator =>
                         org.proxies
                           .get({ name: proxyname })
                           .then( ({revision}) => [ ...accumulator, {proxyname, count:revision.length} ] )
                       );
        return items
            .reduce(reducer, Promise.resolve([]))
            .then( arrayOfResults => console.log(JSON.stringify(arrayOfResults)) );
      });
  })
  .catch( e => console.error(e) );

Create a Keystore and load a Key and Cert

using callbacks:

  var options = {
        environment : 'test',
        name : 'keystore1'
      };
  org.keystores.create(options, function(e, result){
    if (e) { ... }
    console.log('ok. created');
    options.certFile = './mycert.cert';
    options.keyFile = './mykey.pem';
    options.alias = 'alias1';
    options.keyPassword = 'optional password for key file';
    org.keystores.importCert(options, function(e, result){
      if (e) { ... }
      console.log('ok. key and cert stored.');
    });
  });

Read and Update Mask Configs for an Organization

callbacks:

const apigeejs = require('apigee-edge-js');
const apigee = apigeejs.apigee;
var options = {org : 'ORGNAME', netrc: true, verbosity : 1 };
apigee.connect(options, function(e, org) {
  console.log('org: ' + org.conn.orgname);
  org.maskconfigs.get({name: 'default'}, function(e, body) {
    console.log(JSON.stringify(body));
    org.maskconfigs.set({ json : '$.store.book[*].author' }, function(e, body) {
      console.log(JSON.stringify(body));
      org.maskconfigs.add({ xml : '/apigee:Store/Employee' }, function(e, body) {
        console.log(JSON.stringify(body));
        org.maskconfigs.remove({ remove : ['xPathsFault','jSONPathsFault'] }, function(e, body) {
          console.log(JSON.stringify(body));
          org.maskconfigs.add({ variables : 'dino_var' }, function(e, body) {
            console.log(JSON.stringify(body));
            org.maskconfigs.add({ namespaces : { prefix:'apigee', value:'urn://apigee' } }, function(e, body) {
              console.log(JSON.stringify(body));
            });
          });
        });
      });
    });
  });
});

with ES6 promises:

const apigeejs = require('apigee-edge-js');
const apigee = apigeejs.apigee;
var options = {org : 'ORGNAME', netrc: true, verbosity : 1 };
apigee.connect(options)
  .then ( (org) => {
    console.log('org: ' + org.conn.orgname);
    org.maskconfigs.get({name: 'default'})
      .then( (result) => console.log(JSON.stringify(result)) )
      .then( () => org.maskconfigs.set({ json : '$.store.book[*].author' }) )
      .then( (result) => console.log(JSON.stringify(result)) )
      .then( () => org.maskconfigs.add({ xml : '/apigee:Store/Employee' }) )
      .then( (result) => console.log(JSON.stringify(result)) )
      .then( () => org.maskconfigs.remove({ remove : ['xPathsFault','jSONPathsFault'] }) )
      .then( (result) => console.log(JSON.stringify(result)) )
      .then( () => org.maskconfigs.add({ variables : 'dino_var' }) )
      .then( (result) => console.log(JSON.stringify(result)) )
      .then( () => org.maskconfigs.add({ namespaces : { prefix:'apigee', value:'urn://apigee' } })
      .then( (result) => console.log(JSON.stringify(result)) )
    })
  .catch ( e => console.log(e) );

Create a Target Server

ES6 promises:

const apigeejs = require('apigee-edge-js');
const apigee = apigeejs.apigee;
var options = {org : 'ORGNAME', netrc: true, verbosity : 1 };
apigee.connect(options)
  .then ( org => {
    console.log('org: ' + org.conn.orgname);
    return org.targetservers.create({
      environment : 'test',
      target : {
        name : 'targetserver1',
        host: "api.example.com",
        port: 8080,
        sSLInfo : { enabled : false }
      }
    });
  })
  .catch ( e => console.log(e) );

Create a Developer App

apigee.connect(connectOptions)
  .then ( org => {
    const options = {
            developerEmail,
            name : entityName,
            apiProduct : apiProducts[0]
          };
    org.developerapps.create(options)
      .then( result => {
        ...
      })
      .catch( e => {
        console.log('failed to create: ' + e);
      });
  });

Update attributes on a Developer App

apigee.connect(connectOptions)
  .then ( org => {
    const attributes = {
            updatedBy : 'apigee-edge-js',
            updateDate: new Date().toISOString()
          };
    return org.developerapps.update({ developerEmail, app, attributes })
      .then ( result => console.log('new attrs: ' + JSON.stringify(result.attributes)) );
   })
   .catch( e => console.log('failed to update: ' + util.format(e)) );

Load data from a file into a KVM entry

function loadKeyIntoMap(org) {
  var re = new RegExp('(?:\r\n|\r|\n)', 'g');
  var pemcontent = fs.readFileSync(opt.options.pemfile, "utf8").replace(re,'\n');
  var options = {
        env: opt.options.env,
        kvm: opt.options.mapname,
        key: opt.options.entryname,
        value: pemcontent
      };
  common.logWrite('storing new key \'%s\'', opt.options.entryname);
  return org.kvms.put(options)
    .then( _ => common.logWrite('ok. the key was loaded successfully.'));
}

apigee.connect(common.optToOptions(opt))
  .then ( org => {
    common.logWrite('connected');
    return org.kvms.get({ env })
      .then( maps => {
        if (maps.indexOf(mapname) == -1) {
          // the map does not yet exist
          common.logWrite('Need to create the map');
          return org.kvms.create({ env: opt.options.env, name: opt.options.mapname, encrypted:opt.options.encrypted})
            .then( _ => loadKeyIntoMap(org) );
        }

        common.logWrite('ok. the required map exists');
        return loadKeyIntoMap(org);
      });
  })
  .catch( e => console.log('Error: ' + util.format(e)));

Import an OpenAPI Spec

apigee.connect(connectOptions)
  .then ( org =>
     org.specs.create({ name: 'mySpec', filename: '~/foo/bar/spec1.yaml' })
        .then( r => {
          console.log();
          console.log(r);
        }) )
  .catch( e => console.log('failed to create: ' + util.format(e)) );

Lots More Examples

See the examples directory for a set of working example tools. Or you can examine the test directory for code that exercises the library.

To Run the Tests

To run tests you should create a file called testConfig.json and put it in the toplevel dir of the repo. It should have contents like this:

{
  "org" : "my-org-name",
  "user": "[email protected]",
  "password": "password-goes-here",
  "verbosity": 1
}

or:

{
  "org" : "my-org-name",
  "netrc": true
}

The latter example will retrieve administrative credentials for Apigee from your .netrc file.

Then, to run tests:

npm test

or

node_modules/mocha/bin/mocha

To run a specific subset of tests, specify a regex to the grep option:

node_modules/mocha/bin/mocha  --grep "^Cache.*"

Frequently Asked Questions

  1. Is this an official Google product? Is it supported?

    No. Neither this library nor the related example tools are an official Google product. Support for either is available on a best-effort basis via github or community.apigee.com .

  2. What is this thing good for?

    If your team builds nodejs scripts to perform administrative operations on your Apigee organization, you may want to use this library. It provides a wrapper of basic operations to allow you to import and deploy proxies, create products or developers or applications, populate KVMs, create caches, and so on.

  3. Does it have a wrapper for creating a virtualhost?

    No, that's one thing it does not help with, at this time. Let me know if you think that's important.

  4. How does the library authenticate to Apigee ?

    The library obtains an oauth token using the standard client_id and secret for administrative operations. The library caches the token into a filesystem file, for future use. The library runtime automatically caches the token, and refreshes the token as necessary, even during a single long-running script.

    By the way, you can use the cached token in other programs. For example, you could use the ./refreshToken.js script (See the examples) to obtain a fresh token, then use a bash script to read that token cache and perform some curl commands. Or just run refreshToken.js every morning and then any other program you want to use could pull the token from the cache.

  5. Could I use this thing in my Angular or React-based webapp?

    No, I haven't built it for that use case. It relies on node's fs module, and there are probably other dependencies that would prevent it from working correctly in a browser / webpack environment.

    If the community thinks this is important, let me know. I can take a look.

License and Copyright

This material is Copyright (c) 2016-2021 Google LLC, and is licensed under the Apache 2.0 source license.

Bugs

  • The tests are a work in progress

Related