From b0a686809d226b5c226f592ecdf7dc048a39ca0c Mon Sep 17 00:00:00 2001 From: Nadeem Patwekar Date: Fri, 7 Jul 2023 14:11:18 +0530 Subject: [PATCH 1/2] feat: :sparkles: adds update fn, auditlog fn, users type correction --- lib/stack/auditlog/index.js | 75 ++++++++++++++++++++++ lib/stack/bulkOperation/index.js | 51 +++++++++++++++ lib/stack/contentType/index.js | 107 +++++++++++++++++++------------ lib/stack/index.js | 25 ++++++++ test/unit/auditLog-test.js | 71 ++++++++++++++++++++ test/unit/contentType-test.js | 25 ++++++++ test/unit/index.js | 1 + test/unit/mock/objects.js | 62 ++++++++++++++++++ test/unit/stack-test.js | 1 + types/stack/index.d.ts | 2 +- 10 files changed, 379 insertions(+), 41 deletions(-) create mode 100644 lib/stack/auditlog/index.js create mode 100644 test/unit/auditLog-test.js diff --git a/lib/stack/auditlog/index.js b/lib/stack/auditlog/index.js new file mode 100644 index 00000000..7e625af6 --- /dev/null +++ b/lib/stack/auditlog/index.js @@ -0,0 +1,75 @@ +import cloneDeep from 'lodash/cloneDeep' +import error from '../../core/contentstackError' +import { fetchAll, parseData } from '../../entity' + +/** + * + * @namespace AuditLog + */ +export function AuditLog (http, data = {}) { + this.stackHeaders = data.stackHeaders + this.urlPath = `/audit-logs` + if (data.logs) { + Object.assign(this, cloneDeep(data.logs)) + this.urlPath = `/audit-logs/${this.uid}` + + /** + * @description The fetch AuditLog call fetches AuditLog details. + * @memberof AuditLog + * @func fetch + * @returns {Promise} Promise for Branch instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).auditLog('audit_log_item_uid').fetch() + * .then((log) => console.log(log)) + * + */ + this.fetch = async function (param = {}) { + try { + const headers = { + headers: { ...cloneDeep(this.stackHeaders) }, + params: { + ...cloneDeep(param) + } + } || {} + const response = await http.get(this.urlPath, headers) + if (response.data) { + return new AuditLog(http, parseData(response, this.stackHeaders)) + } else { + throw error(response) + } + } catch (err) { + throw error(err) + } + } + } else { + /** + * @description The Get all AuditLog request retrieves the details of all the Branch of a stack. + * @memberof AuditLog + * @func fetchAll + * @param {Int} limit The limit parameter will return a specific number of Branch in the output. + * @param {Int} skip The skip parameter will skip a specific number of Branch in the output. + * @param {Boolean}include_count To retrieve the count of Branch. + * @returns {ContentstackCollection} Result collection of content of specified module. + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).auditLog().fetchAll() + * .then((logs) => console.log(logs)) + * + */ + this.fetchAll = fetchAll(http, LogCollection) + } + return this +} + +export function LogCollection (http, data) { + const obj = cloneDeep(data.logs) || [] + const logCollection = obj.map((userdata) => { + return new AuditLog(http, { logs: userdata, stackHeaders: data.stackHeaders }) + }) + return logCollection +} diff --git a/lib/stack/bulkOperation/index.js b/lib/stack/bulkOperation/index.js index fd17fa62..d8b1ba16 100644 --- a/lib/stack/bulkOperation/index.js +++ b/lib/stack/bulkOperation/index.js @@ -220,4 +220,55 @@ export function BulkOperation (http, data = {}) { } return publishUnpublish(http, '/bulk/delete', httpBody, headers) } + + /** + * The Delete entries and assets in bulk request allows you to delete multiple entries and assets at the same time. + * @memberof BulkOperation + * @func update + * @returns {Promise} Success message + * @param {Boolean} updateBody - Set this with details specifing the content type UIDs, entry UIDs or asset UIDs, and locales of which the entries or assets you want to update. + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * const updateBody = { + * "entries": [{ + * "content_type": "content_type_uid1", + * "uid": "entry_uid", + * "locale": "en-us" + * }, { + * "content_type": "content_type_uid2", + * "uid": "entry_uid", + * "locale": "en-us" + * }], + * "workflow": { + * "workflow_stage": { + * "comment": "Workflow-related Comments", + * "due_date": "Thu Dec 01 2018", + * "notify": false, + * "uid": "workflow_stage_uid", + * "assigned_to": [{ + * "uid": "user_uid", + * "name": "user_name", + * "email": "user_email_id" + * }], + * "assigned_by_roles": [{ + * "uid": "role_uid", + * "name": "role_name" + * }] + * } + * } + * } + * client.stack({ api_key: 'api_key'}).bulkOperation().update(updateBody) + * .then((response) => { console.log(response.notice) }) + * + */ + this.update = async (updateBody = {}) => { + const headers = { + headers: { + ...cloneDeep(this.stackHeaders) + } + } + return publishUnpublish(http, '/bulk/workflow', updateBody, headers) + } } diff --git a/lib/stack/contentType/index.js b/lib/stack/contentType/index.js index 4207a61c..05dcf800 100644 --- a/lib/stack/contentType/index.js +++ b/lib/stack/contentType/index.js @@ -95,6 +95,33 @@ export function ContentType (http, data = {}) { } return new Entry(http, data) } + + /** + * @description References call will fetch all the content types in which a specified content type is referenced. + * @returns {Promise} Promise for ContenttypeReferences + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).contentType('content_type_uid').references() + * .then((contentType) => console.log(contentType)) + */ + this.references = async () => { + try { + const headers = { + headers: { ...cloneDeep(this.stackHeaders) } + } + + const response = await http.get(`/content_types/${this.uid}/references`, headers) + if (response.data) { + return response.data + } else { + throw error(response) + } + } catch (err) { + throw error(err) + } + } } else { /** * @description The Create a content type call creates a new content type in a particular stack of your Contentstack account. @@ -123,52 +150,52 @@ export function ContentType (http, data = {}) { } /** - * @description The Create a content type call creates a new content type in a particular stack of your Contentstack account. - * @memberof ContentType - * @func create - * @returns {Promise} Promise for ContentType instance - * - * @example - * import * as contentstack from '@contentstack/management' - * const client = contentstack.client() - * const content_type = {name: 'My New contentType'} - * client.stack().contentType().create({ content_type }) - * .then((contentType) => console.log(contentType)) - */ + * @description The Create a content type call creates a new content type in a particular stack of your Contentstack account. + * @memberof ContentType + * @func create + * @returns {Promise} Promise for ContentType instance + * + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * const content_type = {name: 'My New contentType'} + * client.stack().contentType().create({ content_type }) + * .then((contentType) => console.log(contentType)) + */ this.create = create({ http: http }) /** - * @description The Query on Content Type will allow to fetch details of all or specific Content Type - * @memberof ContentType - * @func query - * @param {Boolean} include_count Set this to 'true' to include in response the total count of content types available in your stack. - * @returns {Array} Array of ContentTyoe. - * - * @example - * import * as contentstack from '@contentstack/management' - * const client = contentstack.client() - * - * client.stack({ api_key: 'api_key'}).contentType().query({ query: { name: 'Content Type Name' } }).find() - * .then((contentTypes) => console.log(contentTypes)) - */ + * @description The Query on Content Type will allow to fetch details of all or specific Content Type + * @memberof ContentType + * @func query + * @param {Boolean} include_count Set this to 'true' to include in response the total count of content types available in your stack. + * @returns {Array} Array of ContentTyoe. + * + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).contentType().query({ query: { name: 'Content Type Name' } }).find() + * .then((contentTypes) => console.log(contentTypes)) + */ this.query = query({ http: http, wrapperCollection: ContentTypeCollection }) /** - * @description The Import a content type call imports a content type into a stack. - * @memberof ContentType - * @func import - * @param {String} data.content_type path to file - * @example - * import * as contentstack from '@contentstack/management' - * const client = contentstack.client() - * - * const data = { - * content_type: 'path/to/file.json', - * } - * client.stack({ api_key: 'api_key'}).contentType().import(data) - * .then((contentType) => console.log(contentType)) - * - */ + * @description The Import a content type call imports a content type into a stack. + * @memberof ContentType + * @func import + * @param {String} data.content_type path to file + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * const data = { + * content_type: 'path/to/file.json', + * } + * client.stack({ api_key: 'api_key'}).contentType().import(data) + * .then((contentType) => console.log(contentType)) + * + */ this.import = async function (data) { try { const response = await upload({ diff --git a/lib/stack/index.js b/lib/stack/index.js index 5ffa4799..f65d8606 100644 --- a/lib/stack/index.js +++ b/lib/stack/index.js @@ -17,6 +17,7 @@ import { BulkOperation } from './bulkOperation' import { Label } from './label' import { Branch } from './branch' import { BranchAlias } from './branchAlias' +import { AuditLog } from './auditlog' // import { format } from 'util' /** * A stack is a space that stores the content of a project (a web or mobile property). Within a stack, you can create content structures, content entries, users, etc. related to the project. Read more about Stacks. @@ -718,6 +719,30 @@ export function Stack (http, data) { * .then((stack) => console.log(stack)) */ this.query = query({ http: http, wrapperCollection: StackCollection }) + + /** + * @description Audit log displays a record of all the activities performed in a stack and helps you keep a track of all published items, updates, deletes, and current status of the existing content. + * @param {String} + * @returns {AuditLog} + * + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).auditLog().fetchAll() + * .then((logs) => console.log(logs)) + * + * client.stack({ api_key: 'api_key' }).auditLog('log_item_uid').fetch() + * .then((log) => console.log(log)) + * + */ + this.auditLog = (logItemUid = null) => { + const data = { stackHeaders: this.stackHeaders } + if (logItemUid) { + data.logs = { uid: logItemUid } + } + return new AuditLog(http, data) + } } return this } diff --git a/test/unit/auditLog-test.js b/test/unit/auditLog-test.js new file mode 100644 index 00000000..869d962d --- /dev/null +++ b/test/unit/auditLog-test.js @@ -0,0 +1,71 @@ +import Axios from 'axios' +import { expect } from 'chai' +import { describe, it } from 'mocha' +import MockAdapter from 'axios-mock-adapter' +import { noticeMock, stackHeadersMock, systemUidMock, auditLogsMock, auditLogItemMock } from './mock/objects' +import { AuditLog } from '../../lib/stack/auditlog' + +describe('Contentstack AuditLog test', () => { + it('AuditLog test without uid', done => { + const branch = makeAuditLog() + expect(branch).to.not.equal(undefined) + expect(branch.uid).to.be.equal(undefined) + expect(branch.urlPath).to.be.equal('/audit-logs') + expect(branch.fetch).to.equal(undefined) + expect(branch.fetchAll).to.not.equal(undefined) + done() + }) + + it('AuditLog test with uid', done => { + const branch = makeAuditLog({ logs: { uid: 'logUid' } }) + expect(branch).to.not.equal(undefined) + expect(branch.uid).to.be.equal('logUid') + expect(branch.urlPath).to.be.equal('/audit-logs/logUid') + expect(branch.fetch).to.not.equal(undefined) + expect(branch.fetchAll).to.equal(undefined) + done() + }) + + it('AuditLog Fetch all without Stack Headers test', done => { + var mock = new MockAdapter(Axios) + mock.onGet('/audit-logs').reply(200, auditLogsMock) + makeAuditLog() + .fetchAll() + .then((response) => { + expect(response.items[0].created_at).to.be.equal('created_at_date') + expect(response.items[0].uid).to.be.equal('UID') + done() + }) + .catch(done) + }) + + it('AuditLog Fetch all with params test', done => { + var mock = new MockAdapter(Axios) + mock.onGet('/audit-logs').reply(200, auditLogsMock) + makeAuditLog({ stackHeaders: stackHeadersMock }) + .fetchAll({}) + .then((response) => { + expect(response.items[0].created_at).to.be.equal('created_at_date') + expect(response.items[0].uid).to.be.equal('UID') + done() + }) + .catch(done) + }) + + it('AuditLog fetch test', done => { + var mock = new MockAdapter(Axios) + mock.onGet('/audit-logs/UID').reply(200, auditLogItemMock) + makeAuditLog({ stackHeaders: stackHeadersMock, logs: { uid: 'UID' } }) + .fetch() + .then((response) => { + expect(response.created_at).to.be.equal('created_at_date') + expect(response.uid).to.be.equal('UID') + done() + }) + .catch(done) + }) +}) + +function makeAuditLog (data) { + return new AuditLog(Axios, data) +} diff --git a/test/unit/contentType-test.js b/test/unit/contentType-test.js index 6f7f08ce..9ca90da6 100644 --- a/test/unit/contentType-test.js +++ b/test/unit/contentType-test.js @@ -14,6 +14,7 @@ describe('Contentstack ContentType test', () => { expect(contentType.update).to.be.equal(undefined) expect(contentType.delete).to.be.equal(undefined) expect(contentType.fetch).to.be.equal(undefined) + expect(contentType.references).to.be.equal(undefined) expect(contentType.create).to.not.equal(undefined) expect(contentType.query).to.not.equal(undefined) done() @@ -30,6 +31,7 @@ describe('Contentstack ContentType test', () => { expect(contentType.update).to.not.equal(undefined) expect(contentType.delete).to.not.equal(undefined) expect(contentType.fetch).to.not.equal(undefined) + expect(contentType.references).to.not.equal(undefined) expect(contentType.create).to.be.equal(undefined) expect(contentType.query).to.be.equal(undefined) done() @@ -48,6 +50,7 @@ describe('Contentstack ContentType test', () => { expect(contentType.update).to.not.equal(undefined) expect(contentType.delete).to.not.equal(undefined) expect(contentType.fetch).to.not.equal(undefined) + expect(contentType.references).to.not.equal(undefined) expect(contentType.create).to.be.equal(undefined) expect(contentType.query).to.be.equal(undefined) done() @@ -156,6 +159,28 @@ describe('Contentstack ContentType test', () => { .catch(done) }) + it('ContentType references test', done => { + var mock = new MockAdapter(Axios) + mock.onGet('/content_types/UID/references').reply(200, { + references: [ + 'Product', + 'Blog' + ] + }) + makeContentType({ + content_type: { + ...systemUidMock + }, + stackHeaders: stackHeadersMock + }) + .references() + .then((response) => { + expect(response.references).to.be.eql(['Product', 'Blog']) + done() + }) + .catch(done) + }) + it('ContentType delete test', done => { var mock = new MockAdapter(Axios) mock.onDelete('/content_types/UID').reply(200, { diff --git a/test/unit/index.js b/test/unit/index.js index 75f9e21a..8917ba4d 100644 --- a/test/unit/index.js +++ b/test/unit/index.js @@ -35,3 +35,4 @@ require('./authorization-test') require('./oauth-test') require('./marketplace-test') require('./webhooks-test') +require('./auditLog-test') diff --git a/test/unit/mock/objects.js b/test/unit/mock/objects.js index 903e1909..1f43ac80 100644 --- a/test/unit/mock/objects.js +++ b/test/unit/mock/objects.js @@ -720,6 +720,66 @@ const installedStacksMock = { stack3: 'data3' } +const auditLogItemMock = { + logs: { + uid: 'UID', + stack: 'blt8d542b122115b153', + created_at: 'created_at_date', + created_by: 'blt7b815b05d2fe5dd8', + module: 'environment', + event_type: 'create', + request_id: '86352', + metadata: { + title: 'production', + uid: 'blt2c60160a046ce26d' + }, + remote_addr: '202.179.94.0', + request: { + r: '0.5090218519397551', + environment: { + deploy_content: false, + servers: [], + urls: [ + { + url: '', + locale: 'en-us' + } + ], + name: 'production', + color: '#01977c' + } + }, + response: { + notice: 'Environment created successfully.', + environment: { + deploy_content: false, + servers: [], + urls: [ + { + url: '', + locale: 'en-us' + } + ], + name: 'production', + uid: 'blt2c60160a046ce26d', + created_by: 'blt7b815b05d2fe5dd8', + updated_by: 'blt7b815b05d2fe5dd8', + created_at: '2021-08-19T12:37:44.414Z', + updated_at: '2021-08-19T12:37:44.414Z', + ACL: {}, + _version: 1, + isEnvironment: true + } + } + } +} + +const auditLogsMock = { + logs: [ + { ...auditLogItemMock.logs } + ] +} + function mockCollection (mockData, type) { const mock = { ...cloneDeep(noticeMock), @@ -792,6 +852,8 @@ export { installedAppsMock, installedUsersMock, installedStacksMock, + auditLogItemMock, + auditLogsMock, mockCollection, entryMockCollection, checkSystemFields diff --git a/test/unit/stack-test.js b/test/unit/stack-test.js index 9b10102c..b61d4ade 100644 --- a/test/unit/stack-test.js +++ b/test/unit/stack-test.js @@ -738,6 +738,7 @@ describe('Contentstack Stack test', () => { .bulkOperation() expect(bulkOperation.publish).to.not.equal(undefined) expect(bulkOperation.unpublish).to.not.equal(undefined) + expect(bulkOperation.update).to.not.equal(undefined) expect(bulkOperation.stackHeaders).to.not.equal(undefined) expect(bulkOperation.stackHeaders.api_key).to.be.equal('stack_api_key') done() diff --git a/types/stack/index.d.ts b/types/stack/index.d.ts index 7b12e7da..913585ec 100644 --- a/types/stack/index.d.ts +++ b/types/stack/index.d.ts @@ -82,7 +82,7 @@ export interface Stack extends SystemFields { bulkOperation(): BulkOperation - user(): Promise> + users(): Promise> updateUsersRoles(users: AnyProperty): Promise transferOwnership(email: string): Promise settings(): Promise From 9d982ccde28d17363c9cfa907d2ab15815e36532 Mon Sep 17 00:00:00 2001 From: Nadeem Patwekar Date: Fri, 7 Jul 2023 18:38:23 +0530 Subject: [PATCH 2/2] chore: replaced uid and other data --- test/unit/mock/objects.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/unit/mock/objects.js b/test/unit/mock/objects.js index 1f43ac80..214fa219 100644 --- a/test/unit/mock/objects.js +++ b/test/unit/mock/objects.js @@ -685,18 +685,18 @@ const branchMergeQueueFindMock = { const installationConfigLocationMock = { uid: 'Installation_UID', - created_at: '2023-02-28T09:41:01.288Z', - updated_at: '2023-02-28T09:41:01.288Z', - created_by: 'blt22e22222d22d2f22222a2b2f', - updated_by: 'blt22e22222d22d2f22222a2b2f', + created_at: 'created_at_date', + updated_at: 'updated_at_date', + created_by: 'created_by_author', + updated_by: 'updated_by_author', tags: [], ACL: [], _version: 1, title: 'App Name', config: {}, type: 'stack_config_widget', - app_installation_uid: '63fdcc2c84fad28ef0b7564e', - app_uid: '620a496d38c9480018b9b0d6', + app_installation_uid: 'app_installation_uid', + app_uid: 'app_uid', signed: true, enable: true, src: 'http://localhost:3000/config' @@ -723,15 +723,15 @@ const installedStacksMock = { const auditLogItemMock = { logs: { uid: 'UID', - stack: 'blt8d542b122115b153', + stack: 'stack_uid', created_at: 'created_at_date', - created_by: 'blt7b815b05d2fe5dd8', + created_by: 'created_by_author', module: 'environment', event_type: 'create', request_id: '86352', metadata: { title: 'production', - uid: 'blt2c60160a046ce26d' + uid: 'uid' }, remote_addr: '202.179.94.0', request: { @@ -761,11 +761,11 @@ const auditLogItemMock = { } ], name: 'production', - uid: 'blt2c60160a046ce26d', - created_by: 'blt7b815b05d2fe5dd8', - updated_by: 'blt7b815b05d2fe5dd8', - created_at: '2021-08-19T12:37:44.414Z', - updated_at: '2021-08-19T12:37:44.414Z', + uid: 'UID', + created_by: 'created_by_author', + updated_by: 'updated_by_author', + created_at: 'created_at_date', + updated_at: 'updated_at_date', ACL: {}, _version: 1, isEnvironment: true