Skip to content

Commit

Permalink
support env vars in http headers (#146)
Browse files Browse the repository at this point in the history
  • Loading branch information
volkc committed Dec 18, 2020
1 parent b755d31 commit 7cf6ead
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 3 deletions.
45 changes: 42 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,41 @@ const LinkCheckResult = require('link-check').LinkCheckResult;
const markdownLinkExtractor = require('markdown-link-extractor');
const ProgressBar = require('progress');

const envVarPatternMatcher = /(?<pattern>{{env\.(?<name>[a-zA-Z0-9\-_]+)}})/;

/*
* Performs some special replacements for the following patterns:
* - {{BASEURL}} - to be replaced with opts.projectBaseUrl
* - {{env.<env_var_name>}} - to be replaced with the environment variable specified with <env_var_name>
*/
function performSpecialReplacements(str, opts) {
// replace the `{{BASEURL}}` with the opts.projectBaseUrl. Helpful to build absolute urls "relative" to project roots
str = str.replace('{{BASEURL}}', opts.projectBaseUrl);

// replace {{env.<env_var_name>}} with the corresponding environment variable or an empty string if none is set.
var envVarMatch;
do {
envVarMatch = envVarPatternMatcher.exec(str);

if(!envVarMatch) {
break;
}

var envVarPattern = envVarMatch.groups.pattern;
var envVarName = envVarMatch.groups.name;

var envVarPatternReplacement = '';

if(envVarName in process.env) {
envVarPatternReplacement = process.env[envVarName];
}

str = str.replace(envVarPattern, envVarPatternReplacement);
} while (true);

return str;
}

module.exports = function markdownLinkCheck(markdown, opts, callback) {
if (arguments.length === 2 && typeof opts === 'function') {
// optional 'opts' not supplied.
Expand Down Expand Up @@ -51,9 +86,7 @@ module.exports = function markdownLinkCheck(markdown, opts, callback) {
if (opts.replacementPatterns) {
for (let replacementPattern of opts.replacementPatterns) {
let pattern = replacementPattern.pattern instanceof RegExp ? replacementPattern.pattern : new RegExp(replacementPattern.pattern);
// replace the `{{BASEURL}}` with the opts.projectBaseUrl. Helpful to build absolute urls "relative" to project roots
const replacement = replacementPattern.replacement.replace('{{BASEURL}}', opts.projectBaseUrl);
link = link.replace(pattern, replacement);
link = link.replace(pattern, performSpecialReplacements(replacementPattern.replacement, opts));
}
}

Expand All @@ -62,6 +95,12 @@ module.exports = function markdownLinkCheck(markdown, opts, callback) {

if (opts.httpHeaders) {
for (const httpHeader of opts.httpHeaders) {
if (httpHeader.headers) {
for (const header of Object.keys(httpHeader.headers)) {
httpHeader.headers[header] = performSpecialReplacements(httpHeader.headers[header], opts);
}
}

for (const url of httpHeader.urls) {
if (link.startsWith(url)) {
Object.assign(opts.headers, httpHeader.headers);
Expand Down
90 changes: 90 additions & 0 deletions test/markdown-link-check.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,4 +249,94 @@ describe('markdown-link-check', function () {
done();
});
});

it('should enrich http headers with environment variables', function (done) {
process.env.BASIC_AUTH_TOKEN = 'Zm9vOmJhcg==';
markdownLinkCheck(
fs.readFileSync(path.join(__dirname, 'sample.md')).toString().replace(/%%BASE_URL%%/g, baseUrl),
{
baseUrl: baseUrl,
httpHeaders: [
{
urls: [baseUrl + '/basic-auth'],
headers: { 'Authorization': 'Basic {{env.BASIC_AUTH_TOKEN}}', 'Foo': 'Bar' }
}
],
"aliveStatusCodes":[200, 206],
"retryOn429":true,
"retryCount": MAX_RETRY_COUNT,
"fallbackRetryDelay": "500ms"
}, function (err, results) {
expect(err).to.be(null);
done();
});
});

it('should enrich pattern replacement strings with environment variables', function (done) {
process.env.WORKSPACE = 'file://' + __dirname + '/..';
markdownLinkCheck(fs.readFileSync(path.join(__dirname, 'local-file.md')).toString().replace(/%%BASE_URL%%/g, 'file://' + __dirname), {baseUrl: 'file://' + __dirname, projectBaseUrl: 'file://' + __dirname + "/..",replacementPatterns: [{ pattern: '^/', replacement: "{{env.WORKSPACE}}/"}]}, function (err, results) {
expect(err).to.be(null);
expect(results).to.be.an('array');

const expected = [
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 400, status: 'dead' },
{ statusCode: 400, status: 'dead' },
];

expect(results.length).to.be(expected.length);

for (let i = 0; i < results.length; i++) {
expect(results[i].statusCode).to.be(expected[i].statusCode);
expect(results[i].status).to.be(expected[i].status);
}

done();
});
});


it('should correctly resolve special replacement patterns', function (done) {
process.env.MixedCase = 'hello.jpg';
process.env.UPPERCASE = 'hello.jpg';
process.env.lowercase = 'hello.jpg';
process.env['WITH-Special_Characters-123'] = 'hello.jpg';

markdownLinkCheck(fs.readFileSync(path.join(__dirname, 'special-replacements.md')).toString().replace(/%%BASE_URL%%/g, 'file://' + __dirname), {baseUrl: 'file://' + __dirname, projectBaseUrl: 'file://' + __dirname + "/..",replacementPatterns: [
{pattern: '^/', replacement: "{{BASEURL}}/"},
{pattern: '%%ENVVAR_MIXEDCASE_TEST%%', replacement: "{{env.MixedCase}}"},
{pattern: '%%ENVVAR_UPPERCASE_TEST%%', replacement: "{{env.UPPERCASE}}"},
{pattern: '%%ENVVAR_LOWERCASE_TEST%%', replacement: "{{env.lowercase}}"},
{pattern: '%%ENVVAR_WITHSPECIALCHARACTERS_TEST%%', replacement: "{{env.WITH-Special_Characters-123}}"},
{pattern: '%%ENVVAR_NONEXISTENT_TEST%%', replacement: "{{env.ThisIsSomethingThatHopefullyDoesntExist}}"}
]}, function (err, results) {
expect(err).to.be(null);
expect(results).to.be.an('array');

const expected = [
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' },
{ statusCode: 200, status: 'alive' }
];

expect(results.length).to.be(expected.length);

for (let i = 0; i < results.length; i++) {
expect(results[i].statusCode).to.be(expected[i].statusCode);
expect(results[i].status).to.be(expected[i].status);
}

done();
});
});
});
18 changes: 18 additions & 0 deletions test/special-replacements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Sample

This is a test file leveraging special replacement patterns:

![img](/test/%%ENVVAR_MIXEDCASE_TEST%%) (alive)
![img](%%ENVVAR_MIXEDCASE_TEST%%) (alive)

![img](/test/%%ENVVAR_UPPERCASE_TEST%%) (alive)
![img](%%ENVVAR_UPPERCASE_TEST%%) (alive)

![img](/test/%%ENVVAR_LOWERCASE_TEST%%) (alive)
![img](%%ENVVAR_LOWERCASE_TEST%%) (alive)

![img](/test/%%ENVVAR_WITHSPECIALCHARACTERS_TEST%%) (alive)
![img](%%ENVVAR_WITHSPECIALCHARACTERS_TEST%%) (alive)

![img](/test/%%ENVVAR_NONEXISTENT_TEST%%hello.jpg) (alive)
![img](%%ENVVAR_NONEXISTENT_TEST%%hello.jpg) (alive)

0 comments on commit 7cf6ead

Please sign in to comment.