diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 1acd8ce..f8f8033 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -61,3 +61,10 @@ jobs:
method: 'POST'
data: '{ "key": "value" }'
files: '{ "file": "${{ github.workspace }}/testfile.txt" }'
+
+ - name: Request Postman Echo POST single file
+ uses: ./
+ with:
+ url: 'https://postman-echo.com/post'
+ method: 'POST'
+ file: "${{ github.workspace }}/testfile.txt"
diff --git a/README.md b/README.md
index fa3acb2..14fae05 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@ jobs:
|contentType | Request ContentType| application/json |
|data | Request Body Content:
- text content like JSON or XML
- key=value pairs separated by '&' and contentType: application/x-www-form-urlencoded
only for POST / PUT / PATCH Requests | '{}' |
|files | Map of key / absolute file paths send as multipart/form-data request to the API, if set the contentType is set to multipart/form-data, values provided by data will be added as additional FormData values, nested objects are not supported. **Example provided in the _test_ Workflow of this Action** | '{}' |
+|file | Single absolute file path send as `application/octet-stream` request to the API, if set the contentType is set to `application/octet-stream`. This input will be ignored if either `data` or `files` input is present. **Example provided in the _test_ Workflow of this Action** ||
|timeout| Request Timeout in ms | 5000 (5s) |
|username| Username for Basic Auth ||
|password| Password for Basic Auth ||
diff --git a/action.yml b/action.yml
index 91e4f0e..2a8bf95 100644
--- a/action.yml
+++ b/action.yml
@@ -19,6 +19,9 @@ inputs:
description: 'Map of absolute file paths as JSON String'
required: false
default: '{}'
+ file:
+ description: 'A single absolute file path'
+ required: false
username:
description: 'Auth Username'
required: false
diff --git a/dist/index.js b/dist/index.js
index 028e115..fabeafc 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1909,6 +1909,7 @@ const METHOD_POST = 'POST'
* @param {{ baseURL: string; timeout: number; headers: { [name: string]: string } }} param0.instanceConfig
* @param {string} param0.data Request Body as string, default {}
* @param {string} param0.files Map of Request Files (name: absolute path) as JSON String, default: {}
+ * @param {string} param0.file Single request file (absolute path)
* @param {{ username: string; password: string }|undefined} param0.auth Optional HTTP Basic Auth
* @param {*} param0.actions
* @param {number[]} param0.ignoredCodes Prevent Action to fail if the API response with one of this StatusCodes
@@ -1917,7 +1918,7 @@ const METHOD_POST = 'POST'
*
* @returns {void}
*/
-const request = async({ method, instanceConfig, data, files, auth, actions, ignoredCodes, preventFailureOnNoResponse, escapeData }) => {
+const request = async({ method, instanceConfig, data, files, file, auth, actions, ignoredCodes, preventFailureOnNoResponse, escapeData }) => {
try {
if (escapeData) {
data = data.replace(/"[^"]*"/g, (match) => {
@@ -1930,8 +1931,8 @@ const request = async({ method, instanceConfig, data, files, auth, actions, igno
}
if (files && files !== '{}') {
- filesJson = convertToJSON(files)
- dataJson = convertToJSON(data)
+ let filesJson = convertToJSON(files)
+ let dataJson = convertToJSON(data)
if (Object.keys(filesJson).length > 0) {
try {
@@ -1944,6 +1945,12 @@ const request = async({ method, instanceConfig, data, files, auth, actions, igno
}
}
+ // Only consider file if neither data nor files provided
+ if ((!data || data === '{}') && (!files || files === '{}') && file) {
+ data = fs.createReadStream(file)
+ updateConfigForFile(instanceConfig, file, actions)
+ }
+
const requestData = {
auth,
method,
@@ -2041,6 +2048,30 @@ const updateConfig = async (instanceConfig, formData, actions) => {
}
}
+/**
+ * @param instanceConfig
+ * @param filePath
+ * @param {*} actions
+ *
+ * @returns {{ baseURL: string; timeout: number; headers: { [name: string]: string } }}
+ */
+const updateConfigForFile = (instanceConfig, filePath, actions) => {
+ try {
+ const { size } = fs.statSync(filePath)
+
+ return {
+ ...instanceConfig,
+ headers: {
+ ...instanceConfig.headers,
+ 'Content-Length': size,
+ 'Content-Type': 'application/octet-stream'
+ }
+ }
+ } catch(error) {
+ actions.setFailed({ message: `Unable to read Content-Length: ${error.message}`, data, files })
+ }
+}
+
/**
* @param {FormData} formData
*
@@ -4898,6 +4929,7 @@ const instanceConfig = {
const data = core.getInput('data') || '{}';
const files = core.getInput('files') || '{}';
+const file = core.getInput('file')
const method = core.getInput('method') || METHOD_POST;
const preventFailureOnNoResponse = core.getInput('preventFailureOnNoResponse') === 'true';
const escapeData = core.getInput('escapeData') === 'true';
@@ -4909,7 +4941,7 @@ if (typeof ignoreStatusCodes === 'string' && ignoreStatusCodes.length > 0) {
ignoredCodes = ignoreStatusCodes.split(',').map(statusCode => parseInt(statusCode.trim()))
}
-request({ data, method, instanceConfig, auth, preventFailureOnNoResponse, escapeData, files, ignoredCodes, actions: new GithubActions() })
+request({ data, method, instanceConfig, auth, preventFailureOnNoResponse, escapeData, files, file, ignoredCodes, actions: new GithubActions() })
/***/ }),
diff --git a/package-lock.json b/package-lock.json
index 2932195..579f103 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,11 @@
{
"name": "http-request-action",
- "version": "1.8.0",
+ "version": "1.9.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
- "name": "http-request-action",
- "version": "1.8.0",
+ "version": "1.9.0",
"license": "MIT",
"dependencies": {
"@zeit/ncc": "^0.22",
diff --git a/package.json b/package.json
index 85b025d..427edef 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "http-request-action",
- "version": "1.8.2",
+ "version": "1.9.0",
"description": "",
"main": "src/index.js",
"private": false,
diff --git a/src/httpClient.js b/src/httpClient.js
index e35ee9d..c167ab6 100644
--- a/src/httpClient.js
+++ b/src/httpClient.js
@@ -11,6 +11,7 @@ const METHOD_POST = 'POST'
* @param {{ baseURL: string; timeout: number; headers: { [name: string]: string } }} param0.instanceConfig
* @param {string} param0.data Request Body as string, default {}
* @param {string} param0.files Map of Request Files (name: absolute path) as JSON String, default: {}
+ * @param {string} param0.file Single request file (absolute path)
* @param {{ username: string; password: string }|undefined} param0.auth Optional HTTP Basic Auth
* @param {*} param0.actions
* @param {number[]} param0.ignoredCodes Prevent Action to fail if the API response with one of this StatusCodes
@@ -19,7 +20,7 @@ const METHOD_POST = 'POST'
*
* @returns {void}
*/
-const request = async({ method, instanceConfig, data, files, auth, actions, ignoredCodes, preventFailureOnNoResponse, escapeData }) => {
+const request = async({ method, instanceConfig, data, files, file, auth, actions, ignoredCodes, preventFailureOnNoResponse, escapeData }) => {
try {
if (escapeData) {
data = data.replace(/"[^"]*"/g, (match) => {
@@ -32,8 +33,8 @@ const request = async({ method, instanceConfig, data, files, auth, actions, igno
}
if (files && files !== '{}') {
- filesJson = convertToJSON(files)
- dataJson = convertToJSON(data)
+ let filesJson = convertToJSON(files)
+ let dataJson = convertToJSON(data)
if (Object.keys(filesJson).length > 0) {
try {
@@ -46,6 +47,12 @@ const request = async({ method, instanceConfig, data, files, auth, actions, igno
}
}
+ // Only consider file if neither data nor files provided
+ if ((!data || data === '{}') && (!files || files === '{}') && file) {
+ data = fs.createReadStream(file)
+ updateConfigForFile(instanceConfig, file, actions)
+ }
+
const requestData = {
auth,
method,
@@ -143,6 +150,30 @@ const updateConfig = async (instanceConfig, formData, actions) => {
}
}
+/**
+ * @param instanceConfig
+ * @param filePath
+ * @param {*} actions
+ *
+ * @returns {{ baseURL: string; timeout: number; headers: { [name: string]: string } }}
+ */
+const updateConfigForFile = (instanceConfig, filePath, actions) => {
+ try {
+ const { size } = fs.statSync(filePath)
+
+ return {
+ ...instanceConfig,
+ headers: {
+ ...instanceConfig.headers,
+ 'Content-Length': size,
+ 'Content-Type': 'application/octet-stream'
+ }
+ }
+ } catch(error) {
+ actions.setFailed({ message: `Unable to read Content-Length: ${error.message}`, data, files })
+ }
+}
+
/**
* @param {FormData} formData
*
diff --git a/src/index.js b/src/index.js
index e119a8b..4b665b7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -36,6 +36,7 @@ const instanceConfig = {
const data = core.getInput('data') || '{}';
const files = core.getInput('files') || '{}';
+const file = core.getInput('file')
const method = core.getInput('method') || METHOD_POST;
const preventFailureOnNoResponse = core.getInput('preventFailureOnNoResponse') === 'true';
const escapeData = core.getInput('escapeData') === 'true';
@@ -47,4 +48,4 @@ if (typeof ignoreStatusCodes === 'string' && ignoreStatusCodes.length > 0) {
ignoredCodes = ignoreStatusCodes.split(',').map(statusCode => parseInt(statusCode.trim()))
}
-request({ data, method, instanceConfig, auth, preventFailureOnNoResponse, escapeData, files, ignoredCodes, actions: new GithubActions() })
+request({ data, method, instanceConfig, auth, preventFailureOnNoResponse, escapeData, files, file, ignoredCodes, actions: new GithubActions() })