diff --git a/@types/index.d.ts b/@types/index.d.ts index f853620..b94fd10 100644 --- a/@types/index.d.ts +++ b/@types/index.d.ts @@ -295,6 +295,18 @@ export declare interface Domain extends DataStore { // queryPrincipals(query: Object, pagination?: Pagination, callback?: ResultCb>): Promise> } +export declare interface Agent extends PlatformObject +{ +} + +export declare interface AgentSession extends PlatformObject +{ +} + +export declare interface AgentMessage extends PlatformObject +{ +} + export declare interface Branch extends RepositoryObject { root: string tip: string @@ -411,6 +423,7 @@ export declare interface Session { refresh(callback?: (err: Error) => void): Promise disconnect(callback?: (err: Error) => void): Promise reauthenticate(reauthenticateFn: Function): void + useBand(bandId: string): void } export declare interface ApplicationSession extends Session { @@ -421,6 +434,8 @@ export declare interface RepositorySession extends Session { buildRepositoryReference(repository: TypedID|string, callback?: ResultCb): Promise createRepository(obj?: Object, callback?: ResultCb): Promise queryRepositories(query?: Object, pagination?: Pagination, callback?: ResultCb>): Promise> + readRepository(repository: TypedID|string, callback?: ResultCb): Promise + updateRepository(repository: TypedID|string, obj: Object, callback?: ResultCb): Promise } export declare interface BranchSession extends Session { @@ -436,6 +451,8 @@ export declare interface BranchSession extends Session { startBranchChanges(repository: TypedID|string, sourceBranch: TypedID|string, targetBranch: TypedID|string, pagination?: Pagination, opts?: BranchChangesOptions, callback?: ResultCb): Promise invalidateBranchChanges(repository: TypedID|string, branch: TypedID|string, callback?: ResultCb): Promise exportBranchChanges(repository: TypedID|string, sourceBranch: TypedID|string, targetBranch: TypedID|string, view?: BranchChangesView, callback?: ResultCb): Promise + mergeBranch(repository: TypedID|string, sourceBranch: TypedID|string, targetBranch: TypedID|string, options?: Object, callback?: ResultCb): Promise + startMergeBranch(repository: TypedID|string, sourceBranch: TypedID|string, targetBranch: TypedID|string, dryRun?: boolean, callback?: ResultCb): Promise } export declare interface DomainSession extends Session { @@ -458,6 +475,7 @@ export declare interface NodeSession extends Session { createNode(repository: TypedID|string, branch: TypedID|string, obj?: Object, options?: Object, callback?: ResultCb): Promise deleteNodes(repository: TypedID|string, branch: TypedID|string, nodes: string|Array|Array, callback?: ResultCb): Promise updateNodes(repository: TypedID|string, branch: TypedID|string, nodes: Array, callback?: ResultCb): Promise + createNodes(repository: TypedID|string, branch: TypedID|string, nodes: Array, callback?: ResultCb): Promise queryNodeRelatives(repository: TypedID|string, branch: TypedID|string, node: TypedID|string, associationTypeQName: string, associationDirection: string, query?: Object, pagination?: Pagination, callback?: ResultCb>): Promise> queryNodeChildren(repository: TypedID|string, branch: TypedID|string, node: TypedID|string, query: Object, pagination?: Pagination, callback?: ResultCb>): Promise> @@ -470,7 +488,7 @@ export declare interface NodeSession extends Session { unassociateChild(repository: TypedID|string, branch: TypedID|string, node: TypedID|string, childNode: TypedID|string, callback?: ResultCb): Promise deleteNode(repository: TypedID|string, branch: TypedID|string, node: TypedID|string, callback?: ResultCb): Promise updateNode(repository: TypedID|string, branch: TypedID|string, node: T & TypedID, callback?: ResultCb): Promise - patchNode(repository: TypedID|string, branch: TypedID|string, node: TypedID|string, patchObject: Object, callback?: ResultCb): Promise + patchNode(repository: TypedID|string, branch: TypedID|string, node: TypedID|string, patchArray: Array, callback?: ResultCb): Promise addNodeFeature(repository: TypedID|string, branch: TypedID|string, node: TypedID|string, featureId: string, config?: Object, callback?: ResultCb): Promise removeNodeFeature(repository: TypedID|string, branch: TypedID|string, node: TypedID|string, featureId: string, callback?: ResultCb): Promise refreshNode(repository: TypedID|string, branch: TypedID|string, node: TypedID|string, callback?: ResultCb): Promise @@ -490,6 +508,18 @@ export declare interface NodeSession extends Session { startCopyNodes(repository: TypedID|string, sourceBranch: TypedID|string, targetBranch: TypedID|string, nodeIds: Array, config?: SyncNodesConfig, callback?: ResultCb): Promise } +export declare interface SidekickSession extends Session { + readAgent(agent: TypedID|string, callback?: ResultCb): Promise + queryAgents(query: Object, pagination?: Pagination, callback?: ResultCb>): Promise> + createAgent(obj?: Object, callback?: ResultCb): Promise + deleteAgent(agent: TypedID|string, callback?: ResultCb): Promise + updateAgent(agent: Agent, callback?: ResultCb): Promise + + openAgentSession(obj: object, callback?: ResultCb): Promise + startAgentSessionRequest(agent: AgentSession, obj: string, callback?: ResultCb): Promise + readAgentSessionMessage(agent: AgentSession, agentMessageId: string, callback?: ResultCb): Promise +} + export declare interface PrincipalSession extends Session { readPrincipal(domain: TypedID|string, principalId: string, callback?: ResultCb): Promise queryPrincipals(domain: TypedID|string, query: Object, pagination?: Pagination, callback?: ResultCb>): Promise> @@ -537,6 +567,8 @@ export declare interface JobSession extends Session { killJob(jobId: string|TypedID, callback?: ResultCb): Promise downloadJobAttachment(jobId: string|TypedID, attachmentId?: string, opts?: DownloadJobOptions, callback?: ResultCb): Promise waitForJobCompletion(job: string|TypedID, callback?: ResultCb): Promise + pollForJobCompletion(job: string|TypedID, callback?: ResultCb): Promise + pollJob(jobId: string|TypedID, callback?: ResultCb): Promise } export declare interface TransferSession extends Session { @@ -565,6 +597,13 @@ export declare interface ArchiveSession extends Session { downloadArchive(group: string, artifact: string, version: string, vault?: string|TypedID, callback?: ResultCb): Promise } +export declare interface EditorialSession extends Session { + startEditorialFlow(repository: TypedID|string, rootBranch: TypedID|string, operation: string, flowKey: string, properties?: Object, workflowData?: Object, swimlanesObject?: Object, nodeRefArray?:Array): Promise + readCurrentTaskForEditorialFlow(workflowId: string): Promise + transitionEditorialFlow(workflowId: string, route: string): Promise +} + + export declare interface TrackerSession extends Session { trackPage(repositoryId: string, branchId: string, page: PageRendition): void } @@ -580,6 +619,7 @@ export declare interface ReleaseSession extends Session { export declare interface PlatformSession extends Session { readPlatform(callback?: ResultCb): Promise + readAuthInfo(callback?: ResultCb): Promise getPlatformId(callback?: ResultCb): Promise buildPlatformReference(callback?: ResultCb): Promise } @@ -599,7 +639,7 @@ export declare interface UtilitySession extends DefaultSession { } -export declare type DefaultSession = ApplicationSession & ArchiveSession & RepositorySession & BranchSession & DomainSession & GraphQLSession & NodeSession & PrincipalSession & ProjectSession & StackSession & WorkflowSession & ChangesetSession & JobSession & TransferSession & TrackerSession & ReleaseSession & PlatformSession; +export declare type DefaultSession = ApplicationSession & ArchiveSession & RepositorySession & BranchSession & DomainSession & GraphQLSession & NodeSession & PrincipalSession & ProjectSession & StackSession & WorkflowSession & ChangesetSession & JobSession & TransferSession & TrackerSession & ReleaseSession & PlatformSession & EditorialSession & SidekickSession; diff --git a/README.md b/README.md index e87f7ce..05624a4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # cloudcms-javascript-driver -Updated Cloud CMS JS Engine using modern ECMAScript and Promises +Cloud CMS JavaScript Driver with support for ECMAScript Async, Promises and Callbacks. + +For formal support and assistance, please visit https://gitana.io. ## Installation @@ -250,6 +252,22 @@ You can also manually expire the issued Access and Refresh Token, like this: await session.disconnect(); ``` +### Band + +If you have multiple bands configured, you can configure your Session to perform all of its API calls +against a designated band, like this: + +```javascript +session = await cloudcms.connect(); +session.useBand("production"); +``` + +To revert back to the default band: + +```javascript +session.useBand(null); +``` + ### TypeScript The `cloudcms-javascript-driver` includes a TypeScript type interface to improve your editing experience and allow better integration in your TypeScript apps. @@ -297,6 +315,11 @@ const results: Rows = await session.queryNodes(repositoryId, branchI This library uses Mocha and Chai for testing. To test, first add `gitana.json` to the project root. +This file's `baseURL` should point to a local Gitana API instance running Gitana 4.1 or higher. +Typically, the `baseURL` is set to `http://localhost:8080` for testing. + +Within the Gitana API source, you can run the `DriverTest` test to install driver credentials (which +should be put into `gitana.json`). To run all tests: @@ -304,12 +327,16 @@ To run all tests: npm run alltests ``` -To run a single test (`node`): +To run a single test (`transfer10`): ``` -npm run test node +npx mocha 'test/**/transfer10.ts' --recursive --timeout 120000 --watch-extensions ts,js ``` +The unit tests assume a 4.0 compatible API server. +Drop your `gitana.json` file into the project root folder. + + ## Proxy Configure the driver to use an HTTP or HTTPS proxy using the following environment variables to specify @@ -334,6 +361,31 @@ process.env.HTTPS_PROXY = "http://localhost:9090"; })(); ``` +In addition, the following environment variables are supported to prevent certain domains from routing +through the HTTP/HTTPS PROXY endpoints. + +- `NO_PROXY` +- `*_PROXY` + +For more information, see: +https://github.com/Rob--W/proxy-from-env?tab=readme-ov-file#environment-variables + +Example: + +```javascript +const cloudcms = require("cloudcms"); + +process.env.HTTPS_PROXY = "http://localhost:9090"; + +// route everything through "localhost:9090" except connections to https://api.cloudcms.com +process.env.NO_PROXY = "api.cloudcms.com"; + +(async function() { + var session = await cloudcms.connect(); + console.log("Connected!"); +})(); +``` + ## Custom Engine Use the `engine()` method to select the underlying HTTP client engine to use for connectivity to the API. @@ -399,3 +451,41 @@ TODO: how to configure Memory vs Redis ## Custom Cache TODO: how to configure custom caching for JSON responses + +## Jobs + +Jobs are long-running tasks that are loaded onto Gitana's distributed job engine. They run within +queues and execute in the background. When you send a request to start a job, the request will +return right away with a Job ID that you can use to poll for completion. + +In Gitana 3.2 and before, your code can be written like this: + +``` +var startedJob = await session.startCreateProject({ + title: "My Test Project" +}); +var completedJob = await session.waitForJobCompletion(startedJob._doc); +var projectId = completedJob["created-project-id"]; +``` + +In Gitana 4.0 and beyond, there is a slight difference. You must ues the `pollForJobCompletion` method, +like this: + +``` +var startedJob = await session.startCreateProject({ + title: "My Test Project" +}); +var completedJob = await session.pollForJobCompletion(startedJob._doc); +var projectId = completedJob._result["created-project-id"]; +``` + +## Documentation + +Please visit: + +https://gitana.io/documentation/gitana/4.0/developers/cookbooks/javascript2.html +https://gitana.io/documentation/gitana/4.0/developers/drivers/javascript.html + +## Support + +For support, please visit https://gitana.io diff --git a/package.json b/package.json index 80208b3..ac70ead 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cloudcms", - "version": "0.2.23", + "version": "0.2.38", "repository": { "type": "git", "url": "git://github.com/gitana/cloudcms-javascript-driver.git" @@ -31,14 +31,14 @@ "typescript": "^5.4.5" }, "dependencies": { - "axios": "^1.6.8", - "axios-oauth-client": "^2.2.0", + "axios": "^1.7.2", "cheerio": "^1.0.0-rc.10", "form-data": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.4", "moment": "^2.30.1", - "node-fetch": "^2.7.0" + "node-fetch": "^2.7.0", + "proxy-from-env": "^1.1.0" }, "scripts": { "alltests": "mocha 'test/**/*.ts' --recursive --timeout 120000 --watch-extensions ts,js", diff --git a/src/engine.js b/src/engine.js index 250def2..8a7b0f7 100644 --- a/src/engine.js +++ b/src/engine.js @@ -1,6 +1,9 @@ var Helper = require("./helper"); const moment = require("moment/moment"); -const {refreshToken, ownerCredentials} = require("axios-oauth-client"); + +const { HttpsProxyAgent } = require("https-proxy-agent"); +const { HttpProxyAgent } = require("http-proxy-agent"); +const { getProxyForUrl } = require("proxy-from-env"); var ONE_HOUR_MS = 1000 * 60 * 60; @@ -201,8 +204,30 @@ class Engine }); }; + // checks HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment variables + // determines whether a proxy should be configured (otherwise null) + // sets proxy settings onto options + this.applyAgent = function(url, fn) + { + // checks HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment variables + // determines whether a proxy should be configured (otherwise null) + var proxy = getProxyForUrl(url); + if (proxy) + { + // bind in http proxy + if (url.startsWith("https:")) + { + fn(null, new HttpsProxyAgent(proxy)); + } + else if (url.startsWith("http:")) + { + fn(new HttpProxyAgent(proxy)); + } + } + }; + // @abstract - this.buildGetHandler = function(uri, qs) + this.buildGetHandler = function(uri, qs, headers) { return function(done) { @@ -211,7 +236,7 @@ class Engine }; // @abstract - this.buildPostHandler = function(uri, qs, payload) + this.buildPostHandler = function(uri, qs, payload, headers) { return function(done) { @@ -220,7 +245,7 @@ class Engine }; // @abstract - this.buildPutHandler = function(uri, qs, payload) + this.buildPutHandler = function(uri, qs, payload, headers) { return function(done) { @@ -229,7 +254,7 @@ class Engine }; // @abstract - this.buildDeleteHandler = function(uri, qs) + this.buildDeleteHandler = function(uri, qs, headers) { return function(done) { @@ -238,7 +263,7 @@ class Engine }; // @abstract - this.buildPatchHandler = function(uri, qs, payload) + this.buildPatchHandler = function(uri, qs, payload, headers) { return function(done) { @@ -247,7 +272,16 @@ class Engine }; // @abstract - this.buildMultipartPostHandler = function(uri, qs, payload) + this.buildMultipartPostHandler = function(uri, qs, payload, headers) + { + return function(done) + { + // TODO + } + } + + // @abstract + this.buildDownloadHandler = function(uri, params, headers) { return function(done) { @@ -281,10 +315,11 @@ class Engine * * @param uri * @param qs + * @param headers */ - get(uri, qs, callback) + get(uri, qs, headers, callback) { - var fn = this.buildGetHandler(uri, qs); + var fn = this.buildGetHandler(uri, qs, headers); // support for callback approach if (callback && Helper.isFunction(callback)) @@ -315,10 +350,11 @@ class Engine * @param uri * @param qs * @param payload + * @param headers */ - post(uri, qs, payload, callback) + post(uri, qs, payload, headers, callback) { - var fn = this.buildPostHandler(uri, qs, payload); + var fn = this.buildPostHandler(uri, qs, payload, headers); // support for callback approach if (callback && Helper.isFunction(callback)) @@ -349,10 +385,11 @@ class Engine * @param uri * @param qs * @param payload + * @param headers */ - put(uri, qs, payload, callback) + put(uri, qs, payload, headers, callback) { - var fn = this.buildPutHandler(uri, qs, payload); + var fn = this.buildPutHandler(uri, qs, payload, headers); // support for callback approach if (callback && Helper.isFunction(callback)) @@ -382,10 +419,11 @@ class Engine * * @param uri * @param qs + * @param headers */ - del(uri, qs, callback) + del(uri, qs, headers, callback) { - var fn = this.buildDelHandler(uri, qs); + var fn = this.buildDeleteHandler(uri, qs, headers); // support for callback approach if (callback && Helper.isFunction(callback)) @@ -416,10 +454,11 @@ class Engine * @param uri * @param qs * @param payload + * @param headers */ - patch(uri, qs, payload, callback) + patch(uri, qs, payload, headers, callback) { - var fn = this.buildPatchHandler(uri, qs, payload); + var fn = this.buildPatchHandler(uri, qs, payload, headers); // support for callback approach if (callback && Helper.isFunction(callback)) @@ -449,11 +488,13 @@ class Engine * requestObject * * @param uri - * @param parts + * @param qs + * @param payload + * @param headers */ - multipartPost(uri, qs, formData, callback) + multipartPost(uri, qs, payload, headers, callback) { - var fn = this.buildMultipartPostHandler(uri, qs, formData); + var fn = this.buildMultipartPostHandler(uri, qs, payload, headers); // support for callback approach if (callback && Helper.isFunction(callback)) @@ -481,14 +522,13 @@ class Engine /** * @extension_point * - * - * * @param uri * @param qs + * @param headesr */ - download(uri, qs, callback) + download(uri, qs, headers, callback) { - var fn = this.buildDownloadHandler(uri, qs); + var fn = this.buildDownloadHandler(uri, qs, headers); // support for callback approach if (callback && Helper.isFunction(callback)) @@ -571,16 +611,23 @@ class Engine requestObject.headers = requestObject.headers || {} - if (tokenType === 'bearer' && !requestObject.headers.Authorization) { + if (tokenType && tokenType.toLowerCase() === 'bearer' && !requestObject.headers.Authorization) + { requestObject.headers.Authorization = 'Bearer ' + accessToken - } else { + } + else + { var parts = requestObject.url.split('#'); - var token = 'access_token=' + accessToken; + //var token = 'access_token=' + accessToken; + var token = ''; var url = parts[0].replace(/[?&]access_token=[^&#]/, ''); var fragment = parts[1] ? '#' + parts[1] : ''; // Prepend the correct query string parameter to the url. - requestObject.url = url + (url.indexOf('?') > -1 ? '&' : '?') + token + fragment; + if (token || fragment) + { + requestObject.url = url + (url.indexOf('?') > -1 ? '&' : '?') + token + fragment; + } // Attempt to avoid storing the url in proxies, since the access token // is exposed in the query parameters. diff --git a/src/engines/axios/engine.js b/src/engines/axios/engine.js index 8a290e4..ed15490 100644 --- a/src/engines/axios/engine.js +++ b/src/engines/axios/engine.js @@ -3,11 +3,6 @@ var Helper = require("../../helper"); var axios = require('axios'); -const { HttpsProxyAgent} = require("https-proxy-agent"); -const { HttpProxyAgent} = require("http-proxy-agent"); - -var { ownerCredentials, refreshToken } = require("axios-oauth-client"); - class AxiosEngine extends Engine { constructor(config, credentials, storage, options) @@ -30,20 +25,20 @@ class AxiosEngine extends Engine } } - if (process.env.HTTP_PROXY) { - axiosClientConfig.proxy = false; - axiosClientConfig.httpAgent = new HttpProxyAgent(process.env.HTTP_PROXY); - } + self.applyAgent(config.baseURL, function(httpProxyAgent, httpsProxyAgent) { - if (process.env.HTTPS_PROXY) { - axiosClientConfig.proxy = false; - axiosClientConfig.httpsAgent = new HttpsProxyAgent(process.env.HTTPS_PROXY); - } + if (httpProxyAgent) { + axiosClientConfig.proxy = false; + axiosClientConfig.httpAgent = httpProxyAgent; + } else if (httpsProxyAgent) { + axiosClientConfig.proxy = false; + axiosClientConfig.httpsAgent = httpsProxyAgent; + } + }); // build axios client var client = self.client = axios.create(axiosClientConfig); - //// this.doRequest = function(options, callback) @@ -55,6 +50,7 @@ class AxiosEngine extends Engine var err = null; var signedOptions = self.incoming(options); + return client.request(signedOptions) .then(function(_response) { response = _response; @@ -100,7 +96,7 @@ class AxiosEngine extends Engine } // @abstract - this.buildGetHandler = function(uri, params) + this.buildGetHandler = function(uri, params, headers) { var self = this; @@ -121,6 +117,14 @@ class AxiosEngine extends Engine } } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + self.request(options, function(err, response, data, stats) { if (self.isNotFound(err, response)) { @@ -142,7 +146,7 @@ class AxiosEngine extends Engine }; // @abstract - this.buildPostHandler = function(uri, params, payload) + this.buildPostHandler = function(uri, params, payload, headers) { var self = this; @@ -177,6 +181,14 @@ class AxiosEngine extends Engine options.data = payload; } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + self.request(options, function(err, response, data, stats) { if (err) { @@ -189,7 +201,7 @@ class AxiosEngine extends Engine }; // @abstract - this.buildPutHandler = function(uri, params, payload) + this.buildPutHandler = function(uri, params, payload, headers) { var self = this; @@ -216,6 +228,14 @@ class AxiosEngine extends Engine options.data = JSON.stringify(payload); } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + self.request(options, function(err, response, data, stats) { if (err) { @@ -228,7 +248,7 @@ class AxiosEngine extends Engine }; // @abstract - this.buildDelHandler = function(uri, params) + this.buildDeleteHandler = function(uri, params, headers) { var self = this; @@ -249,6 +269,14 @@ class AxiosEngine extends Engine } } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + self.request(options, function(err, response, data, stats) { if (err) { @@ -262,7 +290,7 @@ class AxiosEngine extends Engine }; // @abstract - this.buildPatchHandler = function(uri, params, payload) + this.buildPatchHandler = function(uri, params, payload, headers) { var self = this; @@ -286,7 +314,15 @@ class AxiosEngine extends Engine if (payload) { options.headers["Content-Type"] = "application/json"; - options.data = JSON.stringify(data); + options.data = JSON.stringify(payload); + } + + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } } self.request(options, function(err, response, data, stats) { @@ -300,7 +336,7 @@ class AxiosEngine extends Engine } }; - this.buildMultipartPostHandler = function(uri, params, payload) + this.buildMultipartPostHandler = function(uri, params, payload, headers) { var self = this; @@ -338,6 +374,14 @@ class AxiosEngine extends Engine options.data = payload; } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + options.maxBodyLength = 1000000000; self.request(options, function(err, response, data, stats) { @@ -352,7 +396,7 @@ class AxiosEngine extends Engine }, // @abstract - this.buildDownloadHandler = function(uri, params) + this.buildDownloadHandler = function(uri, params, headers) { var self = this; @@ -374,6 +418,14 @@ class AxiosEngine extends Engine } } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + self.request(options, function(err, response, data, stats) { if (self.isNotFound(err, response)) { @@ -396,12 +448,91 @@ class AxiosEngine extends Engine this.buildGetRefreshToken = function() { - return refreshToken(client, [config.baseURL, "oauth/token"].join("/"), config.clientKey, config.clientSecret); + var self = this; + + return function(refreshToken, optionalScopes) + { + return new Promise(function(resolve, reject) { + + var url = [config.baseURL, "oauth/token"].join("/"); + var clientKey = config.clientKey; + var clientSecret = config.clientSecret; + + if (!optionalScopes || optionalScopes.length === 0) { + optionalScopes = "api"; + } + var scopes = optionalScopes.join(","); + + var params = { + "grant_type": "refresh_token", + "refresh_token": refreshToken, + "scope": scopes + }; + + var headers = { + "Authorization": "Basic " + Buffer.from(clientKey + ":" + clientSecret, "utf-8").toString("base64") + }; + + self.doRequest({ + "url": url, + "method": "POST", + "params": params, + "headers": headers + }, function(err, response, data, stats) { + + if (err) { + return reject(err); + } + + resolve(data); + }); + }); + }; }; this.buildGetOwnerCredentials = function() { - return ownerCredentials(client, [config.baseURL, "oauth/token"].join("/"), config.clientKey, config.clientSecret); + var self = this; + + return function(username, password, optionalScopes) + { + return new Promise(function(resolve, reject) { + + var url = [config.baseURL, "oauth/token"].join("/"); + var clientKey = config.clientKey; + var clientSecret = config.clientSecret; + + if (!optionalScopes || optionalScopes.length === 0) { + optionalScopes = "api"; + } + var scopes = optionalScopes.join(","); + + var params = { + "grant_type": "password", + "username": username, + "password": password, + "scope": scopes + }; + + var headers = { + "Authorization": "Basic " + Buffer.from(clientKey + ":" + clientSecret, "utf-8").toString("base64") + }; + + self.doRequest({ + "url": url, + "method": "POST", + "params": params, + "headers": headers + }, function(err, response, data, stats) { + + if (err) { + return reject(err); + } + + resolve(data); + }); + }); + }; }; } } diff --git a/src/engines/fetch/engine.js b/src/engines/fetch/engine.js index 850646f..8bb1412 100644 --- a/src/engines/fetch/engine.js +++ b/src/engines/fetch/engine.js @@ -1,9 +1,5 @@ var Engine = require("../../engine"); var Helper = require("../../helper"); -var nodeFetch = require("node-fetch"); - -const { HttpsProxyAgent} = require("https-proxy-agent"); -const { HttpProxyAgent} = require("http-proxy-agent"); class FetchEngine extends Engine { @@ -11,13 +7,22 @@ class FetchEngine extends Engine { super(config, credentials, storage, options); + var self = this; if (options && options.fetch) { this.fetch = options.fetch } else { - this.fetch = nodeFetch; + const [major, minor, patch] = process.versions.node.split('.').map(Number) + if (major < 21) + { + this.fetch = require("node-fetch");; + } + else + { + this.fetch = fetch; + } } this.doRequest = (options, callback) => @@ -32,17 +37,17 @@ class FetchEngine extends Engine var params = new URLSearchParams(options.params); options.url += "?" + params.toString(); - // Proxy - if (process.env.HTTP_PROXY) { - options.agent = new HttpProxyAgent(process.env.HTTP_PROXY); - } - - if (process.env.HTTPS_PROXY) { - options.agent = new HttpsProxyAgent(process.env.HTTPS_PROXY); - } - var signedOptions = this.incoming(options); + self.applyAgent(signedOptions.url, function(httpProxyAgent, httpsProxyAgent) { + + if (httpProxyAgent) { + signedOptions.agent = httpProxyAgent; + } else if (httpsProxyAgent) { + signedOptions.agent = httpsProxyAgent; + } + }); + // fetch this.fetch(options.url, signedOptions) .then(async function(_response) { @@ -111,7 +116,7 @@ class FetchEngine extends Engine }; // @abstract - this.buildGetHandler = function(uri, params) + this.buildGetHandler = function(uri, params, headers) { var self = this; @@ -132,6 +137,14 @@ class FetchEngine extends Engine } } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + self.request(options, function(err, response, data, stats) { if (self.isNotFound(err, response)) @@ -154,7 +167,7 @@ class FetchEngine extends Engine }; // @abstract - this.buildPostHandler = function(uri, params, payload) + this.buildPostHandler = function(uri, params, payload, headers) { var self = this; @@ -189,6 +202,13 @@ class FetchEngine extends Engine // options.body = payload; } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } self.request(options, function(err, response, data, stats) { if (err) { @@ -233,6 +253,14 @@ class FetchEngine extends Engine options.body = JSON.stringify(payload); } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + self.request(options, function(err, response, data, stats) { if (err) { @@ -250,7 +278,7 @@ class FetchEngine extends Engine }; // @abstract - this.buildDelHandler = function(uri, params) + this.buildDeleteHandler = function(uri, params, headers) { var self = this; @@ -271,6 +299,14 @@ class FetchEngine extends Engine } } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + self.request(options, function(err, response, data, stats) { if (err) { @@ -288,7 +324,7 @@ class FetchEngine extends Engine }; // @abstract - this.buildPatchHandler = function(uri, params, payload) + this.buildPatchHandler = function(uri, params, payload, headers) { var self = this; @@ -312,7 +348,15 @@ class FetchEngine extends Engine if (payload) { options.headers["Content-Type"] = "application/json"; - options.body = JSON.stringify(data); + options.body = JSON.stringify(payload); + } + + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } } self.request(options, function(err, response, data, stats) { @@ -331,9 +375,8 @@ class FetchEngine extends Engine } }; - // This is the bit that still isn't working! - this.buildMultipartPostHandler = function(uri, params, payload) + this.buildMultipartPostHandler = function(uri, params, payload, headers) { var self = this; @@ -370,6 +413,14 @@ class FetchEngine extends Engine options.body = payload; } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + self.request(options, function(err, response, data, stats) { if (err) { @@ -387,7 +438,7 @@ class FetchEngine extends Engine }, // @abstract - this.buildDownloadHandler = function(uri, params) + this.buildDownloadHandler = function(uri, params, headers) { var self = this; @@ -409,6 +460,14 @@ class FetchEngine extends Engine } } + if (headers) + { + for (var k in headers) + { + options.headers[k] = headers[k]; + } + } + self.request(options, function(err, response, data, stats) { if (self.isNotFound(err, response)) { @@ -429,7 +488,6 @@ class FetchEngine extends Engine } }; - // Not Implemented this.buildGetRefreshToken = function() { var self = this; @@ -474,7 +532,6 @@ class FetchEngine extends Engine }; }; - // Not Implemented this.buildGetOwnerCredentials = function() { var self = this; diff --git a/src/objects/Application.js b/src/objects/Application.js index a8e0dd4..04cc52c 100644 --- a/src/objects/Application.js +++ b/src/objects/Application.js @@ -8,6 +8,7 @@ class Application extends AbstractObject constructor(session, obj) { super(); + Object.assign(this, obj); this.session = session; diff --git a/src/objects/Node.js b/src/objects/Node.js index 2beb710..e2d017c 100644 --- a/src/objects/Node.js +++ b/src/objects/Node.js @@ -15,6 +15,7 @@ const NODE_FNS = [ "removeNodeFeature", "refreshNode", "changeNodeQName", + "changeNodeTypeQName", "nodeTree", "resolveNodePath", "resolveNodePaths", @@ -25,7 +26,9 @@ const NODE_FNS = [ "deleteAttachment", "listVersions", "readVersion", - "restoreVersion" + "restoreVersion", + "publishNodes", + "unpublishNodes" ]; class Node extends AbstractObject { diff --git a/src/session/default/methods/branch.js b/src/session/default/methods/branch.js index a50f2a5..d5c7a20 100644 --- a/src/session/default/methods/branch.js +++ b/src/session/default/methods/branch.js @@ -211,6 +211,51 @@ module.exports = function(Session) return this.download(`/repositories/${repositoryId}/branches/${targetBranchId}/changes/export`, params, callback); } + + // merge + + mergeBranch(repository, sourceBranch, targetBranch, options) + { + var repositoryId = this.acquireId(repository); + var sourceBranchId = this.acquireId(sourceBranch); + var targetBranchId = this.acquireId(targetBranch); + var callback = this.extractOptionalCallback(arguments); + + var params = { + "id": sourceBranchId + }; + + var payload = {}; + if (typeof(options) === "object") + { + payload = JSON.parse(JSON.stringify(options)); + } + + return this.post(`/repositories/${repositoryId}/branches/${targetBranchId}/merge`, params, payload, callback); + } + + startMergeBranch(repository, sourceBranch, targetBranch, dryRun) + { + var repositoryId = this.acquireId(repository); + var sourceBranchId = this.acquireId(sourceBranch); + var targetBranchId = this.acquireId(targetBranch); + var callback = this.extractOptionalCallback(arguments); + + if (typeof(dryRun) !== "boolean") + { + dryRun = false; + } + + var params = { + "id": sourceBranchId, + "dryRun": dryRun + }; + + var payload = {}; + + return this.post(`/repositories/${repositoryId}/branches/${targetBranchId}/merge/start`, params, payload, callback); + } + } return BranchSession; diff --git a/src/session/default/methods/editorial.js b/src/session/default/methods/editorial.js new file mode 100644 index 0000000..4f54d52 --- /dev/null +++ b/src/session/default/methods/editorial.js @@ -0,0 +1,54 @@ +module.exports = function(Session) +{ + class EditorialSession extends Session + { + startEditorialFlow(repository, rootBranch, operation, flowKey, properties, workflowData, swimlanesObject, nodeRefArray) + { + var repositoryId = this.acquireId(repository); + var rootBranchId = this.acquireId(rootBranch); + var callback = this.extractOptionalCallback(arguments); + + var query = {}; + + var payload = {}; + payload.repositoryId = repositoryId; + payload.branchId = rootBranchId; + payload.operation = operation; + payload.flowKey = flowKey; + if (properties) { + payload.properties = properties; + } + if (workflowData) { + payload.workflowData = workflowData; + } + if (swimlanesObject) { + payload.swimlanes = swimlanesObject; + } + if (nodeRefArray) { + payload.nodeRefs = nodeRefArray; + } + + return this.post("/editorial/flows/start", query, payload, callback); + } + + readCurrentTaskForEditorialFlow(workflowId) + { + var callback = this.extractOptionalCallback(arguments); + + return this.get("/editorial/flows/" + workflowId + "/task", {}, callback); + } + + transitionEditorialFlow(workflowId, route) + { + var callback = this.extractOptionalCallback(arguments); + + var query = { + "route": route + }; + + return this.post("/editorial/flows/" + workflowId + "/transition", query, callback); + } + } + + return EditorialSession; +}; \ No newline at end of file diff --git a/src/session/default/methods/job.js b/src/session/default/methods/job.js index 226dc7f..1f7f37c 100644 --- a/src/session/default/methods/job.js +++ b/src/session/default/methods/job.js @@ -38,6 +38,14 @@ module.exports = function(Session) return this.download(`/jobs/${jobId}/attachments/${attachmentId}`, opts, callback); } + async pollJob(job) + { + var jobId = this.acquireId(job); + var callback = this.extractOptionalCallback(arguments); + + return this.get("/jobs/" + jobId + "/poll", callback); + } + async waitForJobCompletion(job) { const jobId = this.acquireId(job); @@ -73,6 +81,42 @@ module.exports = function(Session) return job; } + async pollForJobCompletion(job) + { + var self = this; + + const jobId = this.acquireId(job); + var callback = this.extractOptionalCallback(arguments); + + const _wait = async () => { + job = await self.pollJob(jobId); + if (job.state === "FINISHED") { + return; + } + else if (job.state === "ERROR") { + throw new Error(`Job failed: ${jobId}`) + } + else { + await new Promise(r => setTimeout(r, 1000)); // wait a second + return _wait(); + } + } + + try + { + await _wait(); + } + catch (err) + { + if (callback) callback.call(self, err, job); + throw err; + } + + // in 4.0, it might be better to hand back the job result which is a separate record + if (callback) callback.call(self, null, job); + return job; + } + } return JobSession; diff --git a/src/session/default/methods/node.js b/src/session/default/methods/node.js index 80ed9ea..3a02bca 100644 --- a/src/session/default/methods/node.js +++ b/src/session/default/methods/node.js @@ -141,7 +141,6 @@ module.exports = function(Session) return this.post("/repositories/" + repositoryId + "/branches/" + branchId + "/nodes/find", pagination, config, callback); } - /** * Creates a node. @@ -193,6 +192,37 @@ module.exports = function(Session) return this.post("/repositories/" + repositoryId + "/branches/" + branchId + "/nodes", qs, obj, callback); } + /** + * Creates multiple nodes. + * + * @param repository + * @param branch + * @param objects + * @returns {*} + */ + createNodes(repository, branch, objects) + { + var repositoryId = this.acquireId(repository); + var branchId = this.acquireId(branch); + var callback = this.extractOptionalCallback(arguments); + + var formData = new FormData(); + + for (var i = 0; i < objects.length; i++) + { + var obj = objects[i]; + + formData.append("node-" + i, obj, { + "contentType": "application/json", + "filename": "node-i" + i + }); + } + + var qs = {}; + + return this.multipartPost("/repositories/" + repositoryId + "/branches/" + branchId + "/nodes", qs, formData, callback); + } + queryNodeRelatives(repository, branch, node, associationTypeQName, associationDirection, query, pagination) { var repositoryId = this.acquireId(repository); @@ -380,14 +410,14 @@ module.exports = function(Session) return this.put(`/repositories/${repositoryId}/branches/${branchId}/nodes`, {}, payload, callback); } - patchNode(repository, branch, node, patchObject) + patchNode(repository, branch, node, patchArray) { var repositoryId = this.acquireId(repository); var branchId = this.acquireId(branch); var nodeId = this.acquireId(node); var callback = this.extractOptionalCallback(arguments); - return this.patch("/repositories/" + repositoryId + "/branches/" + branchId + "/nodes/" + nodeId, {}, patchObject, callback); + return this.patch("/repositories/" + repositoryId + "/branches/" + branchId + "/nodes/" + nodeId, {}, patchArray, callback); } addNodeFeature(repository, branch, node, featureId, config) @@ -434,6 +464,20 @@ module.exports = function(Session) return this.post("/repositories/" + repositoryId + "/branches/" + branchId + "/nodes/" + nodeId + "/change_qname", params, callback); } + changeNodeTypeQName(repository, branch, node, newTypeQName) + { + var repositoryId = this.acquireId(repository); + var branchId = this.acquireId(branch); + var nodeId = this.acquireId(node); + var callback = this.extractOptionalCallback(arguments); + + var params = { + "type": newTypeQName + }; + + return this.post("/repositories/" + repositoryId + "/branches/" + branchId + "/nodes/" + nodeId + "/change_type", params, callback); + } + /* moveNodesTo(repository, branch, sourceNodes, targetNode, targetPath) { @@ -777,6 +821,46 @@ module.exports = function(Session) return this.post(`/repositories/${repositoryId}/branches/${targetBranchId}/copyfrom/start`, params, body, callback); } + + publishNodes(repository, branch, nodeIds, options) + { + var repositoryId = this.acquireId(repository); + var branchId = this.acquireId(branch); + var callback = this.extractOptionalCallback(arguments); + + var params = {}; + var body = { + "nodes": nodeIds + }; + + // copy in any options as parameters + if (options) + { + for (var k in options) + { + if (options[k]) { + params[k] = options[k]; + } + } + } + + return this.post(`/repositories/${repositoryId}/branches/${branchId}/publish`, params, body, callback); + } + + unpublishNodes(repository, branch, nodeIds) + { + var repositoryId = this.acquireId(repository); + var branchId = this.acquireId(branch); + var callback = this.extractOptionalCallback(arguments); + + var params = {}; + var body = { + "nodes": nodeIds + }; + + return this.post(`/repositories/${repositoryId}/branches/${branchId}/unpublish`, params, body, callback); + } + } return NodeSession; diff --git a/src/session/default/methods/repository.js b/src/session/default/methods/repository.js index ee24719..39b91ef 100644 --- a/src/session/default/methods/repository.js +++ b/src/session/default/methods/repository.js @@ -29,6 +29,38 @@ module.exports = function(Session) return this.post("/repositories/query", pagination, query, callback); }; + /** + * Read a repository. + * + * @param repository + * + * @returns {*} + */ + readRepository(repository) + { + var repositoryId = this.acquireId(repository); + var callback = this.extractOptionalCallback(arguments); + + return this.get("/repositories/" + repositoryId, {}, callback); + }; + + /** + * Updates a repository. + * + * @param repository + * @param obj + * + * @returns {*} + * + */ + updateRepository(repository, obj) + { + var repositoryId = this.acquireId(repository); + var callback = this.extractOptionalCallback(arguments); + + return this.put("/repositories/" + repositoryId, {}, obj, callback); + } + } return RepositorySession; diff --git a/src/session/default/methods/sidekick.js b/src/session/default/methods/sidekick.js new file mode 100644 index 0000000..c7769ba --- /dev/null +++ b/src/session/default/methods/sidekick.js @@ -0,0 +1,149 @@ +module.exports = function(Session) +{ + class SidekickSession extends Session + { + // async buildSidekickReference(sidekick) + // { + // var sidekickId = this.acquireId(sidekick); + // var callback = this.extractOptionalCallback(arguments); + // + // var platformId = await this.getPlatformId(); + // var ref = `node://${platformId}/${sidekickId}`; + // if (callback) + // { + // callback(ref); + // } + // + // return ref; + // } + + /** + * Reads an agent (sidekick). + * + * @param agent + * @returns {*} + */ + readAgent(agent) + { + var agentId = this.acquireId(agent); + var callback = this.extractOptionalCallback(arguments); + + var qs = {}; + + return this.get("/sidekicks/" + agentId, qs, callback); + } + + /** + * Queries for agents (sidekicks). + * + * @param query + * @param pagination + * @returns {*} + */ + queryAgents(query, pagination) + { + var callback = this.extractOptionalCallback(arguments); + + return this.post("/sidekicks/query", pagination, query, callback); + } + + /** + * Creates an agent (sidekick). + * + * @param obj + * @returns {*} + */ + createAgent(obj) + { + var callback = this.extractOptionalCallback(arguments); + + var qs = {}; + + return this.post("/sidekicks", qs, obj, callback); + } + + /** + * Deletes an agent (sidekick). + * + * @param agent + * @returns {*} + */ + deleteAgent(agent) + { + var agentId = this.acquireId(agent); + var callback = this.extractOptionalCallback(arguments); + + return this.del("/sidekicks/" + agentId, {}, callback); + } + + /** + * Updates an agent. + * + * @param agent + * @returns {*} + */ + updateAgent(agent) + { + var agentId = this.acquireId(agent); + var callback = this.extractOptionalCallback(arguments); + + return this.put("/sidekicks/" + agentId, {}, agent, callback); + } + + + + //////////////////// + + /** + * Opens an agent session + * + * @param obj + * @returns {*} + */ + openAgentSession(obj) + { + var callback = this.extractOptionalCallback(arguments); + + var qs = {}; + + return this.post("/oneteam/sidekicks/session/open", qs, obj, callback); + } + + /** + * Sends a request to an agent session. + * + * @param agentSession + * @param obj + * @returns {*} + */ + startAgentSessionRequest(agentSession, obj) + { + var agentSessionId = this.acquireId(agentSession); + var callback = this.extractOptionalCallback(arguments); + + var qs = {}; + + return this.post("/oneteam/sidekicks/sessions/" + agentSessionId + "/request", qs, obj, callback); + } + + /** + * Reads an agent session message + * + * @param agentSession + * @param agentMessageId + * + * @returns {*} + */ + readAgentSessionMessage(agentSession, agentMessageId) + { + var agentSessionId = this.acquireId(agentSession); + var callback = this.extractOptionalCallback(arguments); + + var qs = {}; + + return this.get("/oneteam/sidekicks/sessions/" + agentSessionId + "/messages/" + agentMessageId, qs, callback); + } + } + + return SidekickSession; +}; \ No newline at end of file diff --git a/src/session/default/methods/transfer.js b/src/session/default/methods/transfer.js index 662189b..02645c7 100644 --- a/src/session/default/methods/transfer.js +++ b/src/session/default/methods/transfer.js @@ -81,6 +81,8 @@ module.exports = function(Session) async exportProject(project, opts, configuration, callback) { + var self = this; + var callback = this.extractOptionalCallback(arguments); var projectRef = await this.buildProjectReference(project); diff --git a/src/session/default/session.js b/src/session/default/session.js index 17a52a3..5b4b4c5 100644 --- a/src/session/default/session.js +++ b/src/session/default/session.js @@ -15,6 +15,9 @@ class DefaultSession } }; + // band is assumed null (default) + this.band = null; + // helper method this.acquireId = function(objOrId) { @@ -80,12 +83,34 @@ class DefaultSession for (var k in this.defaults.qs) { - qs[k] = this.defaults.qs[k]; + if (!(k in qs)) + { + qs[k] = this.defaults.qs[k]; + } } + // pass band via query string? + // if (this.band) + // { + // qs["band"] = this.band; + // } + return qs; }; + this.generateHeaders = function() + { + var headers = {}; + + // pass band via headers? + if (this.band) + { + headers["x-cloudcms-band-id"] = this.band; + } + + return headers; + }; + this.extractOptionalCallback = function(_arguments) { var args = Array.prototype.slice.call(_arguments); @@ -108,7 +133,9 @@ class DefaultSession qs = this.populateDefaultQs(qs); - return this.engine.get(uri, qs, callback); + var headers = this.generateHeaders(); + + return this.engine.get(uri, qs, headers, callback); }; this.post = function(uri, qs, payload, callback) @@ -117,7 +144,9 @@ class DefaultSession qs = this.populateDefaultQs(qs); - return this.engine.post(uri, qs, payload, callback); + var headers = this.generateHeaders(); + + return this.engine.post(uri, qs, payload, headers, callback); }; this.put = function(uri, qs, payload, callback) @@ -126,7 +155,9 @@ class DefaultSession qs = this.populateDefaultQs(qs); - return this.engine.put(uri, qs, payload, callback); + var headers = this.generateHeaders(); + + return this.engine.put(uri, qs, payload, headers, callback); }; this.del = function(uri, qs, callback) @@ -135,7 +166,9 @@ class DefaultSession qs = this.populateDefaultQs(qs); - return this.engine.del(uri, qs, callback); + var headers = this.generateHeaders(); + + return this.engine.del(uri, qs, headers, callback); }; this.patch = function(uri, qs, payload, callback) @@ -144,14 +177,18 @@ class DefaultSession qs = this.populateDefaultQs(qs); - return this.engine.patch(uri, qs, payload, callback); + var headers = this.generateHeaders(); + + return this.engine.patch(uri, qs, payload, headers, callback); }; this.multipartPost = function(uri, qs, formData, callback) { var self = this; - return this.engine.multipartPost(uri, qs, formData, callback); + var headers = this.generateHeaders(); + + return this.engine.multipartPost(uri, qs, formData, headers, callback); }; this.download = function(uri, qs, callback) @@ -160,10 +197,22 @@ class DefaultSession qs = this.populateDefaultQs(qs); - return this.engine.download(uri, qs, callback); + var headers = this.generateHeaders(); + + return this.engine.download(uri, qs, headers, callback); }; } + /** + * Configures the session to use a specific band. + * + * @param bandId + */ + useBand(bandId) + { + this.band = bandId; + } + // HELPER FUNCTIONS /** @@ -299,7 +348,9 @@ var modules = [ require("./methods/tracker"), require("./methods/release"), require("./methods/platform"), - require("./methods/archive") + require("./methods/archive"), + require("./methods/editorial"), + require("./methods/sidekick") ]; for (var i = 0; i < modules.length; i++) { diff --git a/test/editorial/editorial10.ts b/test/editorial/editorial10.ts new file mode 100644 index 0000000..e73fcd5 --- /dev/null +++ b/test/editorial/editorial10.ts @@ -0,0 +1,67 @@ +import * as CloudCMS from "../.."; + +var assert = require('chai').assert; + +describe('editorial_1', function() { + it('should test editorials methods in a repository without error', async function() { + + var session = await CloudCMS.connect(); + + // create a project + var createProjectJob:any = await session.startCreateProject({ + "title": "test project " + new Date().getTime() + }); + createProjectJob = await session.pollForJobCompletion(createProjectJob); + var projectId = createProjectJob._result["created-project-id"]; + var project = await session.readProject(projectId); + + // find the project repository + var repository:CloudCMS.Repository = await session.readDataStore(project.stackId, "content"); + + // start a new editorial flow + var rootBranchId = "master"; + var operation = "edit"; + var flowKey = "default"; + var properties = { + "title": "My Flow 1" + }; + var workflowData = {}; + var swimlanesObject = {}; + var nodeRefArray = []; + var result:any = await session.startEditorialFlow(repository, rootBranchId, operation, flowKey, properties, workflowData, swimlanesObject, nodeRefArray) + + var workflowId = result.workflowId; + var branchId = result.branchId; + var workspaceId = result.workspaceId; + //var workflowTaskIds = result.workflowTaskIds; + + // read the task for the current user + var currentTask:any = await session.readCurrentTaskForEditorialFlow(workflowId); + //console.log("Current Task: " + JSON.stringify(currentTask)); + assert.isNotNull(currentTask); + var routes = currentTask.routes; // "cancel" and "finish" + assert.isNotNull(routes); + assert.equal(2, Object.keys(routes).length); + + // create three nodes on the flow branch + await session.createNode(repository, branchId, { "title": "n1", "foo": "bar"}); + await session.createNode(repository, branchId, { "title": "n2", "foo": "bar"}); + await session.createNode(repository, branchId, { "title": "n3", "foo": "bar"}); + + // ensure 0 matches on master + var results1 = await session.queryNodes(repository, "master", { "foo": "bar" }) + assert.equal(0, results1.rows.length); + + // finish the flow (this commits back to master branch) + var nextTask = await session.transitionEditorialFlow(workflowId, "finish"); + assert.isNotNull(nextTask); + //console.log("Next Task: " + JSON.stringify(nextTask, null, 2)); + + // wait for things to finish (merge, publish) + await session.sleep(5000); + + // now verify the nodes are on master + var results2 = await session.queryNodes(repository, "master", { "foo": "bar" }) + assert.equal(3, results2.rows.length); + }); +}); \ No newline at end of file diff --git a/test/release/release10.ts b/test/release/release10.ts index a695272..352a68a 100644 --- a/test/release/release10.ts +++ b/test/release/release10.ts @@ -9,9 +9,9 @@ describe('release10', function() { var repository = await session.createRepository(); // Create release - let createJob1 = await session.startCreateRelease(repository, { title: 'Test' }); - createJob1 = await session.waitForJobCompletion(createJob1); - const releaseId1 = createJob1["created-release-id"]; + let createJob1:any = await session.startCreateRelease(repository, { title: 'Test' }); + createJob1 = await session.pollForJobCompletion(createJob1); + const releaseId1 = createJob1._result["created-release-id"]; let release = await session.readRelease(repository, releaseId1); assert.isNotNull(release); diff --git a/test/sidekick/sidekick10.ts b/test/sidekick/sidekick10.ts new file mode 100644 index 0000000..3acc375 --- /dev/null +++ b/test/sidekick/sidekick10.ts @@ -0,0 +1,41 @@ +import * as CloudCMS from "../.."; + +var assert = require('chai').assert; + +describe('sidekick_1', function() { + it('should test sidekick methods in a repository without error', async function() { + + var session = await CloudCMS.connect(); + + // create an agent + var agent1 = await session.createAgent({ + "fa": 1 + }); + + // query for agents + var r1 = await session.queryAgents({ + "fa": 1 + }); + assert.equal(1, r1.rows.length); + + // read back agent + var agent2 = await session.readAgent(agent1._doc); + assert.isNotNull(agent2); + + // update the agent + agent2["fo"] = "foo"; + await session.updateAgent(agent2); + + // verify the update worked + var agent3 = await session.readAgent(agent2._doc); + assert.isNotNull(agent3); + assert.equal("foo", agent2["fo"]); + + // delete the agent + await session.deleteAgent(agent3); + + // verify the delete worked + var agent4 = await session.readAgent(agent3._doc); + assert.isNull(agent4); + }); +}); \ No newline at end of file diff --git a/test/transfer/transfer10.ts b/test/transfer/transfer10.ts index 120d621..05d7957 100644 --- a/test/transfer/transfer10.ts +++ b/test/transfer/transfer10.ts @@ -12,8 +12,8 @@ describe('transfer10', function () { var now = `${Date.now()}`; var startResult = await session.startCreateProject({title: `test-${now}`}); - var projectJob = await session.waitForJobCompletion(startResult._doc); - var projectId = projectJob["created-project-id"]; + var projectJob = await session.pollForJobCompletion(startResult._doc); + var projectId = projectJob._result["created-project-id"]; var project = await session.readProject(projectId); var repository = await session.readDataStore(project.stackId, "content"); @@ -59,8 +59,8 @@ describe('transfer10', function () { title: `test2-${now}`, projectType: projectType?.reference }); - var projectJob2 = await session.waitForJobCompletion(startResult2._doc); - var project2Id = projectJob2["created-project-id"]; + var projectJob2 = await session.pollForJobCompletion(startResult2._doc); + var project2Id = projectJob2._result["created-project-id"]; var project2 = await session.readProject(project2Id); var repository2 = await session.readDataStore(project2.stackId, "content"); diff --git a/test/transfer/transfer20.ts b/test/transfer/transfer20.ts index 38e8474..8e9fb3b 100644 --- a/test/transfer/transfer20.ts +++ b/test/transfer/transfer20.ts @@ -12,8 +12,8 @@ describe('transfer20', function () { var now = `${Date.now()}`; var startResult = await session.startCreateProject({title: `test-${now}`}); - var projectJob = await session.waitForJobCompletion(startResult._doc); - var projectId = projectJob["created-project-id"]; + var projectJob = await session.pollForJobCompletion(startResult._doc); + var projectId = projectJob._result["created-project-id"]; var project = await session.readProject(projectId); var repository = await session.readDataStore(project.stackId, "content"); @@ -53,8 +53,8 @@ describe('transfer20', function () { var startResult2 = await session.startCreateProject({ title: `test2-${now}`, }); - var projectJob2 = await session.waitForJobCompletion(startResult2._doc); - var project2Id = projectJob2["created-project-id"]; + var projectJob2 = await session.pollForJobCompletion(startResult2._doc); + var project2Id = projectJob2._result["created-project-id"]; var project2 = await session.readProject(project2Id); var repository2 = await session.readDataStore(project2.stackId, "content");