项目作者: DinoChiesa

项目描述 :
Library of functions wrapping the RESTful Administrative API of Apigee Edge. Find some working example utilities at https://github.com/DinoChiesa/apigee-edge-js-examples
高级语言: JavaScript
项目地址: git://github.com/DinoChiesa/apigee-edge-js.git
创建时间: 2017-03-23T21:06:43Z
项目社区:https://github.com/DinoChiesa/apigee-edge-js

开源协议:Apache License 2.0

下载


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:

  1. const apigeejs = require('apigee-edge-js'),
  2. apigee = apigeejs.apigee;
  3. const options = {
  4. org : config.org,
  5. user: config.username,
  6. password: config.password
  7. };
  8. apigee.connect(options)
  9. .then ( org => {
  10. const options = {
  11. developerEmail : "JDimaggio@example.org",
  12. lastName : "Dimaggio",
  13. firstName : "Josephine",
  14. userName : "JD1"
  15. };
  16. return org.developers.create(options)
  17. .then( result => console.log('ok. developer: ' + JSON.stringify(result)) )
  18. })
  19. .catch ( error => console.log('error: ' + error) );

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

  1. const apigeejs = require('apigee-edge-js'),
  2. apigee = apigeejs.apigee;
  3. let options = { org : config.org, netrc: true };
  4. 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:

  1. const apigeejs = require('apigee-edge-js'),
  2. apigee = apigeejs.apigee;
  3. let options = { org : config.org, passcode: 'abcdefg', user: 'me@example.com' };
  4. apigee.connect(options).then(...);

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

  1. const apigeejs = require('apigee-edge-js'),
  2. apigee = apigeejs.apigee;
  3. let options = { org : 'my-org', apigeex: true, token: 'kjkdjskjskjs.abcdefg' };
  4. 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:

  1. const apigeejs = require('apigee-edge-js'),
  2. utility = apigeejs.utility,
  3. apigee = apigeejs.apigee;
  4. const options = {
  5. mgmtServer: config.mgmtserver,
  6. org : config.org,
  7. user: config.username,
  8. password: config.password
  9. };
  10. apigee.connect(options, function(e, org){
  11. if (e) {
  12. console.log(e);
  13. console.log(e.stack);
  14. process.exit(1);
  15. }
  16. const options = {
  17. developerEmail : "JDimaggio@example.org",
  18. lastName : "Dimaggio",
  19. firstName : "Josephine",
  20. userName : "JD1"
  21. };
  22. org.developers.create(options, function(e, result){
  23. if (e) {
  24. utility.logWrite(JSON.stringify(e));
  25. process.exit(1);
  26. }
  27. utility.logWrite(sprintf('ok. developer: %s', JSON.stringify(result, null, 2)));
  28. });
  29. });

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:

  1. 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:

  1. apigeeOrg.proxies.export({name:'proxyname'})
  2. .then ( result => {
  3. fs.writeFileSync(path.join('/Users/foo/export', result.filename), result.buffer);
  4. console.log('ok');
  5. })
  6. .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:

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

Export a specific revision of an API Proxy

promises:

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

callbacks:

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

Import an API Proxy from a Directory

promises:

  1. var options = {
  2. mgmtServer: mgmtserver,
  3. org : orgname,
  4. user: username,
  5. password:password
  6. };
  7. apigee.connect(options)
  8. .then ( org =>
  9. org.proxies.import({name:opt.options.name, source:'/tmp/path/dir'})
  10. .then ( result =>
  11. console.log('import ok. %s name: %s r%d', term, result.name, result.revision) ) )
  12. .catch ( error => {
  13. console.log(util.format(error));
  14. });

callbacks:

  1. var options = {
  2. mgmtServer: mgmtserver,
  3. org : orgname,
  4. user: username,
  5. password:password
  6. };
  7. apigee.connect(options, function(e, org){
  8. if (e) {
  9. console.log(JSON.stringify(e, null, 2));
  10. process.exit(1);
  11. }
  12. org.proxies.import({name:opt.options.name, source:'/tmp/path/dir'}, function(e, result) {
  13. if (e) {
  14. console.log('error: ' + JSON.stringify(e, null, 2));
  15. if (result) { console.log(JSON.stringify(result, null, 2)); }
  16. process.exit(1);
  17. }
  18. console.log('import ok. %s name: %s r%d', term, result.name, result.revision);
  19. });

Deploy an API Proxy

  1. var options = {
  2. name: 'proxy1',
  3. revision: 2,
  4. environment : 'test'
  5. };
  6. org.proxies.deploy(options)
  7. .then( result => console.log('deployment succeeded.') )
  8. .catch( error => console.log('deployment failed. ' + error) );

Get the latest revision of an API Proxy

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

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.

  1. apigee.connect(options)
  2. .then( org => {
  3. common.logWrite('connected');
  4. org.proxies.get({})
  5. .then( items => {
  6. const reducer = (promise, proxyname) =>
  7. promise .then( accumulator =>
  8. org.proxies
  9. .get({ name: proxyname })
  10. .then( ({revision}) => [ ...accumulator, {proxyname, revision:revision[revision.length-1]} ] )
  11. );
  12. return items
  13. .reduce(reducer, Promise.resolve([]))
  14. .then( arrayOfResults => console.log(JSON.stringify(arrayOfResults)) );
  15. });
  16. })
  17. .catch( e => console.error(e) );

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

Same approach as above.

  1. apigee.connect(options)
  2. .then( org => {
  3. common.logWrite('connected');
  4. org.proxies.get({})
  5. .then( items => {
  6. const reducer = (promise, proxyname) =>
  7. promise .then( accumulator =>
  8. org.proxies
  9. .get({ name: proxyname })
  10. .then( ({revision}) => [ ...accumulator, {proxyname, count:revision.length} ] )
  11. );
  12. return items
  13. .reduce(reducer, Promise.resolve([]))
  14. .then( arrayOfResults => console.log(JSON.stringify(arrayOfResults)) );
  15. });
  16. })
  17. .catch( e => console.error(e) );

Create a Keystore and load a Key and Cert

using callbacks:

  1. var options = {
  2. environment : 'test',
  3. name : 'keystore1'
  4. };
  5. org.keystores.create(options, function(e, result){
  6. if (e) { ... }
  7. console.log('ok. created');
  8. options.certFile = './mycert.cert';
  9. options.keyFile = './mykey.pem';
  10. options.alias = 'alias1';
  11. options.keyPassword = 'optional password for key file';
  12. org.keystores.importCert(options, function(e, result){
  13. if (e) { ... }
  14. console.log('ok. key and cert stored.');
  15. });
  16. });

Read and Update Mask Configs for an Organization

callbacks:

  1. const apigeejs = require('apigee-edge-js');
  2. const apigee = apigeejs.apigee;
  3. var options = {org : 'ORGNAME', netrc: true, verbosity : 1 };
  4. apigee.connect(options, function(e, org) {
  5. console.log('org: ' + org.conn.orgname);
  6. org.maskconfigs.get({name: 'default'}, function(e, body) {
  7. console.log(JSON.stringify(body));
  8. org.maskconfigs.set({ json : '$.store.book[*].author' }, function(e, body) {
  9. console.log(JSON.stringify(body));
  10. org.maskconfigs.add({ xml : '/apigee:Store/Employee' }, function(e, body) {
  11. console.log(JSON.stringify(body));
  12. org.maskconfigs.remove({ remove : ['xPathsFault','jSONPathsFault'] }, function(e, body) {
  13. console.log(JSON.stringify(body));
  14. org.maskconfigs.add({ variables : 'dino_var' }, function(e, body) {
  15. console.log(JSON.stringify(body));
  16. org.maskconfigs.add({ namespaces : { prefix:'apigee', value:'urn://apigee' } }, function(e, body) {
  17. console.log(JSON.stringify(body));
  18. });
  19. });
  20. });
  21. });
  22. });
  23. });
  24. });

with ES6 promises:

  1. const apigeejs = require('apigee-edge-js');
  2. const apigee = apigeejs.apigee;
  3. var options = {org : 'ORGNAME', netrc: true, verbosity : 1 };
  4. apigee.connect(options)
  5. .then ( (org) => {
  6. console.log('org: ' + org.conn.orgname);
  7. org.maskconfigs.get({name: 'default'})
  8. .then( (result) => console.log(JSON.stringify(result)) )
  9. .then( () => org.maskconfigs.set({ json : '$.store.book[*].author' }) )
  10. .then( (result) => console.log(JSON.stringify(result)) )
  11. .then( () => org.maskconfigs.add({ xml : '/apigee:Store/Employee' }) )
  12. .then( (result) => console.log(JSON.stringify(result)) )
  13. .then( () => org.maskconfigs.remove({ remove : ['xPathsFault','jSONPathsFault'] }) )
  14. .then( (result) => console.log(JSON.stringify(result)) )
  15. .then( () => org.maskconfigs.add({ variables : 'dino_var' }) )
  16. .then( (result) => console.log(JSON.stringify(result)) )
  17. .then( () => org.maskconfigs.add({ namespaces : { prefix:'apigee', value:'urn://apigee' } })
  18. .then( (result) => console.log(JSON.stringify(result)) )
  19. })
  20. .catch ( e => console.log(e) );

Create a Target Server

ES6 promises:

  1. const apigeejs = require('apigee-edge-js');
  2. const apigee = apigeejs.apigee;
  3. var options = {org : 'ORGNAME', netrc: true, verbosity : 1 };
  4. apigee.connect(options)
  5. .then ( org => {
  6. console.log('org: ' + org.conn.orgname);
  7. return org.targetservers.create({
  8. environment : 'test',
  9. target : {
  10. name : 'targetserver1',
  11. host: "api.example.com",
  12. port: 8080,
  13. sSLInfo : { enabled : false }
  14. }
  15. });
  16. })
  17. .catch ( e => console.log(e) );

Create a Developer App

  1. apigee.connect(connectOptions)
  2. .then ( org => {
  3. const options = {
  4. developerEmail,
  5. name : entityName,
  6. apiProduct : apiProducts[0]
  7. };
  8. org.developerapps.create(options)
  9. .then( result => {
  10. ...
  11. })
  12. .catch( e => {
  13. console.log('failed to create: ' + e);
  14. });
  15. });

Update attributes on a Developer App

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

Load data from a file into a KVM entry

  1. function loadKeyIntoMap(org) {
  2. var re = new RegExp('(?:\r\n|\r|\n)', 'g');
  3. var pemcontent = fs.readFileSync(opt.options.pemfile, "utf8").replace(re,'\n');
  4. var options = {
  5. env: opt.options.env,
  6. kvm: opt.options.mapname,
  7. key: opt.options.entryname,
  8. value: pemcontent
  9. };
  10. common.logWrite('storing new key \'%s\'', opt.options.entryname);
  11. return org.kvms.put(options)
  12. .then( _ => common.logWrite('ok. the key was loaded successfully.'));
  13. }
  14. apigee.connect(common.optToOptions(opt))
  15. .then ( org => {
  16. common.logWrite('connected');
  17. return org.kvms.get({ env })
  18. .then( maps => {
  19. if (maps.indexOf(mapname) == -1) {
  20. // the map does not yet exist
  21. common.logWrite('Need to create the map');
  22. return org.kvms.create({ env: opt.options.env, name: opt.options.mapname, encrypted:opt.options.encrypted})
  23. .then( _ => loadKeyIntoMap(org) );
  24. }
  25. common.logWrite('ok. the required map exists');
  26. return loadKeyIntoMap(org);
  27. });
  28. })
  29. .catch( e => console.log('Error: ' + util.format(e)));

Import an OpenAPI Spec

  1. apigee.connect(connectOptions)
  2. .then ( org =>
  3. org.specs.create({ name: 'mySpec', filename: '~/foo/bar/spec1.yaml' })
  4. .then( r => {
  5. console.log();
  6. console.log(r);
  7. }) )
  8. .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:

  1. {
  2. "org" : "my-org-name",
  3. "user": "username@example.com",
  4. "password": "password-goes-here",
  5. "verbosity": 1
  6. }

or:

  1. {
  2. "org" : "my-org-name",
  3. "netrc": true
  4. }

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

Then, to run tests:

  1. npm test

or

  1. node_modules/mocha/bin/mocha

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

  1. 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.

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