From 06cb189dca75100be41088f8efc1e9699c506d1c Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:26:50 +0530 Subject: [PATCH 1/4] Added buffer upload in assets --- CHANGELOG.md | 4 +++ lib/stack/asset/index.js | 18 +++++++++--- package-lock.json | 4 +-- package.json | 2 +- test/sanity-check/api/asset-test.js | 26 +++++++++++++++++ test/unit/asset-test.js | 44 +++++++++++++++++++++++++++++ 6 files changed, 91 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08500cf7..d7b701b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [v1.19.6](https://github.com/contentstack/contentstack-management-javascript/tree/v1.19.6) (2025-03-31) + - Enhancement + - Added buffer upload in assets + ## [v1.19.5](https://github.com/contentstack/contentstack-management-javascript/tree/v1.19.5) (2025-03-17) - Fix - Added AuditLog in the stack class diff --git a/lib/stack/asset/index.js b/lib/stack/asset/index.js index 0d1a27e5..f6c513b8 100644 --- a/lib/stack/asset/index.js +++ b/lib/stack/asset/index.js @@ -291,11 +291,21 @@ export function createFormData (data) { if (typeof data.title === 'string') { formData.append('asset[title]', data.title) } - const uploadStream = createReadStream(data.upload) - if (typeof data.content_type === 'string') { - formData.append('asset[upload]', uploadStream, { contentType: data.content_type }) + // Handle Buffer Upload + if (Buffer.isBuffer(data.upload)) { + formData.append("asset[upload]", data.upload, { + filename: data.filename || "uploaded_file", + contentType: data.content_type || "application/octet-stream", + }); + } else if (typeof data.upload === "string") { // Handle File Path Upload + const uploadStream = createReadStream(data.upload); + if (typeof data.content_type === "string") { + formData.append("asset[upload]", uploadStream, { contentType: data.content_type }); + } else { + formData.append("asset[upload]", uploadStream); + } } else { - formData.append('asset[upload]', uploadStream) + throw new Error("Invalid upload format. Must be a file path or Buffer."); } return formData } diff --git a/package-lock.json b/package-lock.json index 4f66bd64..a7aea494 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@contentstack/management", - "version": "1.19.5", + "version": "1.19.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contentstack/management", - "version": "1.19.5", + "version": "1.19.6", "license": "MIT", "dependencies": { "axios": "^1.8.2", diff --git a/package.json b/package.json index 691d9447..a60e84eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/management", - "version": "1.19.5", + "version": "1.19.6", "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", diff --git a/test/sanity-check/api/asset-test.js b/test/sanity-check/api/asset-test.js index 4aadb5be..381fc212 100644 --- a/test/sanity-check/api/asset-test.js +++ b/test/sanity-check/api/asset-test.js @@ -1,3 +1,4 @@ +import fs from 'fs'; import path from 'path' import { expect } from 'chai' import { describe, it, setup } from 'mocha' @@ -38,6 +39,31 @@ describe('Assets api Test', () => { }) .catch(done) }) + + it('should upload asset from buffer', (done) => { + const filePath = path.join(__dirname, '../mock/customUpload.html'); + const fileBuffer = fs.readFileSync(filePath); // Read file into Buffer + const asset = { + upload: fileBuffer, // Buffer upload + filename: 'customUpload.html', // Ensure filename is provided + content_type: 'text/html', // Set content type + title: 'buffer-asset', + description: 'Buffer Asset Desc', + tags: ['Buffer'] + }; + makeAsset().create(asset) + .then((asset) => { + jsonWrite(asset, 'bufferAsset.json'); + expect(asset.uid).to.be.not.equal(null); + expect(asset.url).to.be.not.equal(null); + expect(asset.filename).to.be.equal('customUpload.html'); + expect(asset.title).to.be.equal('buffer-asset'); + expect(asset.description).to.be.equal('Buffer Asset Desc'); + expect(asset.content_type).to.be.equal('text/html'); + done(); + }) + .catch(done); + }); it('should download asset from URL.', done => { makeAsset().download({ url: assetURL, responseType: 'stream' }) diff --git a/test/unit/asset-test.js b/test/unit/asset-test.js index 24926889..7331aea0 100644 --- a/test/unit/asset-test.js +++ b/test/unit/asset-test.js @@ -1,3 +1,4 @@ +import fs from 'fs'; import path from 'path' import Axios from 'axios' import { expect } from 'chai' @@ -171,6 +172,49 @@ describe('Contentstack Asset test', () => { .catch(done) }) + it('should upload asset from buffer', (done) => { + const mock = new MockAdapter(Axios); + mock.onPost('/assets').reply(200, { + asset: { + uid: 'mock-uid', + url: '/assets', + filename: 'customUpload.html', + title: 'buffer-asset', + description: 'Buffer Asset Desc', + content_type: 'text/html', + tags: ['Buffer'], + parent_uid: 'UID' + } + }); + const filePath = path.join(__dirname, '../api/mock/customUpload.html'); + const fileBuffer = fs.readFileSync(filePath); + const assetUpload = { + upload: fileBuffer, // Buffer upload + filename: 'customUpload.html', // Filename to identify the file + content_type: 'text/html', // MIME type + title: 'buffer-asset', + description: 'Buffer Asset Desc', + tags: ['Buffer'], + parent_uid: 'UID' + }; + const form = createFormData(assetUpload)(); // Create FormData for Buffer upload + const boundary = form.getBoundary(); + expect(boundary).to.be.equal(form.getBoundary()); + expect(boundary.length).to.be.greaterThan(30); + makeAsset() + .create(assetUpload) + .then((asset) => { + expect(asset.uid).to.be.equal('mock-uid'); + expect(asset.filename).to.be.equal('customUpload.html'); + expect(asset.title).to.be.equal('buffer-asset'); + expect(asset.description).to.be.equal('Buffer Asset Desc'); + expect(asset.content_type).to.be.equal('text/html'); + expect(asset.tags).to.include('Buffer'); + done(); + }) + .catch(done); + }); + it('Asset replace test', done => { var mock = new MockAdapter(Axios) mock.onPut('/assets/UID').reply(200, { From 2a08666c44acad9468f868dbbeabad382cb0f5e0 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:33:18 +0530 Subject: [PATCH 2/4] Fixed linting erros --- lib/stack/asset/index.js | 20 ++++++------- test/sanity-check/api/asset-test.js | 36 +++++++++++------------ test/unit/asset-test.js | 44 ++++++++++++++--------------- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/lib/stack/asset/index.js b/lib/stack/asset/index.js index f6c513b8..22e7757c 100644 --- a/lib/stack/asset/index.js +++ b/lib/stack/asset/index.js @@ -293,19 +293,19 @@ export function createFormData (data) { } // Handle Buffer Upload if (Buffer.isBuffer(data.upload)) { - formData.append("asset[upload]", data.upload, { - filename: data.filename || "uploaded_file", - contentType: data.content_type || "application/octet-stream", - }); - } else if (typeof data.upload === "string") { // Handle File Path Upload - const uploadStream = createReadStream(data.upload); - if (typeof data.content_type === "string") { - formData.append("asset[upload]", uploadStream, { contentType: data.content_type }); + formData.append('asset[upload]', data.upload, { + filename: data.filename || 'uploaded_file', + contentType: data.content_type || 'application/octet-stream' + }) + } else if (typeof data.upload === 'string') { // Handle File Path Upload + const uploadStream = createReadStream(data.upload) + if (typeof data.content_type === 'string') { + formData.append('asset[upload]', uploadStream, { contentType: data.content_type }) } else { - formData.append("asset[upload]", uploadStream); + formData.append('asset[upload]', uploadStream) } } else { - throw new Error("Invalid upload format. Must be a file path or Buffer."); + throw new Error('Invalid upload format. Must be a file path or Buffer.') } return formData } diff --git a/test/sanity-check/api/asset-test.js b/test/sanity-check/api/asset-test.js index 381fc212..72cb6cdf 100644 --- a/test/sanity-check/api/asset-test.js +++ b/test/sanity-check/api/asset-test.js @@ -1,4 +1,4 @@ -import fs from 'fs'; +import fs from 'fs' import path from 'path' import { expect } from 'chai' import { describe, it, setup } from 'mocha' @@ -39,31 +39,31 @@ describe('Assets api Test', () => { }) .catch(done) }) - + it('should upload asset from buffer', (done) => { - const filePath = path.join(__dirname, '../mock/customUpload.html'); - const fileBuffer = fs.readFileSync(filePath); // Read file into Buffer + const filePath = path.join(__dirname, '../mock/customUpload.html') + const fileBuffer = fs.readFileSync(filePath) // Read file into Buffer const asset = { - upload: fileBuffer, // Buffer upload - filename: 'customUpload.html', // Ensure filename is provided - content_type: 'text/html', // Set content type + upload: fileBuffer, // Buffer upload + filename: 'customUpload.html', // Ensure filename is provided + content_type: 'text/html', // Set content type title: 'buffer-asset', description: 'Buffer Asset Desc', tags: ['Buffer'] - }; + } makeAsset().create(asset) .then((asset) => { - jsonWrite(asset, 'bufferAsset.json'); - expect(asset.uid).to.be.not.equal(null); - expect(asset.url).to.be.not.equal(null); - expect(asset.filename).to.be.equal('customUpload.html'); - expect(asset.title).to.be.equal('buffer-asset'); - expect(asset.description).to.be.equal('Buffer Asset Desc'); - expect(asset.content_type).to.be.equal('text/html'); - done(); + jsonWrite(asset, 'bufferAsset.json') + expect(asset.uid).to.be.not.equal(null) + expect(asset.url).to.be.not.equal(null) + expect(asset.filename).to.be.equal('customUpload.html') + expect(asset.title).to.be.equal('buffer-asset') + expect(asset.description).to.be.equal('Buffer Asset Desc') + expect(asset.content_type).to.be.equal('text/html') + done() }) - .catch(done); - }); + .catch(done) + }) it('should download asset from URL.', done => { makeAsset().download({ url: assetURL, responseType: 'stream' }) diff --git a/test/unit/asset-test.js b/test/unit/asset-test.js index 7331aea0..bf4cb3b3 100644 --- a/test/unit/asset-test.js +++ b/test/unit/asset-test.js @@ -1,4 +1,4 @@ -import fs from 'fs'; +import fs from 'fs' import path from 'path' import Axios from 'axios' import { expect } from 'chai' @@ -173,7 +173,7 @@ describe('Contentstack Asset test', () => { }) it('should upload asset from buffer', (done) => { - const mock = new MockAdapter(Axios); + const mock = new MockAdapter(Axios) mock.onPost('/assets').reply(200, { asset: { uid: 'mock-uid', @@ -185,35 +185,35 @@ describe('Contentstack Asset test', () => { tags: ['Buffer'], parent_uid: 'UID' } - }); - const filePath = path.join(__dirname, '../api/mock/customUpload.html'); - const fileBuffer = fs.readFileSync(filePath); + }) + const filePath = path.join(__dirname, '../api/mock/customUpload.html') + const fileBuffer = fs.readFileSync(filePath) const assetUpload = { - upload: fileBuffer, // Buffer upload - filename: 'customUpload.html', // Filename to identify the file - content_type: 'text/html', // MIME type + upload: fileBuffer, // Buffer upload + filename: 'customUpload.html', // Filename to identify the file + content_type: 'text/html', // MIME type title: 'buffer-asset', description: 'Buffer Asset Desc', tags: ['Buffer'], parent_uid: 'UID' - }; - const form = createFormData(assetUpload)(); // Create FormData for Buffer upload - const boundary = form.getBoundary(); - expect(boundary).to.be.equal(form.getBoundary()); - expect(boundary.length).to.be.greaterThan(30); + } + const form = createFormData(assetUpload)() // Create FormData for Buffer upload + const boundary = form.getBoundary() + expect(boundary).to.be.equal(form.getBoundary()) + expect(boundary.length).to.be.greaterThan(30) makeAsset() .create(assetUpload) .then((asset) => { - expect(asset.uid).to.be.equal('mock-uid'); - expect(asset.filename).to.be.equal('customUpload.html'); - expect(asset.title).to.be.equal('buffer-asset'); - expect(asset.description).to.be.equal('Buffer Asset Desc'); - expect(asset.content_type).to.be.equal('text/html'); - expect(asset.tags).to.include('Buffer'); - done(); + expect(asset.uid).to.be.equal('mock-uid') + expect(asset.filename).to.be.equal('customUpload.html') + expect(asset.title).to.be.equal('buffer-asset') + expect(asset.description).to.be.equal('Buffer Asset Desc') + expect(asset.content_type).to.be.equal('text/html') + expect(asset.tags).to.include('Buffer') + done() }) - .catch(done); - }); + .catch(done) + }) it('Asset replace test', done => { var mock = new MockAdapter(Axios) From 49ad4c2a23222a5cae8e555df3548def5e9de024 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:51:19 +0530 Subject: [PATCH 3/4] axios version bump --- package-lock.json | 6 ++++-- package.json | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a7aea494..639e3179 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.19.6", "license": "MIT", "dependencies": { - "axios": "^1.8.2", + "axios": "^1.8.3", "form-data": "^4.0.2", "lodash": "^4.17.21", "qs": "^6.14.0" @@ -3681,7 +3681,9 @@ } }, "node_modules/axios": { - "version": "1.8.2", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", diff --git a/package.json b/package.json index a60e84eb..76a74ee0 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "author": "Contentstack", "license": "MIT", "dependencies": { - "axios": "^1.8.2", + "axios": "^1.8.3", "form-data": "^4.0.2", "lodash": "^4.17.21", "qs": "^6.14.0" From b747cf34eed6d8ad2404f5bd73a20e86a02e8ab3 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:18:32 +0530 Subject: [PATCH 4/4] Downgraded version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 639e3179..27c8a5ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@contentstack/management", - "version": "1.19.6", + "version": "1.19.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contentstack/management", - "version": "1.19.6", + "version": "1.19.5", "license": "MIT", "dependencies": { "axios": "^1.8.3", diff --git a/package.json b/package.json index 76a74ee0..2895cf97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/management", - "version": "1.19.6", + "version": "1.19.5", "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",