mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 22:30:15 +00:00
Support for tag editor dynamic variable args (#512)
This commit is contained in:
parent
14bdea8b34
commit
de08895092
@ -27,10 +27,11 @@ describe('ResponseExtension General', async () => {
|
||||
});
|
||||
|
||||
it('fails on empty filter', async () => {
|
||||
await models.response.create({parentId: 'req_test'}, '{"foo": "bar"}');
|
||||
const request = await models.request.create({parentId: 'foo'});
|
||||
await models.response.create({parentId: request._id, statusCode: 200}, '{"foo": "bar"}');
|
||||
|
||||
try {
|
||||
await templating.render(`{% response "body", "req_test", "" %}`);
|
||||
await templating.render(`{% response "body", "${request._id}", "" %}`);
|
||||
fail('Should have failed');
|
||||
} catch (err) {
|
||||
expect(err.message).toContain('No body filter specified');
|
||||
|
@ -49,10 +49,6 @@ export default ({
|
||||
throw new Error('No request specified');
|
||||
}
|
||||
|
||||
if (field !== 'raw' && !filter) {
|
||||
throw new Error(`No ${field} filter specified`);
|
||||
}
|
||||
|
||||
const request = await context.util.models.request.getById(id);
|
||||
if (!request) {
|
||||
throw new Error(`Could not find request ${id}`);
|
||||
@ -65,7 +61,11 @@ export default ({
|
||||
}
|
||||
|
||||
if (!response.statusCode) {
|
||||
throw new Error('No responses for request');
|
||||
throw new Error('No successful responses for request');
|
||||
}
|
||||
|
||||
if (field !== 'raw' && !filter) {
|
||||
throw new Error(`No ${field} filter specified`);
|
||||
}
|
||||
|
||||
const sanitizedFilter = filter.trim();
|
||||
|
@ -3,6 +3,7 @@
|
||||
export type NunjucksParsedTagArg = {
|
||||
type: 'string' | 'number' | 'boolean' | 'number' | 'variable' | 'expression',
|
||||
value: string | number | boolean,
|
||||
forceVariable?: boolean,
|
||||
quotedBy?: '"' | "'"
|
||||
};
|
||||
|
||||
|
@ -67,13 +67,16 @@ class CookieList extends React.PureComponent<Props> {
|
||||
|
||||
return (
|
||||
<tr className="selectable" key={i}>
|
||||
<RenderedText render={handleRender} component="td">
|
||||
{cookie.domain}
|
||||
</RenderedText>
|
||||
<RenderedText render={handleRender} component="td"
|
||||
props={{className: 'force-wrap wide'}}>
|
||||
{cookieString}
|
||||
</RenderedText>
|
||||
<td>
|
||||
<RenderedText render={handleRender}>
|
||||
{cookie.domain}
|
||||
</RenderedText>
|
||||
</td>
|
||||
<td className="force-wrap wide">
|
||||
<RenderedText render={handleRender}>
|
||||
{cookieString}
|
||||
</RenderedText>
|
||||
</td>
|
||||
<td onClick={null} className="text-right no-wrap">
|
||||
<button className="btn btn--super-compact btn--outlined"
|
||||
onClick={e => handleShowModifyCookieModal(cookie)}
|
||||
|
@ -79,6 +79,7 @@ class NunjucksModal extends PureComponent {
|
||||
onChange={this._handleTemplateChange}
|
||||
defaultValue={defaultTemplate}
|
||||
handleRender={handleRender}
|
||||
handleGetRenderContext={handleGetRenderContext}
|
||||
workspace={workspace}
|
||||
/>
|
||||
);
|
||||
|
@ -2,10 +2,8 @@
|
||||
import * as React from 'react';
|
||||
|
||||
type Props = {
|
||||
component: string,
|
||||
children: string,
|
||||
render: Function,
|
||||
props?: Object
|
||||
render: Function
|
||||
};
|
||||
|
||||
type State = {
|
||||
@ -35,8 +33,7 @@ class RenderedText extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {component, props} = this.props;
|
||||
return React.createElement(component, props || {}, this.state.renderedText);
|
||||
return this.state.renderedText;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,11 @@ import {trackEvent} from '../../../analytics/index';
|
||||
import type {BaseModel} from '../../../models/index';
|
||||
import type {Workspace} from '../../../models/workspace';
|
||||
import type {PluginArgumentEnumOption} from '../../../templating/extensions/index';
|
||||
import {Dropdown, DropdownButton, DropdownDivider, DropdownItem} from '../base/dropdown/index';
|
||||
|
||||
type Props = {
|
||||
handleRender: Function,
|
||||
handleGetRenderContext: Function,
|
||||
defaultValue: string,
|
||||
onChange: Function,
|
||||
workspace: Workspace
|
||||
@ -30,7 +32,8 @@ type State = {
|
||||
allDocs: {[string]: Array<BaseModel>},
|
||||
rendering: boolean,
|
||||
preview: string,
|
||||
error: string
|
||||
error: string,
|
||||
variables: Array<{name: string, value: string}>
|
||||
};
|
||||
|
||||
@autobind
|
||||
@ -48,7 +51,8 @@ class TagEditor extends React.PureComponent<Props, State> {
|
||||
allDocs: {},
|
||||
rendering: true,
|
||||
preview: '',
|
||||
error: ''
|
||||
error: '',
|
||||
variables: []
|
||||
};
|
||||
}
|
||||
|
||||
@ -68,8 +72,15 @@ class TagEditor extends React.PureComponent<Props, State> {
|
||||
await this._update(tagDefinitions, activeTagDefinition, activeTagData, true);
|
||||
}
|
||||
|
||||
async loadVariables () {
|
||||
const context = await this.props.handleGetRenderContext();
|
||||
const variables = context.keys;
|
||||
this.setState({variables});
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.load();
|
||||
this.loadVariables();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps: Props) {
|
||||
@ -104,7 +115,11 @@ class TagEditor extends React.PureComponent<Props, State> {
|
||||
this.setState({allDocs, loadingDocs: false});
|
||||
}
|
||||
|
||||
_updateArg (argValue: string | number, argIndex: number) {
|
||||
_updateArg (
|
||||
argValue: string | number | boolean,
|
||||
argIndex: number,
|
||||
forceNewType: string | null = null
|
||||
) {
|
||||
const {tagDefinitions, activeTagData, activeTagDefinition} = this.state;
|
||||
|
||||
if (!activeTagData) {
|
||||
@ -112,10 +127,13 @@ class TagEditor extends React.PureComponent<Props, State> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!activeTagDefinition) {
|
||||
console.warn('No active tag definition to update', {state: this.state});
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure all arguments exist
|
||||
const defaultArgs = activeTagDefinition
|
||||
? this._getDefaultTagData(activeTagDefinition).args
|
||||
: [];
|
||||
const defaultArgs = this._getDefaultTagData(activeTagDefinition).args;
|
||||
for (let i = 0; i < defaultArgs.length; i++) {
|
||||
if (activeTagData.args[i]) {
|
||||
continue;
|
||||
@ -135,10 +153,42 @@ class TagEditor extends React.PureComponent<Props, State> {
|
||||
// Update it
|
||||
argData.value = argValue;
|
||||
|
||||
// Update type if we need to
|
||||
if (forceNewType) {
|
||||
// Ugh, what a hack (because it's enum)
|
||||
(argData: any).type = forceNewType;
|
||||
}
|
||||
|
||||
this._update(tagDefinitions, activeTagDefinition, tagData, false);
|
||||
}
|
||||
|
||||
_handleChange (e: SyntheticEvent<HTMLInputElement>) {
|
||||
async _handleChangeArgVariable (options: {argIndex: number, variable: boolean}) {
|
||||
const {variable, argIndex} = options;
|
||||
const {activeTagData, activeTagDefinition, variables} = this.state;
|
||||
|
||||
if (!activeTagData || !activeTagDefinition) {
|
||||
console.warn('Failed to change arg variable', {state: this.state});
|
||||
return;
|
||||
}
|
||||
|
||||
const argData = activeTagData.args[argIndex];
|
||||
const argDef = activeTagDefinition.args[argIndex];
|
||||
const existingValue = argData ? argData.value : '';
|
||||
|
||||
if (variable) {
|
||||
const variable = variables.find(v => v.value === existingValue);
|
||||
const firstVariable = variables.length ? variables[0].name : '';
|
||||
const value = variable ? variable.name : firstVariable;
|
||||
return this._updateArg(value || 'my_variable', argIndex, 'variable');
|
||||
} else {
|
||||
const initialType = argDef ? argDef.type : 'string';
|
||||
const variable = variables.find(v => v.name === existingValue);
|
||||
const value = variable ? variable.value : '';
|
||||
return this._updateArg(value, argIndex, initialType);
|
||||
}
|
||||
}
|
||||
|
||||
_handleChange (e: SyntheticEvent<HTMLInputElement>, forceVariable: boolean = false) {
|
||||
const parent = e.currentTarget.parentNode;
|
||||
let argIndex = -1;
|
||||
if (parent instanceof HTMLElement) {
|
||||
@ -250,6 +300,19 @@ class TagEditor extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
renderArgVariable (path: string) {
|
||||
const {variables} = this.state;
|
||||
return (
|
||||
<select type="text" defaultValue={path || ''} onChange={this._handleChange}>
|
||||
{variables.map((v, i) => (
|
||||
<option key={`${i}::${v.name}`} value={v.name}>
|
||||
{v.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
renderArgString (value: string, placeholder: string) {
|
||||
return (
|
||||
<input
|
||||
@ -355,9 +418,13 @@ class TagEditor extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
const value = argData.value.toString();
|
||||
const isVariable = argData.type === 'variable';
|
||||
const argInputVariable = isVariable ? this.renderArgVariable(value) : null;
|
||||
|
||||
let argInput;
|
||||
let isVariableAllowed = false;
|
||||
if (argDefinition.type === 'string') {
|
||||
isVariableAllowed = true;
|
||||
const placeholder = typeof argDefinition.placeholder === 'string'
|
||||
? argDefinition.placeholder
|
||||
: '';
|
||||
@ -370,6 +437,7 @@ class TagEditor extends React.PureComponent<Props, State> {
|
||||
const modelId = typeof value === 'string' ? value : 'unknown';
|
||||
argInput = this.renderArgModel(modelId, model);
|
||||
} else if (argDefinition.type === 'number') {
|
||||
isVariableAllowed = true;
|
||||
const placeholder = typeof argDefinition.placeholder === 'string'
|
||||
? argDefinition.placeholder
|
||||
: '';
|
||||
@ -385,16 +453,39 @@ class TagEditor extends React.PureComponent<Props, State> {
|
||||
) ? fnOrString(argDefinition.displayName, argDatas) : '';
|
||||
|
||||
return (
|
||||
<div key={argIndex} className="form-control form-control--outlined">
|
||||
<label>
|
||||
{fnOrString(displayName, argDatas)}
|
||||
{help && (
|
||||
<HelpTooltip className="space-left">{help}</HelpTooltip>
|
||||
)}
|
||||
<div data-arg-index={argIndex}>
|
||||
{argInput}
|
||||
<div key={argIndex} className="form-row">
|
||||
<div className="form-control form-control--outlined">
|
||||
<label>
|
||||
{fnOrString(displayName, argDatas)}
|
||||
{argData.type === 'variable' ? (
|
||||
<span className="faded space-left">(Variable)</span>
|
||||
) : null}
|
||||
{help && (
|
||||
<HelpTooltip className="space-left">{help}</HelpTooltip>
|
||||
)}
|
||||
<div data-arg-index={argIndex}>
|
||||
{argInputVariable || argInput}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
{isVariableAllowed ? (
|
||||
<div className="form-control form-control--outlined form-control--no-label width-auto">
|
||||
<Dropdown right>
|
||||
<DropdownButton className="btn btn--clicky">
|
||||
<i className="fa fa-gear"/>
|
||||
</DropdownButton>
|
||||
<DropdownDivider>Input Type</DropdownDivider>
|
||||
<DropdownItem value={{variable: false, argIndex}}
|
||||
onClick={this._handleChangeArgVariable}>
|
||||
<i className={'fa ' + (isVariable ? '' : 'fa-check')}/> Static Value
|
||||
</DropdownItem>
|
||||
<DropdownItem value={{variable: true, argIndex}}
|
||||
onClick={this._handleChangeArgVariable}>
|
||||
<i className={'fa ' + (isVariable ? 'fa-check' : '')}/> Environment Variable
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</label>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -422,7 +513,8 @@ class TagEditor extends React.PureComponent<Props, State> {
|
||||
} else {
|
||||
previewElement = (
|
||||
<pre>
|
||||
<code className="block selectable scrollable force-wrap">{preview || <span> </span>}</code>
|
||||
<code className="block selectable scrollable force-wrap">{preview ||
|
||||
<span> </span>}</code>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user