Skip to content

Commit 52b49fc

Browse files
Merge branch 'development' into fix/1694-global-fields
2 parents 76c65a5 + 2264ba5 commit 52b49fc

File tree

7 files changed

+157
-59
lines changed

7 files changed

+157
-59
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# Changelog
22

33
## [v1.19.6](https://github.com/contentstack/contentstack-management-javascript/tree/v1.19.6) (2025-03-24)
4-
- Fix
4+
- Enhancement
55
- Added stack headers in global fields response
6+
- Added buffer upload in assets
7+
68
## [v1.19.5](https://github.com/contentstack/contentstack-management-javascript/tree/v1.19.5) (2025-03-17)
79
- Fix
810
- Added AuditLog in the stack class

lib/stack/asset/index.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,21 @@ export function createFormData (data) {
291291
if (typeof data.title === 'string') {
292292
formData.append('asset[title]', data.title)
293293
}
294-
const uploadStream = createReadStream(data.upload)
295-
if (typeof data.content_type === 'string') {
296-
formData.append('asset[upload]', uploadStream, { contentType: data.content_type })
294+
// Handle Buffer Upload
295+
if (Buffer.isBuffer(data.upload)) {
296+
formData.append('asset[upload]', data.upload, {
297+
filename: data.filename || 'uploaded_file',
298+
contentType: data.content_type || 'application/octet-stream'
299+
})
300+
} else if (typeof data.upload === 'string') { // Handle File Path Upload
301+
const uploadStream = createReadStream(data.upload)
302+
if (typeof data.content_type === 'string') {
303+
formData.append('asset[upload]', uploadStream, { contentType: data.content_type })
304+
} else {
305+
formData.append('asset[upload]', uploadStream)
306+
}
297307
} else {
298-
formData.append('asset[upload]', uploadStream)
308+
throw new Error('Invalid upload format. Must be a file path or Buffer.')
299309
}
300310
return formData
301311
}

package-lock.json

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"author": "Contentstack",
5353
"license": "MIT",
5454
"dependencies": {
55-
"axios": "^1.8.2",
55+
"axios": "^1.8.3",
5656
"form-data": "^4.0.2",
5757
"lodash": "^4.17.21",
5858
"qs": "^6.14.0"

sanity-report-dev11.js

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,88 @@
1-
const dotenv = require('dotenv')
2-
const fs = require('fs')
1+
import Slack from "@slack/bolt";
2+
const { App } = Slack;
3+
import dotenv from "dotenv";
4+
import fs from "fs";
35

4-
dotenv.config()
6+
dotenv.config();
57

6-
const user1 = process.env.USER1
7-
const user2 = process.env.USER2
8-
const user3 = process.env.USER3
9-
const user4 = process.env.USER4
8+
const user1 = process.env.USER1;
9+
const user2 = process.env.USER2;
10+
const user3 = process.env.USER3;
11+
const user4 = process.env.USER4;
1012

1113
const mochawesomeJsonOutput = fs.readFileSync(
12-
'./mochawesome-report/mochawesome.json',
13-
'utf-8'
14-
)
15-
const mochawesomeReport = JSON.parse(mochawesomeJsonOutput)
14+
"./mochawesome-report/mochawesome.json",
15+
"utf-8"
16+
);
17+
const mochawesomeReport = JSON.parse(mochawesomeJsonOutput);
1618

17-
const totalTests = mochawesomeReport.stats.tests
18-
const passedTests = mochawesomeReport.stats.passes
19-
const failedTests = mochawesomeReport.stats.failures
19+
const totalTests = mochawesomeReport.stats.tests;
20+
const passedTests = mochawesomeReport.stats.passes;
21+
const failedTests = mochawesomeReport.stats.failures;
2022

21-
let durationInSeconds = Math.floor(mochawesomeReport.stats.duration / 1000)
22-
const durationInMinutes = Math.floor(durationInSeconds / 60)
23-
durationInSeconds %= 60
23+
let durationInSeconds = Math.floor(mochawesomeReport.stats.duration / 1000);
24+
const durationInMinutes = Math.floor(durationInSeconds / 60);
25+
durationInSeconds %= 60;
2426

2527
const resultMessage =
2628
passedTests === totalTests
2729
? `:white_check_mark: Success (${passedTests} / ${totalTests} Passed)`
28-
: `:x: Failure (${passedTests} / ${totalTests} Passed)`
30+
: `:x: Failure (${passedTests} / ${totalTests} Passed)`;
2931

30-
const pipelineName = process.env.GO_PIPELINE_NAME
31-
const pipelineCounter = process.env.GO_PIPELINE_COUNTER
32-
const goCdServer = process.env.GOCD_SERVER
32+
const pipelineName = process.env.GO_PIPELINE_NAME;
33+
const pipelineCounter = process.env.GO_PIPELINE_COUNTER;
34+
const goCdServer = process.env.GOCD_SERVER;
3335

34-
const reportUrl = `http://${goCdServer}/go/files/${pipelineName}/${pipelineCounter}/sanity/1/sanity/test-results/mochawesome-report/sanity-report.html`
36+
const reportUrl = `http://${goCdServer}/go/files/${pipelineName}/${pipelineCounter}/sanity/1/sanity/test-results/mochawesome-report/sanity-report.html`;
3537

36-
let tagUsers = ``
37-
if (failedTests > 0) {
38-
tagUsers = `<@${user1}> <@${user2}> <@${user3}> <@${user4}>`
39-
}
38+
let tagUsers =
39+
failedTests > 0 ? `<@${user1}> <@${user2}> <@${user3}> <@${user4}>` : "";
4040

4141
const slackMessage = {
42-
text: `Dev11, SDK-CMA Sanity
43-
*Result:* ${resultMessage}. ${durationInMinutes}m ${durationInSeconds}s
44-
*Failed Tests:* ${failedTests}
45-
<${reportUrl}|View Report>
46-
${tagUsers}`
47-
}
42+
text: `Dev11, SDK-CMA Sanity\n*Result:* ${resultMessage}. ${durationInMinutes}m ${durationInSeconds}s\n*Failed Tests:* ${failedTests}\n<${reportUrl}|View Report>\n${tagUsers}`,
43+
};
4844

49-
const slackWebhookUrl = process.env.SLACK_WEBHOOK_URL
45+
const app = new App({
46+
token: process.env.SLACK_BOT_TOKEN,
47+
signingSecret: process.env.SLACK_SIGNING_SECRET,
48+
});
5049

5150
const sendSlackMessage = async (message) => {
52-
const payload = {
53-
text: message
54-
}
55-
5651
try {
57-
const response = await fetch(slackWebhookUrl, {
58-
method: 'POST',
59-
headers: {
60-
'Content-Type': 'application/json'
61-
},
62-
body: JSON.stringify(payload)
63-
})
64-
65-
if (!response.ok) {
66-
throw new Error(`Error sending message to Slack: ${response.statusText}`)
52+
const result = await app.client.chat.postMessage({
53+
token: process.env.SLACK_BOT_TOKEN,
54+
channel: process.env.SLACK_CHANNEL2,
55+
text: message,
56+
});
57+
58+
if (failedTests > 0) {
59+
await sendFailureDetails(result.ts);
6760
}
61+
} catch (error) {
62+
console.error("Error sending Slack message:", error);
63+
}
64+
};
6865

69-
console.log('Message sent to Slack successfully')
66+
const sendFailureDetails = async (threadTs) => {
67+
const failedSuites = mochawesomeReport.results
68+
.flatMap((result) => result.suites)
69+
.filter((suite) => suite.failures.length > 0);
70+
71+
let failureDetails = "*Failed Test Modules:*\n";
72+
for (const suite of failedSuites) {
73+
failureDetails += `- *${suite.title}*: ${suite.failures.length} failed\n`;
74+
}
75+
76+
try {
77+
await app.client.chat.postMessage({
78+
token: process.env.SLACK_BOT_TOKEN,
79+
channel: process.env.SLACK_CHANNEL,
80+
text: failureDetails,
81+
thread_ts: threadTs,
82+
});
7083
} catch (error) {
71-
console.error('Error:', error)
84+
console.error("Error sending failure details:", error);
7285
}
73-
}
74-
sendSlackMessage(slackMessage.text)
86+
};
87+
88+
sendSlackMessage(slackMessage.text);

test/sanity-check/api/asset-test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fs from 'fs'
12
import path from 'path'
23
import { expect } from 'chai'
34
import { describe, it, setup } from 'mocha'
@@ -39,6 +40,31 @@ describe('Assets api Test', () => {
3940
.catch(done)
4041
})
4142

43+
it('should upload asset from buffer', (done) => {
44+
const filePath = path.join(__dirname, '../mock/customUpload.html')
45+
const fileBuffer = fs.readFileSync(filePath) // Read file into Buffer
46+
const asset = {
47+
upload: fileBuffer, // Buffer upload
48+
filename: 'customUpload.html', // Ensure filename is provided
49+
content_type: 'text/html', // Set content type
50+
title: 'buffer-asset',
51+
description: 'Buffer Asset Desc',
52+
tags: ['Buffer']
53+
}
54+
makeAsset().create(asset)
55+
.then((asset) => {
56+
jsonWrite(asset, 'bufferAsset.json')
57+
expect(asset.uid).to.be.not.equal(null)
58+
expect(asset.url).to.be.not.equal(null)
59+
expect(asset.filename).to.be.equal('customUpload.html')
60+
expect(asset.title).to.be.equal('buffer-asset')
61+
expect(asset.description).to.be.equal('Buffer Asset Desc')
62+
expect(asset.content_type).to.be.equal('text/html')
63+
done()
64+
})
65+
.catch(done)
66+
})
67+
4268
it('should download asset from URL.', done => {
4369
makeAsset().download({ url: assetURL, responseType: 'stream' })
4470
.then((response) => {

test/unit/asset-test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fs from 'fs'
12
import path from 'path'
23
import Axios from 'axios'
34
import { expect } from 'chai'
@@ -171,6 +172,49 @@ describe('Contentstack Asset test', () => {
171172
.catch(done)
172173
})
173174

175+
it('should upload asset from buffer', (done) => {
176+
const mock = new MockAdapter(Axios)
177+
mock.onPost('/assets').reply(200, {
178+
asset: {
179+
uid: 'mock-uid',
180+
url: '/assets',
181+
filename: 'customUpload.html',
182+
title: 'buffer-asset',
183+
description: 'Buffer Asset Desc',
184+
content_type: 'text/html',
185+
tags: ['Buffer'],
186+
parent_uid: 'UID'
187+
}
188+
})
189+
const filePath = path.join(__dirname, '../api/mock/customUpload.html')
190+
const fileBuffer = fs.readFileSync(filePath)
191+
const assetUpload = {
192+
upload: fileBuffer, // Buffer upload
193+
filename: 'customUpload.html', // Filename to identify the file
194+
content_type: 'text/html', // MIME type
195+
title: 'buffer-asset',
196+
description: 'Buffer Asset Desc',
197+
tags: ['Buffer'],
198+
parent_uid: 'UID'
199+
}
200+
const form = createFormData(assetUpload)() // Create FormData for Buffer upload
201+
const boundary = form.getBoundary()
202+
expect(boundary).to.be.equal(form.getBoundary())
203+
expect(boundary.length).to.be.greaterThan(30)
204+
makeAsset()
205+
.create(assetUpload)
206+
.then((asset) => {
207+
expect(asset.uid).to.be.equal('mock-uid')
208+
expect(asset.filename).to.be.equal('customUpload.html')
209+
expect(asset.title).to.be.equal('buffer-asset')
210+
expect(asset.description).to.be.equal('Buffer Asset Desc')
211+
expect(asset.content_type).to.be.equal('text/html')
212+
expect(asset.tags).to.include('Buffer')
213+
done()
214+
})
215+
.catch(done)
216+
})
217+
174218
it('Asset replace test', done => {
175219
var mock = new MockAdapter(Axios)
176220
mock.onPut('/assets/UID').reply(200, {

0 commit comments

Comments
 (0)