apigee-edge-js
Version:
nodejs library for the administration API for Apigee (Edge and X and hybrid).
345 lines (323 loc) • 10.9 kB
JavaScript
// spec.js
// ------------------------------------------------------------------
// Copyright 2019-2023 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// DISCLAIMER
//
// This module wraps the /dapi API, which is at this moment undocumented and
// unsupported, and subject to change. That means this module may stop
// functioning at some point. Use it at your own risk!
//
const utility = require("./utility.js"),
common = require("./common.js"),
promiseWrap = require("./promiseWrap.js"),
//Readable = require('stream').Readable,
path = require("path"),
fs = require("fs"),
request = require("postman-request"),
urljoin = require("url-join"),
sprintf = require("sprintf-js").sprintf;
// uncomment to debug
//request.debug = true;
function Spec(conn) {
this.conn = conn;
}
function specUrlRoot(conn) {
return "https://apigee.com";
}
function urlBase(conn) {
return urljoin(specUrlRoot(conn), "/organizations", conn.orgname);
}
Spec.prototype.getHome = promiseWrap(function (cb) {
// GET :apigeecom/organizations/:org/specs/folder/home
var conn = this.conn;
common.insureFreshToken(conn, function (requestOptions) {
requestOptions.url = urljoin(urlBase(conn), "specs/folder/home");
if (conn.verbosity > 0) {
utility.logWrite(sprintf("GET %s", requestOptions.url));
}
request.get(requestOptions, common.callback(conn, [200], cb));
});
});
Spec.prototype.list = promiseWrap(function (cb) {
// GET :apigeecom/organizations/:org/specs/folder/home
// followed by a filter
var conn = this.conn;
common.insureFreshToken(conn, function (requestOptions) {
requestOptions.url = urljoin(urlBase(conn), "specs/folder/home");
if (conn.verbosity > 0) {
utility.logWrite(sprintf("GET %s", requestOptions.url));
}
request.get(requestOptions, function (error, response, body) {
if (error) {
utility.logWrite(error);
return cb(error, body);
}
let contents = JSON.parse(body).contents,
names = contents.map((x) => x.name);
return cb(null, names);
});
});
});
Spec.prototype.getMeta = promiseWrap(function (options, cb) {
// GET :apigeecom/organizations/:org/specs/folder/home
// ...followed by...
// GET :apigeecom/dapi/api/organizations/gaccelerate3/specs/doc/167649
let conn = this.conn,
name = options.name;
if (!name) {
return cb(new Error("missing name"));
}
common.insureFreshToken(conn, function (requestOptions) {
requestOptions.url = urljoin(urlBase(conn), "specs/folder/home");
if (conn.verbosity > 0) {
utility.logWrite(sprintf("GET %s", requestOptions.url));
}
request.get(requestOptions, function (error, response, body) {
if (error) {
utility.logWrite(error);
return cb(error, body);
}
let contents = JSON.parse(body).contents,
selected = contents.find((x) => x.name == name);
if (selected) {
requestOptions.url = urljoin(specUrlRoot(conn), selected.self);
if (conn.verbosity > 0) {
utility.logWrite(sprintf("GET %s", requestOptions.url));
}
request.get(requestOptions, common.callback(conn, [200], cb));
} else {
return cb(new Error("not found"));
}
});
});
});
Spec.prototype.get = promiseWrap(function (options, cb) {
// GET :apigeecom/organizations/:org/specs/folder/home
// ...followed by...
// GET :apigeecom/dapi/api/organizations/gaccelerate3/specs/doc/167649
let conn = this.conn,
name = options.name;
if (!name) {
return cb(new Error("missing name"));
}
common.insureFreshToken(conn, function (requestOptions) {
requestOptions.url = urljoin(urlBase(conn), "specs/folder/home");
if (conn.verbosity > 0) {
utility.logWrite(sprintf("GET %s", requestOptions.url));
}
request.get(requestOptions, function (error, response, body) {
if (error) {
utility.logWrite(error);
return cb(error, body);
}
let contents = JSON.parse(body).contents,
selected = contents.find((x) => x.name == name);
if (selected) {
requestOptions.url = urljoin(specUrlRoot(conn), selected.content);
if (conn.verbosity > 0) {
utility.logWrite(sprintf("GET %s", requestOptions.url));
}
request.get(requestOptions, common.callback(conn, [200, 204], cb));
} else {
return cb(new Error("not found"));
}
});
});
});
function verifyFilename(options) {
let filename = options.file || options.filename;
if (!filename) {
return [null, new Error("Missing filename")];
}
filename = path.resolve(filename);
if (!fs.existsSync(filename)) {
return [null, new Error("The resourcefile does not exist")];
}
return [filename, null];
}
// function readableFromString(s) {
// let stream = new Readable();
// stream.push(s);
// stream.push(null);
// return stream;
// }
function putContent(conn, requestOptions, docMetdata, newContent, cb) {
requestOptions.url = urljoin(specUrlRoot(conn), docMetdata.content);
requestOptions.headers["content-type"] = "text/plain";
requestOptions.body = newContent;
if (conn.verbosity > 0) {
utility.logWrite(sprintf("PUT %s", requestOptions.url));
}
let afterPut = function (e, result) {
if (conn.verbosity > 0) {
if (e) {
const util = require("util");
utility.logWrite("PUT error: " + util.format(e));
}
}
return cb(e, result);
};
// streaming (Chunked transfer encoding) is apparently not supported by the /dapi.
// So we cannot do
// fs.createReadStream(filename).pipe(request.put(...));
//
request.put(requestOptions, common.callback(conn, [200], afterPut));
}
Spec.prototype.create = promiseWrap(function (options, cb) {
// POST :apigeecom/dapi/api/organizations/:org/specs/doc
// .. followed by...
// PUT :apigeecom/dapi/api/organizations/:org/specs/doc/203024/content
// Content-type: text/plain
//
let conn = this.conn,
name = options.name,
filename;
if (!name) {
return cb(new Error("missing name for Doc"));
}
if (!options.content && !options.filename) {
return cb(new Error("missing content and filename for Doc"));
}
if (options.filename) {
let e;
[filename, e] = verifyFilename(options);
if (e) {
return cb(e);
}
}
if (conn.verbosity > 0) {
utility.logWrite(sprintf("Create Doc %s", name));
}
common.insureFreshToken(conn, function (requestOptions) {
requestOptions.url = urljoin(urlBase(conn), "specs/doc");
requestOptions.headers["content-type"] = "application/json";
requestOptions.body = JSON.stringify({ kind: "Doc", name });
if (conn.verbosity > 0) {
utility.logWrite(sprintf("POST %s", requestOptions.url));
}
request.post(requestOptions, function (error, response, body) {
if (error) {
utility.logWrite(error);
return cb(error, body);
}
let docMetadata = JSON.parse(body);
if (docMetadata.content) {
putContent(
conn,
requestOptions,
docMetadata,
filename ? fs.readFileSync(filename, "utf8") : options.content,
cb
);
} else {
return cb(new Error("content path was not found"));
}
});
});
});
Spec.prototype.update = promiseWrap(function (options, cb) {
// GET :apigeecom/organizations/:org/specs/folder/home
// ...followed by...
// PUT :apigeecom/dapi/api/organizations/:org/specs/doc/203024/content
// Content-type: text/plain
let conn = this.conn,
name = options.name,
filename;
if (!name) {
return cb(new Error("missing name for Doc"));
}
if (!options.content && !options.filename) {
return cb(new Error("missing content and filename for Doc"));
}
if (options.filename) {
let e;
[filename, e] = verifyFilename(options);
if (e) {
return cb(e);
}
}
common.insureFreshToken(conn, function (requestOptions) {
requestOptions.url = urljoin(urlBase(conn), "/specs/folder/home");
if (conn.verbosity > 0) {
utility.logWrite(sprintf("GET %s", requestOptions.url));
}
request.get(requestOptions, function (error, response, body) {
if (error) {
utility.logWrite(error);
return cb(error, body);
}
let contents = JSON.parse(body).contents,
docMetadata = contents.find((x) => x.name == name);
if (docMetadata) {
putContent(
conn,
requestOptions,
docMetadata,
filename ? fs.readFileSync(filename, "utf8") : options.content,
cb
);
} else {
return cb(new Error("name wasnot found"));
}
});
});
});
Spec.prototype.del = promiseWrap(function (options, cb) {
// GET :apigeecom/organizations/:org/specs/folder/home
// ...followed by...
// GET :apigeecom/dapi/api/organizations/gaccelerate3/specs/doc/203024
// ...followed by...
// DELETE :apigeecom/dapi/api/organizations/gaccelerate3/specs/doc/203024
let conn = this.conn,
name = options.name;
if (!name) {
return cb(new Error("missing name"));
}
common.insureFreshToken(conn, function (requestOptions) {
requestOptions.url = urljoin(urlBase(conn), "/specs/folder/home");
if (conn.verbosity > 0) {
utility.logWrite(sprintf("GET %s", requestOptions.url));
}
request.get(requestOptions, function (error, response, body) {
if (error) {
utility.logWrite(error);
return cb(error, body);
}
let contents = JSON.parse(body).contents,
selected = contents.find((x) => x.name == name);
if (selected) {
requestOptions.url = urljoin(specUrlRoot(conn), selected.self);
if (conn.verbosity > 0) {
utility.logWrite(sprintf("GET %s", requestOptions.url));
}
request.get(requestOptions, function (error, response, body) {
if (error) {
utility.logWrite(error);
return cb(error, body);
}
requestOptions.headers["if-match"] = response.headers.etag;
if (conn.verbosity > 0) {
utility.logWrite(sprintf("DELETE %s", requestOptions.url));
}
request.del(requestOptions, common.callback(conn, [200, 204], cb));
});
} else {
return cb(new Error("not found"));
}
});
});
});
module.exports = Spec;