Allow prompt values to be clear + add actions to template tag plugin API (#2736)

Co-authored-by: Opender Singh <opender.singh@konghq.com>
This commit is contained in:
Julien Giovaresco 2020-12-01 00:15:17 +01:00 committed by GitHub
parent 1bdd8137da
commit dfc89a3111
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 102 additions and 2 deletions

View File

@ -6,6 +6,8 @@ import * as _request from './request';
import * as _response from './response'; import * as _response from './response';
import * as _store from './store'; import * as _store from './store';
export { PluginStore } from './store';
export const app = _app; export const app = _app;
export const data = _data; export const data = _data;
export const network = _network; export const network = _network;

View File

@ -2,7 +2,16 @@
import type { Plugin } from '../index'; import type { Plugin } from '../index';
import * as models from '../../models'; import * as models from '../../models';
export function init(plugin: Plugin) { export type PluginStore = {
hasItem(string): Promise<boolean>,
setItem(string, string): Promise<void>,
getItem(string): Promise<string | null>,
removeItem(string): Promise<void>,
clear(): Promise<void>,
all(): Promise<Array<{ key: string, value: string }>>,
};
export function init(plugin: Plugin): { store: PluginStore } {
return { return {
store: { store: {
async hasItem(key: string): Promise<boolean> { async hasItem(key: string): Promise<boolean> {

View File

@ -46,6 +46,10 @@ export default class BaseExtension {
return this._ext.args || []; return this._ext.args || [];
} }
getActions() {
return this._ext.actions || [];
}
isDeprecated() { isDeprecated() {
return this._ext.deprecated || false; return this._ext.deprecated || false;
} }

View File

@ -2,6 +2,7 @@
import type { NunjucksParsedTagArg } from '../utils'; import type { NunjucksParsedTagArg } from '../utils';
import type { Request } from '../../models/request'; import type { Request } from '../../models/request';
import type { Response } from '../../models/response'; import type { Response } from '../../models/response';
import type { PluginStore } from '../../plugins/context';
export type PluginArgumentValue = string | number | boolean; export type PluginArgumentValue = string | number | boolean;
type DisplayName = string | ((args: Array<NunjucksParsedTagArg>) => string); type DisplayName = string | ((args: Array<NunjucksParsedTagArg>) => string);
@ -75,12 +76,23 @@ export type PluginTemplateTagContext = {
}, },
}; };
export type PluginTemplateTagActionContext = {
store: PluginStore,
};
export type PluginTemplateTagAction = {
name: string,
icon?: string,
run: (context: PluginTemplateTagActionContext) => Promise<void>,
};
export type PluginTemplateTag = { export type PluginTemplateTag = {
args: Array<PluginArgument>, args: Array<PluginArgument>,
name: string, name: string,
displayName: DisplayName, displayName: DisplayName,
disablePreview: () => boolean, disablePreview: () => boolean,
description: string, description: string,
actions: Array<PluginTemplateTagAction>,
run: (context: PluginTemplateTagContext, ...arg: Array<any>) => Promise<any> | any, run: (context: PluginTemplateTagContext, ...arg: Array<any>) => Promise<any> | any,
deprecated?: boolean, deprecated?: boolean,
validate?: (value: any) => ?string, validate?: (value: any) => ?string,

View File

@ -100,6 +100,7 @@ export async function getTagDefinitions(): Promise<Array<NunjucksParsedTag>> {
description: ext.getDescription(), description: ext.getDescription(),
disablePreview: ext.getDisablePreview(), disablePreview: ext.getDisablePreview(),
args: ext.getArgs(), args: ext.getArgs(),
actions: ext.getActions(),
})); }));
} }

View File

@ -2,6 +2,7 @@
import type { PluginArgumentEnumOption } from './extensions'; import type { PluginArgumentEnumOption } from './extensions';
import objectPath from 'objectpath'; import objectPath from 'objectpath';
import type { PluginStore } from '../plugins/context';
export type NunjucksParsedTagArg = { export type NunjucksParsedTagArg = {
type: 'string' | 'number' | 'boolean' | 'variable' | 'expression' | 'enum' | 'file' | 'model', type: 'string' | 'number' | 'boolean' | 'variable' | 'expression' | 'enum' | 'file' | 'model',
@ -21,9 +22,16 @@ export type NunjucksParsedTagArg = {
extensions?: Array<string>, extensions?: Array<string>,
}; };
export type NunjucksActionTag = {
name: string,
icon?: string,
run: (context: PluginStore) => Promise<void>,
};
export type NunjucksParsedTag = { export type NunjucksParsedTag = {
name: string, name: string,
args: Array<NunjucksParsedTagArg>, args: Array<NunjucksParsedTagArg>,
actions: Array<NunjucksActionTag>,
rawValue?: string, rawValue?: string,
displayName?: string, displayName?: string,
description?: string, description?: string,

View File

@ -4,7 +4,11 @@ import autobind from 'autobind-decorator';
import classnames from 'classnames'; import classnames from 'classnames';
import clone from 'clone'; import clone from 'clone';
import * as templating from '../../../templating'; import * as templating from '../../../templating';
import type { NunjucksParsedTag, NunjucksParsedTagArg } from '../../../templating/utils'; import type {
NunjucksActionTag,
NunjucksParsedTag,
NunjucksParsedTagArg,
} from '../../../templating/utils';
import * as templateUtils from '../../../templating/utils'; import * as templateUtils from '../../../templating/utils';
import * as db from '../../../common/database'; import * as db from '../../../common/database';
import * as models from '../../../models'; import * as models from '../../../models';
@ -15,6 +19,8 @@ import type { Workspace } from '../../../models/workspace';
import type { PluginArgumentEnumOption } from '../../../templating/extensions/index'; import type { PluginArgumentEnumOption } from '../../../templating/extensions/index';
import { Dropdown, DropdownButton, DropdownDivider, DropdownItem } from '../base/dropdown/index'; import { Dropdown, DropdownButton, DropdownDivider, DropdownItem } from '../base/dropdown/index';
import FileInputButton from '../base/file-input-button'; import FileInputButton from '../base/file-input-button';
import { getTemplateTags } from '../../../plugins';
import * as pluginContexts from '../../../plugins/context';
type Props = { type Props = {
handleRender: Function, handleRender: Function,
@ -252,6 +258,20 @@ class TagEditor extends React.PureComponent<Props, State> {
this._update(this.state.tagDefinitions, tagDefinition, null, false); this._update(this.state.tagDefinitions, tagDefinition, null, false);
} }
async _handleActionClick(action: NunjucksActionTag) {
const templateTags = await getTemplateTags();
const activeTemplateTag = templateTags.find(({ templateTag }) => {
return templateTag.name === this.state.activeTagData.name;
});
const helperContext = {
...pluginContexts.store.init(activeTemplateTag.plugin),
};
await action.run(helperContext);
return this._handleRefresh();
}
_setSelectRef(n: ?HTMLSelectElement) { _setSelectRef(n: ?HTMLSelectElement) {
this._select = n; this._select = n;
@ -626,6 +646,33 @@ class TagEditor extends React.PureComponent<Props, State> {
); );
} }
renderActions(actions = []) {
return (
<div className="form-row">
<div className="form-control">
<label>Actions</label>
<div className="form-row">{actions.map(this.renderAction)}</div>
</div>
</div>
);
}
renderAction(action: NunjucksActionTag, index: number) {
const name = action.name;
const icon = action.icon ? <i className={action.icon} /> : undefined;
return (
<button
key={name}
className="btn btn--clicky btn--largest"
type="button"
onClick={() => this._handleActionClick(action)}>
{icon}
{name}
</button>
);
}
render() { render() {
const { error, preview, activeTagDefinition, activeTagData, rendering } = this.state; const { error, preview, activeTagDefinition, activeTagData, rendering } = this.state;
@ -671,6 +718,9 @@ class TagEditor extends React.PureComponent<Props, State> {
activeTagDefinition.args.map((argDefinition: NunjucksParsedTagArg, index) => activeTagDefinition.args.map((argDefinition: NunjucksParsedTagArg, index) =>
this.renderArg(argDefinition, activeTagData.args, index), this.renderArg(argDefinition, activeTagData.args, index),
)} )}
{activeTagDefinition?.actions?.length && this.renderActions(activeTagDefinition.actions)}
{!activeTagDefinition && ( {!activeTagDefinition && (
<div className="form-control form-control--outlined"> <div className="form-control form-control--outlined">
<label> <label>

View File

@ -268,6 +268,10 @@
min-height: var(--line-height-sm); min-height: var(--line-height-sm);
height: unset; height: unset;
} }
&.btn--largest {
width: 100%;
}
} }
*:disabled { *:disabled {

View File

@ -48,6 +48,16 @@ module.exports.templateTags = [
defaultValue: true, defaultValue: true,
}, },
], ],
actions: [
{
name: 'Clear',
icon: 'fa fa-trash',
run: context => {
console.log(`[prompt] Clear action`);
return context.store.clear();
},
},
],
async run(context, title, label, defaultValue, explicitStorageKey, maskText, saveLastValue) { async run(context, title, label, defaultValue, explicitStorageKey, maskText, saveLastValue) {
if (!title) { if (!title) {
throw new Error('Title attribute is required for prompt tag'); throw new Error('Title attribute is required for prompt tag');