diff --git a/.talismanrc b/.talismanrc index 3e694712..b61827c6 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,5 +1,5 @@ threshold: medium fileignoreconfig: - filename: package-lock.json - checksum: 71a070335979f3eed857a887e4bd36d9a1e14b08370c55117c55b45b77889b68 + checksum: ef5d374553f431b5a952069f46184ec7e49efd7d72143e1a1642994758db4359 version: "" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c03f04d0..435bb15c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,28 @@ # Changelog -## [v1.10.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.8.0) (2023-07-21) +## [v1.11.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.11.0) (2023-09-03) + - Fixes and Enhancements + - Allows contenttype in asset upload + - Taxonomy feature addition + - Terms feature addition +## [v1.10.2](https://github.com/contentstack/contentstack-management-javascript/tree/v1.10.2) (2023-08-23) + - Fixes and Enhancements + - RefreshToken error handling + - Handling workflow response of object format + - Support for overwrite flag in Contenttype and Global fields import functionality +## [v1.10.1](https://github.com/contentstack/contentstack-management-javascript/tree/v1.10.1) (2023-08-01) + - Fixes: + - Token leak + - Users type + - ContentstackCollection type + - Environment param type + - Enhancements + - Adds Auditlogs functions + - Contenttype references function +## [v1.10.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.10.0) (2023-07-21) - Fixes - Fixes Breaking Changes -## [v1.9.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.8.0) (2023-07-19) +## [v1.9.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.9.0) (2023-07-19) - Features: - NRP support added - Audit logs APIs support diff --git a/lib/entity.js b/lib/entity.js index 5ca89fc2..b3ed2a4f 100644 --- a/lib/entity.js +++ b/lib/entity.js @@ -84,7 +84,7 @@ export const create = ({ http, params }) => { try { const response = await http.post(this.urlPath, data, headers) if (response.data) { - return new this.constructor(http, parseData(response, this.stackHeaders, this.content_type_uid)) + return new this.constructor(http, parseData(response, this.stackHeaders, this.content_type_uid, this.taxonomy_uid)) } else { throw error(response) } @@ -144,7 +144,7 @@ export const update = (http, type, params = {}) => { } }) if (response.data) { - return new this.constructor(http, parseData(response, this.stackHeaders, this.content_type_uid)) + return new this.constructor(http, parseData(response, this.stackHeaders, this.content_type_uid, this.taxonomy_uid)) } else { throw error(response) } @@ -204,7 +204,7 @@ export const fetch = (http, type, params = {}) => { response.data[type]['content_type'] = response.data['content_type'] response.data[type]['schema'] = response.data['schema'] } - return new this.constructor(http, parseData(response, this.stackHeaders, this.content_type_uid)) + return new this.constructor(http, parseData(response, this.stackHeaders, this.content_type_uid, this.taxonomy_uid)) } else { throw error(response) } @@ -235,7 +235,7 @@ export const fetchAll = (http, wrapperCollection, params = {}) => { } } -export function parseData (response, stackHeaders, contentTypeUID) { +export function parseData (response, stackHeaders, contentTypeUID, taxonomy_uid) { const data = response.data || {} if (stackHeaders) { data.stackHeaders = stackHeaders @@ -243,6 +243,9 @@ export function parseData (response, stackHeaders, contentTypeUID) { if (contentTypeUID) { data.content_type_uid = contentTypeUID } + if (taxonomy_uid) { + data.taxonomy_uid = taxonomy_uid + } return data } @@ -266,4 +269,36 @@ export async function get (http, url, params, data) { } catch (err) { throw error(err) } +} + +export const move = (http, type, force = false, params = {}) => { + return async function (param = {}) { + try { + let updateData = {} + const json = cloneDeep(this) + delete json.parent_uid + if (type) { + updateData[type] = json + } else { + updateData = json + } + const headers = { + headers: { ...cloneDeep(this.stackHeaders), ...cloneDeep(params) }, + params: { + ...cloneDeep(param) + } + } || {} + if (force === true) { + headers.params.force = true + } + const response = await http.put(`${this.urlPath}/move`, updateData, headers) + if (response.data) { + return new this.constructor(http, parseData(response, this.stackHeaders, this.content_type_uid, this.taxonomy_uid)) + } else { + throw error(response) + } + } catch (err) { + throw error(err) + } + } } \ No newline at end of file diff --git a/lib/stack/asset/index.js b/lib/stack/asset/index.js index 6de9eb9d..dbb06f9f 100644 --- a/lib/stack/asset/index.js +++ b/lib/stack/asset/index.js @@ -292,7 +292,11 @@ export function createFormData (data) { formData.append('asset[title]', data.title) } const uploadStream = createReadStream(data.upload) - formData.append('asset[upload]', uploadStream) + if (typeof data.content_type === 'string') { + formData.append('asset[upload]', uploadStream, { contentType: data.content_type }) + } else { + formData.append('asset[upload]', uploadStream) + } return formData } } diff --git a/lib/stack/index.js b/lib/stack/index.js index 94cde5fa..d86f72cb 100644 --- a/lib/stack/index.js +++ b/lib/stack/index.js @@ -18,7 +18,8 @@ import { Label } from './label' import { Branch } from './branch' import { BranchAlias } from './branchAlias' import { AuditLog } from './auditlog' -// import { format } from 'util' +import { Taxonomy } from './taxonomy' + /** * 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. * @namespace Stack @@ -684,6 +685,28 @@ export function Stack (http, data) { } return new Role(http, data) } + + /** + * @description Taxonomies allow you to group a collection of content within a stack. Using taxonomies you can group content types that need to work together + * @param {String} uid The UID of the Taxonomy you want to get details. + * @returns {Taxonomy} Instance of Taxonomy. + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).taxonomy().create() + * .then((taxonomy) => console.log(taxonomy)) + * + * client.stack({ api_key: 'api_key'}).taxonomy('taxonomy_uid').fetch() + * .then((taxonomy) => console.log(taxonomy)) + */ + this.taxonomy = (taxonomyUid = '') => { + const data = { stackHeaders: this.stackHeaders } + if (taxonomyUid) { + data.taxonomy = { uid: taxonomyUid } + } + return new Taxonomy(http, data) + } } else { /** * @description The Create stack call creates a new stack in your Contentstack account. diff --git a/lib/stack/taxonomy/index.js b/lib/stack/taxonomy/index.js new file mode 100644 index 00000000..ffd13b79 --- /dev/null +++ b/lib/stack/taxonomy/index.js @@ -0,0 +1,124 @@ +/* eslint-disable camelcase */ +import cloneDeep from 'lodash/cloneDeep' +import { + create, + fetch, + query, + update, + deleteEntity +} from '../../entity' +import { Terms, TermsCollection } from './terms' + +export function Taxonomy (http, data = {}) { + this.stackHeaders = data.stackHeaders + this.urlPath = `/taxonomies` + + if (data.taxonomy) { + Object.assign(this, cloneDeep(data.taxonomy)) + if (data.taxonomy.terms) { + this.terms = new TermsCollection(http, { terms: data.taxonomy.terms, stackHeaders: data.stackHeaders }, this.uid) + } + this.urlPath = `/taxonomies/${this.uid}` + + /** + * @description The Update taxonomy call is used to update an existing taxonomy. + * @memberof Taxonomy + * @func update + * @returns {Promise} Promise for Taxonomy instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).taxonomy('taxonomyUid').fetch() + * .then((taxonomy) => { + * taxonomy.name = 'taxonomy name' + * return taxonomy.update() + * }) + * .then((taxonomy) => console.log(taxonomy)) + * + */ + this.update = update(http, 'taxonomy') + + /** + * @description The Delete taxonomy call is used to delete an existing taxonomy. + * @memberof Taxonomy + * @func delete + * @returns {Promise} Response Object. + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).taxonomy('taxonomyUid').delete() + * .then((response) => console.log(response.notice)) + * + */ + this.delete = deleteEntity(http) + + /** + * @description The Fetch taxonomy call is used to fetch an existing taxonomy. + * @memberof Taxonomy + * @func fetch + * @returns {Promise} Promise for Taxonomy instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).taxonomy('taxonomyUid').fetch() + * .then((taxonomy) => console.log(taxonomy)) + * + */ + this.fetch = fetch(http, 'taxonomy') + + this.terms = (uid = '') => { + const data = { stackHeaders: this.stackHeaders } + data.taxonomy_uid = this.uid + if (uid) { + data.term = { uid: uid } + } + return new Terms(http, data) + } + } else { + /** + * @description The Create taxonomy call is used to create a taxonomy. + * @memberof Taxonomy + * @func create + * @returns {Promise} Promise for Taxonomy instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * const taxonomy = { + * uid: 'taxonomy_testing1', + * name: 'taxonomy testing', + * description: 'Description for Taxonomy testing' + * } + * client.stack({ api_key: 'api_key'}).taxonomy().create({taxonomy}) + * .then(taxonomy) => console.log(taxonomy) + * + */ + this.create = create({ http }) + + /** + * @description The Query on Taxonomy will allow to fetch details of all Taxonomies. + * @memberof Taxonomy + * @param {Object} params - URI parameters + * @prop {Object} params.query - Queries that you can use to fetch filtered results. + * @func query + * @returns {Array} Array of Taxonomy. + * + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack().taxonomy().query().find() + * .then((taxonomies) => console.log(taxonomies) + */ + this.query = query({ http: http, wrapperCollection: TaxonomyCollection }) + } +} +export function TaxonomyCollection (http, data) { + const obj = cloneDeep(data.taxonomy) || [] + const taxonomyCollection = obj.map((userdata) => { + return new Taxonomy(http, { taxonomy: userdata, stackHeaders: data.stackHeaders }) + }) + return taxonomyCollection +} diff --git a/lib/stack/taxonomy/terms/index.js b/lib/stack/taxonomy/terms/index.js new file mode 100644 index 00000000..8a98b5f7 --- /dev/null +++ b/lib/stack/taxonomy/terms/index.js @@ -0,0 +1,209 @@ +import cloneDeep from 'lodash/cloneDeep' +import { + create, + fetch, + update, + query, + deleteEntity, + move, + parseData +} from '../../../entity' + +export function Terms (http, data) { + this.stackHeaders = data.stackHeaders + this.taxonomy_uid = data.taxonomy_uid + this.urlPath = `/taxonomies/${this.taxonomy_uid}/terms` + + if (data && data.term) { + Object.assign(this, cloneDeep(data.term)) + this.urlPath = `/taxonomies/${this.taxonomy_uid}/terms/${this.uid}` + + /** + * @description The Update terms call is used to update an existing term. + * @memberof Terms + * @func update + * @returns {Promise} Promise for Terms instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).terms('terms_uid').fetch() + * .then((terms) => { + * terms.name = 'terms name' + * return terms.update() + * }) + * .then((terms) => console.log(terms)) + * + */ + this.update = update(http, 'term') + + /** + * @description The Delete terms call is used to delete an existing term. + * @memberof Terms + * @func delete + * @returns {Promise} Response Object. + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).terms('terms_uid').delete() + * .then((response) => console.log(response.notice)) + * + */ + this.delete = deleteEntity(http) + + /** + * @description The Fetch terms call is used to fetch an existing term. + * @memberof Terms + * @func fetch + * @returns {Promise} Promise for Terms instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).terms('terms_uid').fetch() + * .then((terms) => console.log(terms)) + * + */ + this.fetch = fetch(http, 'term') + + /** + * @description The ancestors call is used to get all the ancestor terms of an existing term. + * @memberof Terms + * @func ancestors + * @returns {Promise} Promise for Terms instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).terms('terms_uid').ancestors() + * .then((terms) => console.log(terms)) + * + */ + this.ancestors = async (params = {}) => { + try { + const headers = { + headers: { ...cloneDeep(this.stackHeaders), ...cloneDeep(params) } + } + const response = await http.get(`${this.urlPath}/ancestors`, headers) + return parseData(response, this.stackHeaders) + } catch (err) { + console.error(err) + throw err + } + } + + /** + * @description The move call is used to existing term. + * @memberof Terms + * @func descendants + * @returns {Promise} Promise for Terms instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack({ api_key: 'api_key'}).terms('terms_uid').descendants() + * .then((terms) => console.log(terms)) + * + */ + this.descendants = async (params = {}) => { + try { + const headers = { + headers: { ...cloneDeep(this.stackHeaders), ...cloneDeep(params) } + } + const response = await http.get(`${this.urlPath}/descendants`, headers) + return parseData(response, this.stackHeaders) + } catch (err) { + console.error(err) + throw err + } + } + + /** + * @description The move call is used to update the parent uid. + * @memberof Terms + * @func anscestors + * @returns {Promise} Promise for Terms instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * const term = { + * parent_uid: 'parent_uid', + * order: 2 + * } + * client.stack({ api_key: 'api_key'}).terms('terms_uid').move(term) + * .then((terms) => console.log(terms)) + * + */ + this.move = move(http, 'term') + } else { + /** + * @description The Create terms call is used to create a terms. + * @memberof Terms + * @func create + * @returns {Promise} Promise for Terms instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * const terms = { + * uid: 'terms_testing1', + * name: 'terms testing', + * description: 'Description for terms testing' + * } + * client.stack({ api_key: 'api_key'}).terms().create({terms}) + * .then(terms) => console.log(terms) + * + */ + this.create = create({ http }) + + /** + * @description The Query on Terms will allow to fetch details of all Terms. + * @memberof Terms + * @param {Object} params - URI parameters + * @prop {Object} params.query - Queries that you can use to fetch filtered results. + * @func query + * @returns {Array} Array of Terms. + * + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * + * client.stack().terms().query().find() + * .then((terms) => console.log(terms) + */ + this.query = query({ http: http, wrapperCollection: TermsCollection }) + } + /** + * @description The Search terms call is used to search a term. + * @memberof Terms + * @func search + * @returns {Promise} Promise for Terms instance + * @example + * import * as contentstack from '@contentstack/management' + * const client = contentstack.client() + * const term_string = '' + * client.stack({ api_key: 'api_key'}).terms().search(term_string) + * .then(terms) => console.log(terms) + * + */ + this.search = async (term = '', params = {}) => { + try { + const headers = { + headers: { ...cloneDeep(this.stackHeaders), ...cloneDeep(params) } + } + const response = await http.get(`taxonomies/${this.taxonomy_uid}/terms?term=${term}`, headers) + return parseData(response, this.stackHeaders) + } catch (err) { + console.error(err) + throw err + } + } +} +export function TermsCollection (http, data) { + const obj = cloneDeep(data.terms) || [] + const termsCollection = obj.map((term) => { + return new Terms(http, { term: term, taxonomy_uid: data.taxonomy_uid, stackHeaders: data.stackHeaders }) + }) + return termsCollection +} diff --git a/package-lock.json b/package-lock.json index d3339964..eb8d451e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.10.2", "license": "MIT", "dependencies": { - "axios": "^1.4.0", + "axios": "^1.5.1", "form-data": "^3.0.1", "lodash": "^4.17.21", "qs": "^6.11.2" @@ -3617,9 +3617,9 @@ } }, "node_modules/axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -5953,9 +5953,9 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "engines": { "node": "*" @@ -15160,9 +15160,9 @@ "dev": true }, "axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -16916,9 +16916,9 @@ "dev": true }, "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, "get-intrinsic": { diff --git a/package.json b/package.json index 14ba5438..3ab84b5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/management", - "version": "1.10.2", + "version": "1.11.0", "description": "The Content Management API is used to manage the content of your Contentstack account", "main": "./dist/node/contentstack-management.js", "browser": "./dist/web/contentstack-management.js", @@ -49,7 +49,7 @@ "author": "Contentstack", "license": "MIT", "dependencies": { - "axios": "^1.4.0", + "axios": "^1.5.1", "form-data": "^3.0.1", "lodash": "^4.17.21", "qs": "^6.11.2" diff --git a/test/api/asset-test.js b/test/api/asset-test.js index 78f91fce..0a534fa6 100644 --- a/test/api/asset-test.js +++ b/test/api/asset-test.js @@ -90,6 +90,30 @@ describe('Assets api Test', () => { }) .catch(done) }) + + it('Asset Upload in folder with contenttype', done => { + const asset = { + upload: path.join(__dirname, './mock/berries.jfif'), + title: 'customasset2 in Folder', + description: 'Custom Asset Desc in Folder', + parent_uid: folderUID, + tags: 'folder', + content_type: 'image/jpeg' + } + makeAsset().create(asset) + .then((asset) => { + publishAssetUID = asset.uid + expect(asset.uid).to.be.not.equal(null) + expect(asset.url).to.be.not.equal(null) + expect(asset.filename).to.be.equal('berries.jfif') + expect(asset.title).to.be.equal('customasset2 in Folder') + expect(asset.description).to.be.equal('Custom Asset Desc in Folder') + expect(asset.content_type).to.be.equal('image/jpeg') + expect(asset.parent_uid).to.be.equal(folderUID) + done() + }) + .catch(done) + }) it('Replace asset ', done => { const asset = { upload: path.join(__dirname, './mock/upload.html') diff --git a/test/api/mock/berries.jfif b/test/api/mock/berries.jfif new file mode 100644 index 00000000..f0e4c1a0 Binary files /dev/null and b/test/api/mock/berries.jfif differ diff --git a/test/api/taxonomy-test.js b/test/api/taxonomy-test.js new file mode 100644 index 00000000..968a7025 --- /dev/null +++ b/test/api/taxonomy-test.js @@ -0,0 +1,86 @@ +import { expect } from 'chai' +import { describe, it, setup } from 'mocha' +import { jsonReader } from '../utility/fileOperations/readwrite' +import { contentstackClient } from '../utility/ContentstackClient.js' + +var client = {} +var stack = {} + +const taxonomy = { + uid: 'taxonomy_testing1', + name: 'taxonomy testing', + description: 'Description for Taxonomy testing' +} + +var taxonomyUID = '' +var taxonomyDelUID = 'taxonomy_testing' + +describe('taxonomy api Test', () => { + setup(() => { + const user = jsonReader('loggedinuser.json') + stack = jsonReader('stack.json') + client = contentstackClient(user.authtoken) + }) + + it('Create taxonomy', done => { + makeTaxonomy() + .create(taxonomy) + .then((taxonomyResponse) => { + expect(taxonomyResponse.name).to.be.equal(taxonomy.name) + done() + }) + .catch(done) + }) + + it('Fetch taxonomy from uid', done => { + makeTaxonomy(taxonomyUID) + .fetch() + .then((taxonomyResponse) => { + expect(taxonomyResponse.uid).to.be.equal(taxonomyUID) + expect(taxonomyResponse.name).to.be.not.equal(null) + done() + }) + .catch(done) + }) + + it('Update taxonomy from uid', done => { + makeTaxonomy(taxonomyUID) + .fetch() + .then((taxonomyResponse) => { + taxonomyResponse.name = 'Updated Name' + return taxonomyResponse.update() + }) + .then((taxonomyResponse) => { + expect(taxonomyResponse.uid).to.be.equal(taxonomyUID) + expect(taxonomyResponse.name).to.be.equal('Updated Name') + done() + }) + .catch(done) + }) + + it('Delete taxonomy from uid', done => { + makeTaxonomy(taxonomyDelUID) + .delete() + .then((taxonomyResponse) => { + expect(taxonomyResponse.notice).to.be.equal('Taxonomy deleted successfully.') + done() + }) + .catch(done) + }) + + it('Query to get all taxonomies', async () => { + makeTaxonomy() + .query() + .find() + .then((response) => { + response.items.forEach((taxonomyResponse) => { + expect(taxonomyResponse.uid).to.be.not.equal(null) + expect(taxonomyResponse.name).to.be.not.equal(null) + }) + }) + }) +}) + +function makeTaxonomy (uid = null) { + return client.stack({ api_key: stack.api_key }).taxonomy(uid) +} diff --git a/test/api/terms-test.js b/test/api/terms-test.js new file mode 100644 index 00000000..7d10a9bd --- /dev/null +++ b/test/api/terms-test.js @@ -0,0 +1,137 @@ +import { describe, it, beforeEach } from 'mocha' +import { expect } from 'chai' +import { jsonReader } from '../utility/fileOperations/readwrite' +import { contentstackClient } from '../utility/ContentstackClient.js' + +var client = {} +var stack = {} + +const taxonomy_uid = '' +const term_uid = '' +const term = { + term: { + uid: 'term_test', + name: 'Term test' + }, + parent_uid: null +} + +describe('Terms API Test', () => { + beforeEach(() => { + const user = jsonReader('loggedinuser.json') + stack = jsonReader('stack.json') + client = contentstackClient(user.authtoken) + }) + + it('Create term', async () => { + try { + const response = await makeTerms(taxonomy_uid).create(term) + expect(response.notice).to.be.equal('Term created successfully.') + expect(response.uid).to.be.equal(term.term.uid) + } catch (err) { + console.log(err) + } + }) + + it('Query and get all terms', async () => { + try { + const response = await makeTerms(taxonomy_uid).query().find() + expect(response.items).to.be.an('array') + expect(response.items[0].uid).not.to.be.equal(null) + expect(response.items[0].name).not.to.be.equal(null) + } catch (err) { + console.log(err) + } + }) + + it('Fetch term from UID', async () => { + try { + const response = await makeTerms(taxonomy_uid, term_uid).fetch() + expect(response.uid).to.be.equal(term_uid) + expect(response.name).not.to.be.equal(null) + expect(response.created_by).not.to.be.equal(null) + expect(response.updated_by).not.to.be.equal(null) + } catch (err) { + console.log(err) + } + }) + + it('Update term', async () => { + try { + const response = await makeTerms(taxonomy_uid, term_uid).fetch() + .then((term) => { + term.name = 'fashion' + return term.update() + }) + expect(response.notice).to.be.equal('Term updated successfully.') + expect(response.uid).to.be.equal(term_uid) + expect(response.name).to.be.equal('fashion') + expect(response.created_by).not.to.be.equal(null) + expect(response.updated_by).not.to.be.equal(null) + } catch (err) { + console.log(err) + } + }) + + it('Delete term from UID', async () => { + try { + const response = await makeTerms(term_uid).delete() + expect(response.notice).to.be.equal('') + } catch (err) { + console.log(err) + } + }) + + it('Ancestors of the term given', async () => { + try { + const response = await makeTerms(taxonomy_uid, term_uid).ancestors() + expect(response.terms[0].uid).not.to.be.equal(null) + expect(response.terms[0].name).not.to.be.equal(null) + expect(response.terms[0].created_by).not.to.be.equal(null) + expect(response.terms[0].updated_by).not.to.be.equal(null) + } catch (err) { + console.log(err) + } + }) + + it('Descendants of the term given', async () => { + try { + const response = await makeTerms(taxonomy_uid, term_uid).descendants() + expect(response.terms.uid).not.to.be.equal(null) + expect(response.terms.name).not.to.be.equal(null) + expect(response.terms.created_by).not.to.be.equal(null) + expect(response.terms.updated_by).not.to.be.equal(null) + } catch (err) { + console.log(err) + } + }) + it('search term', async () => { + term_string = '' + try { + const response = await makeTerms(taxonomy_uid).search(term_string) + expect(response.terms).to.be.an('array') + } catch (err) { + console.log(err) + } + }) + it('move term', async () => { + try { + const term = { + parent_uid: 'parent_uid', + order: 2 + } + await makeTerms(taxonomy_uid, term_uid).move({ term }) + .then((term) => { + term.parent_uid = 'parent_uid' + console.log(term.move()) + return term.move() + }) + } catch (err) { + console.log(err) + } + }) +}) + +function makeTerms (taxonomy_uid, term_uid = null) { + return client.stack({ api_key: stack.api_key }).taxonomy(taxonomy_uid).terms(term_uid) +} diff --git a/test/test.js b/test/test.js index 818f04cb..d7e30522 100644 --- a/test/test.js +++ b/test/test.js @@ -25,3 +25,5 @@ require('./api/release-test') require('./api/label-test') require('./api/contentType-delete-test') require('./api/delete-test') +require('./api/taxonomy-test') +require('./api/terms-test') diff --git a/test/unit/asset-test.js b/test/unit/asset-test.js index 184db299..30176aa2 100644 --- a/test/unit/asset-test.js +++ b/test/unit/asset-test.js @@ -154,7 +154,8 @@ describe('Contentstack Asset test', () => { title: 'customasset', description: 'Custom Asset Desc', tags: ['Custom'], - parent_uid: 'UID' + parent_uid: 'UID', + content_type: 'text/html' } const form = createFormData(assetUpload)() var boundary = form.getBoundary() @@ -322,7 +323,7 @@ function makeAsset (data) { function checkAsset (asset) { checkSystemFields(asset) - expect(asset.content_type).to.be.equal('image/png') + expect(asset.content_type).to.be.equal('text/html') expect(asset.file_size).to.be.equal('42670') expect(asset.tags.length).to.be.equal(0) expect(asset.filename).to.be.equal('file.png') diff --git a/test/unit/index.js b/test/unit/index.js index abd607f4..2877b750 100644 --- a/test/unit/index.js +++ b/test/unit/index.js @@ -33,3 +33,5 @@ require('./deployment-test') require('./app-request-test') require('./authorization-test') require('./auditLog-test') +require('./taxonomy-test') +require('./terms-test') diff --git a/test/unit/mock/objects.js b/test/unit/mock/objects.js index b525b6c6..1c82cc1a 100644 --- a/test/unit/mock/objects.js +++ b/test/unit/mock/objects.js @@ -212,7 +212,7 @@ const releaseMock = { const assetMock = { ...systemFieldsMock, ...systemFieldsUserMock, - content_type: 'image/png', + content_type: 'text/html', file_size: '42670', tags: [], filename: 'file.png', @@ -539,7 +539,7 @@ const appInstallMock = { status: 'installed', installation_uid: 'installationUID', redirect_to: 'config', - redirect_uri: 'redirect_uri', + redirect_uri: 'redirect_uri' } const installationMock = { @@ -613,7 +613,7 @@ const branchCompareAllMock = { compare_branch: 'dev' }, diff: [...globalFieldDiff, ...contentTypeDiff], - next_url:'https://api.contentstack.io/v3/stacks/branches/compare?base_branch=main&compare_branch=dev&skip=0&limit=100' + next_url: 'https://api.contentstack.io/v3/stacks/branches/compare?base_branch=main&compare_branch=dev&skip=0&limit=100' } const branchCompareContentTypeMock = { @@ -621,8 +621,8 @@ const branchCompareContentTypeMock = { base_branch: 'UID', compare_branch: 'dev' }, - diff: [ ...contentTypeDiff ], - next_url:'https://api.contentstack.io/v3/stacks/branches/compare?base_branch=main&compare_branch=dev&skip=0&limit=100' + diff: [...contentTypeDiff], + next_url: 'https://api.contentstack.io/v3/stacks/branches/compare?base_branch=main&compare_branch=dev&skip=0&limit=100' } const branchCompareGlobalFieldMock = { @@ -631,7 +631,7 @@ const branchCompareGlobalFieldMock = { compare_branch: 'dev' }, diff: [...globalFieldDiff], - next_url:'https://api.contentstack.io/v3/stacks/branches/compare?base_branch=main&compare_branch=dev&skip=0&limit=100' + next_url: 'https://api.contentstack.io/v3/stacks/branches/compare?base_branch=main&compare_branch=dev&skip=0&limit=100' } const branchMergeAllMock = { @@ -647,7 +647,7 @@ const branchMergeAllMock = { status: 'in_progress' }, merged_at: null, - errors: [ + errors: [ { code: 'error_code', message: 'Error message' @@ -725,6 +725,56 @@ const auditLogsMock = { ] } +const taxonomyMock = { + uid: 'UID', + name: 'name', + description: 'Description for Taxonomy', + terms_count: 4, + referenced_terms_count: 3, + referenced_entries_count: 6 +} +const termsMock = { + taxonomy_uid: 'taxonomy_uid', + uid: 'UID', + name: 'name', + parent_uid: 'term_2', + depth: 2, + children_count: 2, + referenced_entries_count: 2, + ancestors: [{ + uid: 'term_1', + name: 'Term 1', + parent_uid: null, + depth: 1, + children_count: 3, + referenced_entries_count: 3 + }, + { + uid: 'term_2', + name: 'Term 2', + parent_uid: 'term_1', + depth: 2, + children_count: 2, + referenced_entries_count: 2 + }], + descendants: [{ + uid: 'term_4', + name: 'Term 4', + parent_uid: 'term_3', + depth: 3, + children_count: 1, + referenced_entries_count: 2 + }, + { + uid: 'term_5', + name: 'Term 5', + parent_uid: 'term_4', + depth: 4, + children_count: 0, + referenced_entries_count: 4 + }] +} + function mockCollection (mockData, type) { const mock = { ...cloneDeep(noticeMock), @@ -793,6 +843,8 @@ export { branchMergeQueueFetchMock, auditLogsMock, auditLogItemMock, + taxonomyMock, + termsMock, mockCollection, entryMockCollection, checkSystemFields diff --git a/test/unit/taxonomy-test.js b/test/unit/taxonomy-test.js new file mode 100644 index 00000000..b9435185 --- /dev/null +++ b/test/unit/taxonomy-test.js @@ -0,0 +1,132 @@ +import Axios from 'axios' +import { expect } from 'chai' +import { describe, it } from 'mocha' +import MockAdapter from 'axios-mock-adapter' +import { Taxonomy } from '../../lib/stack/taxonomy' +import { systemUidMock, stackHeadersMock, taxonomyMock, noticeMock } from './mock/objects' + +describe('Contentstack Taxonomy test', () => { + it('taxonomy create test', done => { + var mock = new MockAdapter(Axios) + mock.onPost('/taxonomies').reply(200, { + taxonomy: { + ...taxonomyMock + } + }) + makeTaxonomy() + .create() + .then((taxonomy) => { + checkTaxonomy(taxonomy) + done() + }) + .catch(done) + }) + it('Taxonomy fetch test', done => { + var mock = new MockAdapter(Axios) + mock.onGet('/taxonomies/UID').reply(200, { + taxonomy: { + ...taxonomyMock + } + }) + makeTaxonomy({ + taxonomy: { + ...systemUidMock + }, + stackHeaders: stackHeadersMock + }) + .fetch() + .then((taxonomy) => { + checkTaxonomy(taxonomy) + done() + }) + .catch(done) + }) + it('Taxonomies query test', done => { + var mock = new MockAdapter(Axios) + mock.onGet('/taxonomies').reply(200, { + taxonomy: [ + taxonomyMock + ] + }) + makeTaxonomy() + .query() + .find() + .then((taxonomies) => { + checkTaxonomy(taxonomies.items[0]) + done() + }) + .catch(done) + }) + it('Taxonomy update test', done => { + var mock = new MockAdapter(Axios) + mock.onPut('/taxonomies/UID').reply(200, { + taxonomy: { + ...taxonomyMock + } + }) + makeTaxonomy({ + taxonomy: { + ...systemUidMock + }, + stackHeaders: stackHeadersMock + }) + .update() + .then((taxonomy) => { + checkTaxonomy(taxonomy) + done() + }) + .catch(done) + }) + it('taxonomy delete test', done => { + var mock = new MockAdapter(Axios) + mock.onDelete('/taxonomies/UID').reply(200, { + ...noticeMock + }) + makeTaxonomy({ + taxonomy: { + ...systemUidMock + }, + stackHeaders: stackHeadersMock + }) + .delete() + .then((response) => { + expect(response.notice).to.be.equal(noticeMock.notice) + done() + }) + .catch(done) + }) + it('Taxonomy test without uid', done => { + const taxonomy = makeTaxonomy() + expect(taxonomy.urlPath).to.be.equal('/taxonomies') + expect(taxonomy.stackHeaders).to.be.equal(undefined) + expect(taxonomy.update).to.be.equal(undefined) + expect(taxonomy.delete).to.be.equal(undefined) + expect(taxonomy.fetch).to.be.equal(undefined) + expect(taxonomy.create).to.not.equal(undefined) + expect(taxonomy.query).to.not.equal(undefined) + done() + }) + it('Taxonomy test with uid', done => { + const taxonomy = makeTaxonomy({ + taxonomy: { + ...systemUidMock + } + }) + expect(taxonomy.urlPath).to.be.equal(`/taxonomies/${systemUidMock.uid}`) + expect(taxonomy.stackHeaders).to.be.equal(undefined) + expect(taxonomy.update).to.not.equal(undefined) + expect(taxonomy.delete).to.not.equal(undefined) + expect(taxonomy.fetch).to.not.equal(undefined) + expect(taxonomy.create).to.be.equal(undefined) + expect(taxonomy.query).to.be.equal(undefined) + done() + }) +}) + +function makeTaxonomy (data = {}) { + return new Taxonomy(Axios, data) +} + +function checkTaxonomy (taxonomy) { + expect(taxonomy.name).to.be.equal('name') +} diff --git a/test/unit/terms-test.js b/test/unit/terms-test.js new file mode 100644 index 00000000..67e93420 --- /dev/null +++ b/test/unit/terms-test.js @@ -0,0 +1,214 @@ +import Axios from 'axios' +import { expect } from 'chai' +import { describe, it } from 'mocha' +import MockAdapter from 'axios-mock-adapter' +import { Terms } from '../../lib/stack/taxonomy/terms' +import { systemUidMock, stackHeadersMock, termsMock, noticeMock } from './mock/objects' + +describe('Contentstack Term test', () => { + it('term create test', done => { + var mock = new MockAdapter(Axios) + mock.onPost(`/taxonomies/taxonomy_uid/terms`).reply(200, { + term: { + ...termsMock + } + }) + makeTerms() + .create() + .then((term) => { + checkTerms(term) + done() + }) + .catch(done) + }) + it('Term fetch test', done => { + var mock = new MockAdapter(Axios) + mock.onGet(`/taxonomies/taxonomy_uid/terms/UID`).reply(200, { + term: { + ...termsMock + } + }) + makeTerms({ + term: { + ...systemUidMock + }, + stackHeaders: stackHeadersMock + }) + .fetch() + .then((term) => { + checkTerms(term) + done() + }) + .catch(done) + }) + it('Terms query test', done => { + var mock = new MockAdapter(Axios) + mock.onGet(`/taxonomies/taxonomy_uid/terms`).reply(200, { + terms: [ + termsMock + ] + }) + makeTerms() + .query() + .find() + .then((terms) => { + checkTerms(terms.items[0]) + done() + }) + .catch(done) + }) + it('Term update test', done => { + var mock = new MockAdapter(Axios) + mock.onPut(`/taxonomies/taxonomy_uid/terms/UID`).reply(200, { + term: { + ...termsMock + } + }) + makeTerms({ + term: { + ...systemUidMock + }, + stackHeaders: stackHeadersMock + }) + .update() + .then((term) => { + checkTerms(term) + done() + }) + .catch(done) + }) + it('term delete test', done => { + var mock = new MockAdapter(Axios) + mock.onDelete(`/taxonomies/taxonomy_uid/terms/UID`).reply(200, { + ...noticeMock + }) + makeTerms({ + term: { + ...systemUidMock + }, + stackHeaders: stackHeadersMock + }) + .delete() + .then((term) => { + expect(term.notice).to.be.equal(noticeMock.notice) + done() + }) + .catch(done) + }) + it('term ancestors test', done => { + var mock = new MockAdapter(Axios) + mock.onGet(`/taxonomies/taxonomy_uid/terms/UID/ancestors`).reply(200, { + term: { + ...termsMock + } + }) + makeTerms({ + term: { + ...systemUidMock + }, + stackHeaders: stackHeadersMock + }) + .ancestors() + .then((terms) => { + expect(terms.term.uid).to.be.equal('UID') + expect(terms.term.parent_uid).to.be.equal('term_2') + expect(terms.term.ancestors[0].uid).to.be.equal('term_1') + expect(terms.term.ancestors[1].uid).to.be.equal('term_2') + expect(terms.term.ancestors[1].parent_uid).to.be.equal('term_1') + done() + }) + .catch(done) + }) + it('term descendants test', done => { + var mock = new MockAdapter(Axios) + mock.onGet(`/taxonomies/taxonomy_uid/terms/UID/descendants`).reply(200, { + term: { + ...termsMock + } + }) + makeTerms({ + term: { + ...systemUidMock + }, + stackHeaders: stackHeadersMock + }) + .descendants() + .then((terms) => { + expect(terms.term.uid).to.be.equal('UID') + expect(terms.term.descendants[0].uid).to.be.equal('term_4') + expect(terms.term.descendants[1].uid).to.be.equal('term_5') + expect(terms.term.descendants[1].parent_uid).to.be.equal('term_4') + done() + }) + .catch(done) + }) + it('Term test without term uid', done => { + const term = makeTerms() + expect(term.stackHeaders).to.be.equal(undefined) + expect(term.update).to.be.equal(undefined) + expect(term.delete).to.be.equal(undefined) + expect(term.fetch).to.be.equal(undefined) + expect(term.create).not.to.be.equal(undefined) + expect(term.query).not.to.be.equal(undefined) + done() + }) + it('Term test with term uid', done => { + const term = makeTerms({ + term: { + ...systemUidMock + } + }) + expect(term.urlPath).to.be.equal(`/taxonomies/taxonomy_uid/terms/${systemUidMock.uid}`) + expect(term.stackHeaders).to.be.equal(undefined) + expect(term.update).not.to.be.equal(undefined) + expect(term.delete).not.to.be.equal(undefined) + expect(term.fetch).not.to.be.equal(undefined) + expect(term.create).to.be.equal(undefined) + expect(term.query).to.be.equal(undefined) + done() + }) + it('term search test', done => { + var mock = new MockAdapter(Axios) + mock.onGet(`/taxonomies/taxonomy_uid/terms?term=UID`).reply(200, { + term: { + ...termsMock + } + }) + makeTerms() + .search('UID') + .then((terms) => { + expect(terms.term.uid).to.be.equal('UID') + done() + }) + .catch(done) + }) + it('term move test', done => { + var mock = new MockAdapter(Axios) + mock.onPut(`/taxonomies/taxonomy_uid/terms/UID/move`).reply(200, { + term: { + ...termsMock + } + }) + makeTerms({ + term: { + ...systemUidMock + }, + stackHeaders: stackHeadersMock + }) + .move() + .then((terms) => { + checkTerms(terms) + expect(terms.uid).to.be.equal('UID') + done() + }) + .catch(done) + }) +}) + +function makeTerms (data = {}) { + return new Terms(Axios, { taxonomy_uid: 'taxonomy_uid', ...data }) +} + +function checkTerms (terms) { + expect(terms.name).to.be.equal('name') +} diff --git a/test/unit/user-test.js b/test/unit/user-test.js index 2d0c6318..13d599b3 100644 --- a/test/unit/user-test.js +++ b/test/unit/user-test.js @@ -162,26 +162,25 @@ describe('Contentstack User test', () => { }) user.getTasks() - .then((userTasks) => { - const assignment = userTasks.asassignments - expect(assignment.api_key).to.be.equal(stackHeadersMock.api_key) - expect(assignment.content_type).to.be.equal("CT_UID") - expect(assignment.entry_uid).to.be.equal("ETR_UID") - expect(assignment.locale).to.be.equal("en-us") - expect(assignment.org_uid).to.be.equal("orgUID") - expect(assignment.type).to.be.equal("workflow_stage") - expect(assignment.entry_locale).to.be.equal("en-us") - expect(assignment.version).to.be.equal(1) - expect(assignment.assigned_to[0]).to.be.equal("user_UID") - expect(assignment.assigned_at).to.be.equal("assign_date") - expect(assignment.assigned_by).to.be.equal("assign_by") - expect(assignment.due_date).to.be.equal("due_date") - done() - }) - .catch(done) + .then((userTasks) => { + const assignment = userTasks.asassignments + expect(assignment.api_key).to.be.equal(stackHeadersMock.api_key) + expect(assignment.content_type).to.be.equal('CT_UID') + expect(assignment.entry_uid).to.be.equal('ETR_UID') + expect(assignment.locale).to.be.equal('en-us') + expect(assignment.org_uid).to.be.equal('orgUID') + expect(assignment.type).to.be.equal('workflow_stage') + expect(assignment.entry_locale).to.be.equal('en-us') + expect(assignment.version).to.be.equal(1) + expect(assignment.assigned_to[0]).to.be.equal('user_UID') + expect(assignment.assigned_at).to.be.equal('assign_date') + expect(assignment.assigned_by).to.be.equal('assign_by') + expect(assignment.due_date).to.be.equal('due_date') + done() + }) + .catch(done) }) - it('User get task with params test', done => { var mock = new MockAdapter(Axios) mock.onGet('/user/assignments').reply(200, { @@ -194,24 +193,24 @@ describe('Contentstack User test', () => { } }) - user.getTasks({sort: 'sort'}) - .then((userTasks) => { - const assignment = userTasks.asassignments - expect(assignment.api_key).to.be.equal(stackHeadersMock.api_key) - expect(assignment.content_type).to.be.equal("CT_UID") - expect(assignment.entry_uid).to.be.equal("ETR_UID") - expect(assignment.locale).to.be.equal("en-us") - expect(assignment.org_uid).to.be.equal("orgUID") - expect(assignment.type).to.be.equal("workflow_stage") - expect(assignment.entry_locale).to.be.equal("en-us") - expect(assignment.version).to.be.equal(1) - expect(assignment.assigned_to[0]).to.be.equal("user_UID") - expect(assignment.assigned_at).to.be.equal("assign_date") - expect(assignment.assigned_by).to.be.equal("assign_by") - expect(assignment.due_date).to.be.equal("due_date") - done() - }) - .catch(done) + user.getTasks({ sort: 'sort' }) + .then((userTasks) => { + const assignment = userTasks.asassignments + expect(assignment.api_key).to.be.equal(stackHeadersMock.api_key) + expect(assignment.content_type).to.be.equal('CT_UID') + expect(assignment.entry_uid).to.be.equal('ETR_UID') + expect(assignment.locale).to.be.equal('en-us') + expect(assignment.org_uid).to.be.equal('orgUID') + expect(assignment.type).to.be.equal('workflow_stage') + expect(assignment.entry_locale).to.be.equal('en-us') + expect(assignment.version).to.be.equal(1) + expect(assignment.assigned_to[0]).to.be.equal('user_UID') + expect(assignment.assigned_at).to.be.equal('assign_date') + expect(assignment.assigned_by).to.be.equal('assign_by') + expect(assignment.due_date).to.be.equal('due_date') + done() + }) + .catch(done) }) })