Skip to content

Commit 416fca1

Browse files
authored
Merge pull request #55 from githubocto/mr/ia/expose-axios-config
feat: expose axios_config
2 parents 77ea764 + 40dfcf4 commit 416fca1

File tree

7 files changed

+110
-37
lines changed

7 files changed

+110
-37
lines changed

README.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,29 @@ For example, if this field is set to `Bearer abc123` then the following header i
101101
}
102102
```
103103

104-
And in the action yaml:
105-
`authorization: 'Bearer abc123'`
104+
#### `axios_config` (optional)
105+
106+
Under the hood, the `http` backend uses [Axios](https://github.com/axios/axios) for data fetching. By default, Flat assumes you're interested in using the `GET` method to fetch data, but if you'd like to `POST` (e.g., sending a GraphQL query), the `axios_config` option allows you to override this behavior.
107+
108+
Specifically, the `axios_config` parameter should reflect a relative path to a `.json` file in your repository. This JSON file should mirror the shape of [Axios' request config parameters](https://github.com/axios/axios#request-config), with a few notable exceptions.
109+
110+
- `url` and `baseURL` will both be ignored, as the `http_url` specified above will take precedence.
111+
- `headers` will be merged in with the authorization header described by the `authorization` parameter above. Please do not put secret keys here, as they will be stored in plain text!
112+
- All `function` parameters will be ignored (e.g., `transformRequest`).
113+
- The response type is always set to `responseType: 'stream'` in the background.
114+
115+
An example `axios_config` might look thusly if you were interested in hitting GitHub's GraphQL API ([here is a demo](https://github.com/githubocto/flat-demo-graphql)) 👇
116+
117+
```json
118+
{
119+
"method": "post",
120+
"data": {
121+
"query": "query { repository(owner:\"octocat\", name:\"Hello-World\") { issues(last:20, states:CLOSED) { edges { node { title url labels(first:5) { edges { node { name } } } } } } } }"
122+
}
123+
}
124+
```
125+
126+
We advise escaping double quotes like `\"` in your JSON file.
106127

107128
#### `downloaded_filename`
108129

action.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ name: 'Flat Data'
22
description: 'The GitHub action which powers data fetching for Flat'
33
author: 'GitHub OCTO'
44
inputs:
5+
axios_config:
6+
description: 'A path (relative to the root of your repo) of a JSON file containing a subset of Axios configuration'
7+
required: false
58
downloaded_filename:
69
description: 'The filename to use for writing data.'
710
required: false

dist/index.js

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,36 @@ async function fetchHTTP(config) {
3737
core.info('Fetching: HTTP');
3838
// Authorization headers
3939
const auth = {
40-
headers: {
41-
authorization: config.authorization,
42-
}
40+
authorization: config.authorization,
4341
};
44-
const headers = config.authorization ? auth : {};
42+
const authHeader = config.authorization ? auth : {};
43+
let response;
4544
try {
46-
const response = await axios_1.default.get(config.http_url, {
47-
method: 'get',
48-
responseType: 'stream',
49-
...headers,
50-
});
45+
if (config.axios_config) {
46+
const axiosConfig = fs_1.default.readFileSync(config.axios_config, {
47+
encoding: 'utf8',
48+
});
49+
const parsed = JSON.parse(axiosConfig);
50+
const combinedWithOtherConfigValues = {
51+
...parsed,
52+
url: config.http_url,
53+
headers: {
54+
...parsed.headers,
55+
...authHeader,
56+
},
57+
responseType: 'stream',
58+
};
59+
response = await axios_1.default(combinedWithOtherConfigValues);
60+
}
61+
else {
62+
response = await axios_1.default.get(config.http_url, {
63+
method: 'get',
64+
responseType: 'stream',
65+
headers: {
66+
...authHeader,
67+
},
68+
});
69+
}
5170
const filename = config.downloaded_filename;
5271
const writer = fs_1.default.createWriteStream(filename);
5372
response.data.pipe(writer);
@@ -249,6 +268,7 @@ const CommonConfigSchema = z.object({
249268
});
250269
const HTTPConfigSchema = z
251270
.object({
271+
axios_config: z.string().optional(),
252272
http_url: z.string(),
253273
authorization: z.string().optional(),
254274
mask: z.string().optional(), // string array of secrets or boolean
@@ -265,6 +285,7 @@ const ConfigSchema = z.union([HTTPConfigSchema, SQLConfigSchema]);
265285
function getConfig() {
266286
const raw = {};
267287
const keys = [
288+
'axios_config',
268289
'downloaded_filename',
269290
'http_url',
270291
'authorization',
@@ -480,15 +501,16 @@ async function run() {
480501
// if including a mask config then we can strip out secrets from the http_url
481502
sourceMasked = source; // if no secrets to mask then this is just source
482503
if (config.mask) {
483-
if (config.mask === 'true' || config.mask === 'false') { // mask param is a string
504+
if (config.mask === 'true' || config.mask === 'false') {
505+
// mask param is a string
484506
shouldMask = JSON.parse(config.mask); // convert to boolean
485507
}
486508
else {
487509
try {
488510
const maskArray = JSON.parse(config.mask);
489511
maskArray.forEach((secretToMask) => {
490-
const regex = new RegExp(secretToMask, "g");
491-
sourceMasked = sourceMasked.replace(regex, "***");
512+
const regex = new RegExp(secretToMask, 'g');
513+
sourceMasked = sourceMasked.replace(regex, '***');
492514
});
493515
}
494516
catch (error) {
@@ -513,7 +535,7 @@ async function run() {
513535
core.debug(`Invoking ${config.postprocess} with ${filename}...`);
514536
try {
515537
const raw = child_process_1.execSync(`NO_COLOR=true deno run -q --allow-read --allow-write --allow-run --allow-net --allow-env --unstable ${config.postprocess} ${filename}`).toString();
516-
core.info("Deno output:");
538+
core.info('Deno output:');
517539
core.info(raw);
518540
}
519541
catch (error) {

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/backends/http.ts

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,50 @@
11
import * as core from '@actions/core'
22
import { HTTPConfig } from '../config'
33
import fs from 'fs'
4-
import axios from 'axios'
4+
import axios, { AxiosResponse } from 'axios'
55

66
export default async function fetchHTTP(config: HTTPConfig): Promise<string> {
77
core.info('Fetching: HTTP')
88

99
// Authorization headers
10-
const auth = {
11-
headers: {
12-
authorization: config.authorization,
13-
}
14-
};
15-
const headers = config.authorization ? auth : {}
10+
const auth = {
11+
authorization: config.authorization,
12+
}
13+
const authHeader = config.authorization ? auth : {}
14+
15+
let response: AxiosResponse<any>
1616

1717
try {
18-
const response = await axios.get(config.http_url, {
19-
method: 'get',
20-
responseType: 'stream',
21-
...headers,
22-
})
18+
if (config.axios_config) {
19+
const axiosConfig = fs.readFileSync(config.axios_config, {
20+
encoding: 'utf8',
21+
})
22+
23+
const parsed = JSON.parse(axiosConfig)
24+
25+
const combinedWithOtherConfigValues = {
26+
...parsed,
27+
url: config.http_url,
28+
headers: {
29+
...parsed.headers,
30+
...authHeader,
31+
},
32+
responseType: 'stream',
33+
}
34+
35+
response = await axios(combinedWithOtherConfigValues)
36+
} else {
37+
response = await axios.get(config.http_url, {
38+
method: 'get',
39+
responseType: 'stream',
40+
headers: {
41+
...authHeader,
42+
},
43+
})
44+
}
2345
const filename = config.downloaded_filename
2446
const writer = fs.createWriteStream(filename)
47+
2548
response.data.pipe(writer)
2649
await new Promise((resolve, reject) => {
2750
writer.on('finish', resolve)
@@ -32,4 +55,4 @@ export default async function fetchHTTP(config: HTTPConfig): Promise<string> {
3255
core.setFailed(error)
3356
throw error
3457
}
35-
}
58+
}

src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type CommonConfig = z.infer<typeof CommonConfigSchema>
1212

1313
const HTTPConfigSchema = z
1414
.object({
15+
axios_config: z.string().optional(),
1516
http_url: z.string(),
1617
authorization: z.string().optional(),
1718
mask: z.string().optional(), // string array of secrets or boolean
@@ -34,6 +35,7 @@ export type Config = z.infer<typeof ConfigSchema>
3435
export function getConfig(): Config {
3536
const raw: { [k: string]: string } = {}
3637
const keys = [
38+
'axios_config',
3739
'downloaded_filename',
3840
'http_url',
3941
'authorization',

src/main.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,20 @@ async function run(): Promise<void> {
3131
// if including a mask config then we can strip out secrets from the http_url
3232
sourceMasked = source // if no secrets to mask then this is just source
3333
if (config.mask) {
34-
if (config.mask === 'true' || config.mask === 'false') { // mask param is a string
34+
if (config.mask === 'true' || config.mask === 'false') {
35+
// mask param is a string
3536
shouldMask = JSON.parse(config.mask) // convert to boolean
3637
} else {
3738
try {
3839
const maskArray: string[] = JSON.parse(config.mask)
3940
maskArray.forEach((secretToMask: string) => {
40-
const regex = new RegExp(secretToMask, "g")
41-
sourceMasked = sourceMasked.replace(regex, "***")
41+
const regex = new RegExp(secretToMask, 'g')
42+
sourceMasked = sourceMasked.replace(regex, '***')
4243
})
43-
} catch(error) {
44-
core.setFailed('Mask param formatted incorrectly. It should be a string array OR a "true" or "false" string.')
44+
} catch (error) {
45+
core.setFailed(
46+
'Mask param formatted incorrectly. It should be a string array OR a "true" or "false" string.'
47+
)
4548
}
4649
}
4750
}
@@ -64,9 +67,8 @@ async function run(): Promise<void> {
6467
`NO_COLOR=true deno run -q --allow-read --allow-write --allow-run --allow-net --allow-env --unstable ${config.postprocess} ${filename}`
6568
).toString()
6669

67-
core.info("Deno output:")
70+
core.info('Deno output:')
6871
core.info(raw)
69-
7072
} catch (error) {
7173
core.setFailed(error)
7274
}
@@ -117,7 +119,7 @@ async function run(): Promise<void> {
117119

118120
const files = [...alreadyEditedFiles, ...editedFiles]
119121
core.exportVariable('FILES', files)
120-
122+
121123
core.endGroup()
122124
}
123125

0 commit comments

Comments
 (0)