Allow the escaping of variables.

This results in two important changes:

- Made changes so that we actually use the replacement regex groups.
This was supposed to happen from the start but I guess I didn't notice.
By fixing this the code for finding dependencies and the cache#parse is
now a bit more reliable.

- This also changes how we store variables in the cache (which I've been
meaning to do for a while.) removing the dollar sign that usually
preceeded all cached requests in Beau. It's all more sensible now.

There's more refactoring left to do.
This commit is contained in:
David Diaz 2018-04-11 15:49:05 -06:00
parent e9c07cbbf9
commit f13cbda84a
12 changed files with 74 additions and 48 deletions

View File

@ -97,7 +97,7 @@ Beau {
], ],
"cache": RequestCache { "cache": RequestCache {
"$cache": Object { "$cache": Object {
"$env": Object {}, "env": Object {},
}, },
}, },
"config": Config { "config": Config {

View File

@ -18,3 +18,26 @@ Object {
}, },
} }
`; `;
exports[`RequestList should fetch dependencies 1`] = `
RequestCache {
"$cache": Object {
"env": Object {
"environmental": true,
},
"get-posts": Object {
"body": "{\\"hello\\": \\"world\\"}",
"request": Object {
"body": undefined,
"endpoint": "http://martianwabbit.com/post",
"headers": undefined,
},
"response": Object {
"body": "{\\"hello\\": \\"world\\"}",
"headers": Array [],
"status": 200,
},
},
},
}
`;

View File

@ -74,7 +74,7 @@ describe(`Beau's plugin system`, () => {
it(`should look for dynamic values executing and replacing them`, async () => { it(`should look for dynamic values executing and replacing them`, async () => {
let cache = new RequestCache(); let cache = new RequestCache();
cache.add('$value2', '2'); cache.add('value2', '2');
let req = await request.exec(cache); let req = await request.exec(cache);

View File

@ -32,8 +32,8 @@ describe('Request', () => {
}; };
cache = new RequestCache(); cache = new RequestCache();
cache.add('$session', { token: 'abc123' }); cache.add('session', { token: 'abc123' });
cache.add('$profile', { UserId: 14 }); cache.add('profile', { UserId: 14 });
request = new Request(validRequestConfig); request = new Request(validRequestConfig);
requestWithoutDependencies = new Request({ requestWithoutDependencies = new Request({

View File

@ -6,11 +6,11 @@ describe('Request Cache', () => {
beforeEach(() => { beforeEach(() => {
cache = new RequestCache(); cache = new RequestCache();
cache.add('$session', { cache.add('session', {
hello: 'World' hello: 'World'
}); });
cache.add('$array', [ cache.add('array', [
{ {
id: 1, id: 1,
name: 'Sergio' name: 'Sergio'
@ -23,12 +23,12 @@ describe('Request Cache', () => {
}); });
it('should add keys to the cache', () => { it('should add keys to the cache', () => {
expect(cache.$cache.$session.hello).toBe('World'); expect(cache.$cache.session.hello).toBe('World');
}); });
describe('get', () => { describe('get', () => {
it('should be able to find key values with a given path', () => { it('should be able to find key values with a given path', () => {
expect(cache.get('$session.hello')).toBe('World'); expect(cache.get('session.hello')).toBe('World');
}); });
it('should throw when given an invalid path', () => { it('should throw when given an invalid path', () => {
@ -79,5 +79,11 @@ describe('Request Cache', () => {
it('should return null when passed null', () => { it('should return null when passed null', () => {
expect(cache.parse(null)).toBe(null); expect(cache.parse(null)).toBe(null);
}); });
it(`shouldn't replace escaped variables`, () => {
expect(cache.parse(`\\$session.hello is $session.hello`)).toBe(
`$session.hello is World`
);
});
}); });
}); });

View File

@ -39,8 +39,10 @@ describe('RequestList', () => {
expect(requests.list.length).toBe(2); expect(requests.list.length).toBe(2);
}); });
it('should fetch dependencies', () => { it('should fetch dependencies', async () => {
requests.fetchDependencies(['get-posts']); await expect(
requests.fetchDependencies(['get-posts'])
).resolves.toMatchSnapshot();
}); });
it('should execute requests by alias.', async () => { it('should execute requests by alias.', async () => {
@ -54,7 +56,7 @@ describe('RequestList', () => {
it('should return a cached result if available', async () => { it('should return a cached result if available', async () => {
const obj = { test: true }; const obj = { test: true };
requests.cache.add('$test', obj); requests.cache.add('test', obj);
await expect(requests.execByAlias('test')).resolves.toBe(obj); await expect(requests.execByAlias('test')).resolves.toBe(obj);
}); });

View File

@ -64,7 +64,7 @@ class Config {
let requestDefinitionIsString = typeof host[key] === 'string'; let requestDefinitionIsString = typeof host[key] === 'string';
let originalRequest = requestDefinitionIsString let originalRequest = requestDefinitionIsString
? { ALIAS: host[key] } ? { ALIAS: host[key] }
: deepMerge.all([host[key]]); : host[key];
let request = UpperCaseKeys(originalRequest); let request = UpperCaseKeys(originalRequest);

View File

@ -27,11 +27,11 @@ class Plugins {
throw new Error(`Plugin items should contain only one key.`); throw new Error(`Plugin items should contain only one key.`);
} }
name = Object.keys(plugin)[0]; name = keys[0];
settings = plugin[name]; settings = plugin[name];
} }
plugin = requireg(`./beau-${toKebabCase(name)}`); plugin = requireg(`beau-${toKebabCase(name)}`);
new plugin(this, settings); new plugin(this, settings);
} }
@ -45,22 +45,6 @@ class Plugins {
return result; return result;
} }
execPreRequestModifiers(request, originalRequest) {
return this.executeModifier(
'preRequestModifiers',
request,
originalRequest
);
}
execPostRequestModifiers(response, originalRequest) {
return this.executeModifier(
'postRequestModifiers',
response,
originalRequest
);
}
replaceDynamicValues(obj) { replaceDynamicValues(obj) {
return replaceInObject(obj, val => { return replaceInObject(obj, val => {
try { try {

View File

@ -62,8 +62,12 @@ class Request {
set = this.findDependencies(request[key], set); set = this.findDependencies(request[key], set);
}); });
} else if (type === 'string') { } else if (type === 'string') {
const matches = request.match(replacementRegex) || []; const matches = [];
const deps = matches.map(m => m.split('.')[0].substring(1)); request.replace(
replacementRegex,
(match, g1) => !match.startsWith('\\') && matches.push(g1)
);
const deps = matches.map(m => m.split('.')[0]);
return new Set([...set, ...deps]); return new Set([...set, ...deps]);
} }
@ -82,7 +86,8 @@ class Request {
settings = this.plugins.replaceDynamicValues(settings); settings = this.plugins.replaceDynamicValues(settings);
settings = this.plugins.execPreRequestModifiers( settings = this.plugins.executeModifier(
'preRequestModifiers',
settings, settings,
this.originalRequest this.originalRequest
); );
@ -120,12 +125,13 @@ class Request {
body: response.body body: response.body
}; };
results = this.plugins.execPostRequestModifiers( results = this.plugins.executeModifier(
'postRequestModifiers',
results, results,
this.originalRequest this.originalRequest
); );
cache.add(`$${this.ALIAS}`, results); cache.add(this.ALIAS, results);
return results; return results;
} catch ({ error }) { } catch ({ error }) {

View File

@ -16,13 +16,13 @@ class RequestCache {
get(path) { get(path) {
let result = this.$cache; let result = this.$cache;
path.split('.').forEach(part => { path.split('.').forEach(part => {
result = result[part]; if (result[part] === undefined) {
});
if (typeof result === 'undefined') {
throw new Error(`${path} not found in cache: `, path); throw new Error(`${path} not found in cache: `, path);
} }
result = result[part];
});
return result; return result;
} }
@ -31,9 +31,15 @@ class RequestCache {
return null; return null;
} }
return replaceInObject(item, item => return replaceInObject(item, item => {
item.replace(replacementRegex, key => this.get(key)) return item.replace(replacementRegex, (match, key) => {
); if (match.startsWith('\\')) {
return match.replace('\\$', '$');
}
return this.get(key);
});
});
} }
} }

View File

@ -10,12 +10,12 @@ class RequestList {
this.list = this.loadRequests(); this.list = this.loadRequests();
this.cache = new RequestCache(); this.cache = new RequestCache();
this.cache.add(`$env`, this.config.ENVIRONMENT); this.cache.add(`env`, this.config.ENVIRONMENT);
} }
async execByAlias(alias) { async execByAlias(alias) {
if (this.cache.exists(`$${alias}`)) { if (this.cache.exists(alias)) {
return this.cache.get(`$${alias}`); return this.cache.get(alias);
} }
const request = this.list.find(r => r.ALIAS === alias); const request = this.list.find(r => r.ALIAS === alias);
@ -26,8 +26,7 @@ class RequestList {
try { try {
await this.fetchDependencies(Array.from(request.DEPENDENCIES)); await this.fetchDependencies(Array.from(request.DEPENDENCIES));
const response = await request.exec(this.cache); return await request.exec(this.cache);
return response;
} catch (reason) { } catch (reason) {
throw new Error( throw new Error(
`Request: ${request.VERB} ${ `Request: ${request.VERB} ${

View File

@ -11,7 +11,7 @@ const httpVerbs = [
]; ];
const requestRegex = new RegExp(`(${httpVerbs.join('|')})\\s(.*)`, 'i'); const requestRegex = new RegExp(`(${httpVerbs.join('|')})\\s(.*)`, 'i');
const replacementRegex = /\$([a-zA-Z\.\d\-\_\/\\\:]+)/g; const replacementRegex = /(?:\\?)\$([a-zA-Z\.\d\-\_\/\\\:]+)/g;
const dynamicValueRegex = /\$\[(\w+\((?:.|[\n\r])+?\))\]/g; const dynamicValueRegex = /\$\[(\w+\((?:.|[\n\r])+?\))\]/g;
const UpperCaseKeys = function(obj) { const UpperCaseKeys = function(obj) {