mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 06:34:05 -08:00
✨ Add custom fields to TheHive (#1985)
* ⚡Add custom fields support to TheHive node * ⚡ Improvements to #1527 * 🐛 Make it also work without custom fields set Co-authored-by: Mika Luhta <12100880+mluhta@users.noreply.github.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
c5a1bc007f
commit
c983603306
|
@ -13,6 +13,7 @@ import {
|
|||
} from 'n8n-workflow';
|
||||
|
||||
import * as moment from 'moment';
|
||||
import { Eq } from './QueryFunctions';
|
||||
|
||||
export async function theHiveApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = this.getCredentials('theHiveApi');
|
||||
|
@ -77,7 +78,10 @@ export function prepareOptional(optionals: IDataObject): IDataObject {
|
|||
const response: IDataObject = {};
|
||||
for (const key in optionals) {
|
||||
if (optionals[key] !== undefined && optionals[key] !== null && optionals[key] !== '') {
|
||||
if (moment(optionals[key] as string, moment.ISO_8601).isValid()) {
|
||||
if (['customFieldsJson', 'customFieldsUi'].indexOf(key) > -1) {
|
||||
continue; // Ignore customFields, they need special treatment
|
||||
}
|
||||
else if (moment(optionals[key] as string, moment.ISO_8601).isValid()) {
|
||||
response[key] = Date.parse(optionals[key] as string);
|
||||
} else if (key === 'artifacts') {
|
||||
response[key] = JSON.parse(optionals[key] as string);
|
||||
|
@ -91,6 +95,73 @@ export function prepareOptional(optionals: IDataObject): IDataObject {
|
|||
return response;
|
||||
}
|
||||
|
||||
export async function prepareCustomFields(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, additionalFields: IDataObject, jsonParameters = false): Promise<IDataObject | undefined> {
|
||||
// Check if the additionalFields object contains customFields
|
||||
if (jsonParameters === true) {
|
||||
const customFieldsJson = additionalFields.customFieldsJson;
|
||||
// Delete from additionalFields as some operations (e.g. alert:update) do not run prepareOptional
|
||||
// which would remove the extra fields
|
||||
delete additionalFields.customFieldsJson;
|
||||
|
||||
if (typeof customFieldsJson === 'string') {
|
||||
return JSON.parse(customFieldsJson);
|
||||
} else if (typeof customFieldsJson === 'object') {
|
||||
return customFieldsJson as IDataObject;
|
||||
} else if (customFieldsJson) {
|
||||
throw Error('customFieldsJson value is invalid');
|
||||
}
|
||||
} else if (additionalFields.customFieldsUi) {
|
||||
// Get Custom Field Types from TheHive
|
||||
const version = this.getCredentials('theHiveApi')?.apiVersion;
|
||||
const endpoint = version === 'v1' ? '/customField' : '/list/custom_fields';
|
||||
|
||||
const requestResult = await theHiveApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
endpoint as string,
|
||||
);
|
||||
|
||||
// Convert TheHive3 response to the same format as TheHive 4
|
||||
// [{name, reference, type}]
|
||||
const hiveCustomFields = version === 'v1' ? requestResult : Object.keys(requestResult).map(key => requestResult[key]);
|
||||
// Build reference to type mapping object
|
||||
const referenceTypeMapping = hiveCustomFields.reduce((acc: IDataObject, curr: IDataObject) => (acc[curr.reference as string] = curr.type, acc), {});
|
||||
|
||||
// Build "fieldName": {"type": "value"} objects
|
||||
const customFieldsUi = (additionalFields.customFieldsUi as IDataObject);
|
||||
const customFields : IDataObject = (customFieldsUi?.customFields as IDataObject[]).reduce((acc: IDataObject, curr: IDataObject) => {
|
||||
const fieldName = curr.field as string;
|
||||
|
||||
// Might be able to do some type conversions here if needed, TODO
|
||||
|
||||
acc[fieldName] = {
|
||||
[referenceTypeMapping[fieldName]]: curr.value,
|
||||
};
|
||||
return acc;
|
||||
}, {} as IDataObject);
|
||||
|
||||
delete additionalFields.customFieldsUi;
|
||||
return customFields;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function buildCustomFieldSearch(customFields: IDataObject): IDataObject[] {
|
||||
const customFieldTypes = ['boolean', 'date', 'float', 'integer', 'number', 'string'];
|
||||
const searchQueries: IDataObject[] = [];
|
||||
Object.keys(customFields).forEach(customFieldName => {
|
||||
const customField = customFields[customFieldName] as IDataObject;
|
||||
|
||||
// Figure out the field type from the object's keys
|
||||
const fieldType = Object.keys(customField)
|
||||
.filter(key => customFieldTypes.indexOf(key) > -1)[0];
|
||||
const fieldValue = customField[fieldType];
|
||||
|
||||
searchQueries.push(Eq(`customFields.${customFieldName}.${fieldType}`, fieldValue));
|
||||
});
|
||||
return searchQueries;
|
||||
}
|
||||
|
||||
export function prepareSortQuery(sort: string, body: { query: [IDataObject] }) {
|
||||
if (sort) {
|
||||
const field = sort.substring(1);
|
||||
|
|
|
@ -56,7 +56,9 @@ import {
|
|||
} from './QueryFunctions';
|
||||
|
||||
import {
|
||||
buildCustomFieldSearch,
|
||||
mapResource,
|
||||
prepareCustomFields,
|
||||
prepareOptional,
|
||||
prepareRangeQuery,
|
||||
prepareSortQuery,
|
||||
|
@ -180,6 +182,31 @@ export class TheHive implements INodeType {
|
|||
}
|
||||
return returnData;
|
||||
},
|
||||
async loadCustomFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const version = this.getCredentials('theHiveApi')?.apiVersion;
|
||||
const endpoint = version === 'v1' ? '/customField' : '/list/custom_fields';
|
||||
|
||||
const requestResult = await theHiveApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
endpoint as string,
|
||||
);
|
||||
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
|
||||
// Convert TheHive3 response to the same format as TheHive 4
|
||||
const customFields = version === 'v1' ? requestResult : Object.keys(requestResult).map(key => requestResult[key]);
|
||||
|
||||
for (const field of customFields) {
|
||||
returnData.push({
|
||||
name: `${field.name}: ${field.reference}`,
|
||||
value: field.reference,
|
||||
description: `${field.type}: ${field.description}`,
|
||||
} as INodePropertyOptions);
|
||||
}
|
||||
|
||||
return returnData;
|
||||
},
|
||||
async loadObservableOptions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
// if v1 is not used we remove 'count' option
|
||||
const version = this.getCredentials('theHiveApi')?.apiVersion;
|
||||
|
@ -296,10 +323,17 @@ export class TheHive implements INodeType {
|
|||
for (let i = 0; i < length; i++) {
|
||||
if (resource === 'alert') {
|
||||
if (operation === 'count') {
|
||||
const countQueryAttributs: any = prepareOptional(this.getNodeParameter('filters', i, {}) as INodeParameters); // tslint:disable-line:no-any
|
||||
|
||||
const filters = this.getNodeParameter('filters', i, {}) as INodeParameters;
|
||||
const countQueryAttributs: any = prepareOptional(filters); // tslint:disable-line:no-any
|
||||
|
||||
const _countSearchQuery: IQueryObject = And();
|
||||
|
||||
if ('customFieldsUi' in filters) {
|
||||
const customFields = await prepareCustomFields.call(this, filters) as IDataObject;
|
||||
const searchQueries = buildCustomFieldSearch(customFields);
|
||||
(_countSearchQuery['_and'] as IQueryObject[]).push(...searchQueries);
|
||||
}
|
||||
|
||||
for (const key of Object.keys(countQueryAttributs)) {
|
||||
if (key === 'tags') {
|
||||
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
||||
|
@ -348,6 +382,10 @@ export class TheHive implements INodeType {
|
|||
}
|
||||
|
||||
if (operation === 'create') {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as INodeParameters;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
|
||||
const customFields = await prepareCustomFields.call(this, additionalFields, jsonParameters);
|
||||
const body: IDataObject = {
|
||||
title: this.getNodeParameter('title', i),
|
||||
description: this.getNodeParameter('description', i),
|
||||
|
@ -360,7 +398,8 @@ export class TheHive implements INodeType {
|
|||
source: this.getNodeParameter('source', i),
|
||||
sourceRef: this.getNodeParameter('sourceRef', i),
|
||||
follow: this.getNodeParameter('follow', i, true),
|
||||
...prepareOptional(this.getNodeParameter('optionals', i, {}) as INodeParameters),
|
||||
customFields,
|
||||
...prepareOptional(additionalFields),
|
||||
};
|
||||
|
||||
const artifactUi = this.getNodeParameter('artifactUi', i) as IDataObject;
|
||||
|
@ -497,12 +536,18 @@ export class TheHive implements INodeType {
|
|||
|
||||
const version = credentials.apiVersion;
|
||||
|
||||
const queryAttributs: any = prepareOptional(this.getNodeParameter('filters', i, {}) as INodeParameters); // tslint:disable-line:no-any
|
||||
|
||||
const filters = this.getNodeParameter('filters', i, {}) as INodeParameters;
|
||||
const queryAttributs: any = prepareOptional(filters); // tslint:disable-line:no-any
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
const _searchQuery: IQueryObject = And();
|
||||
|
||||
if ('customFieldsUi' in filters) {
|
||||
const customFields = await prepareCustomFields.call(this, filters) as IDataObject;
|
||||
const searchQueries = buildCustomFieldSearch(customFields);
|
||||
(_searchQuery['_and'] as IQueryObject[]).push(...searchQueries);
|
||||
}
|
||||
|
||||
for (const key of Object.keys(queryAttributs)) {
|
||||
if (key === 'tags') {
|
||||
(_searchQuery['_and'] as IQueryObject[]).push(
|
||||
|
@ -634,14 +679,18 @@ export class TheHive implements INodeType {
|
|||
|
||||
if (operation === 'update') {
|
||||
const alertId = this.getNodeParameter('id', i) as string;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
|
||||
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||
const customFields = await prepareCustomFields.call(this, updateFields, jsonParameters);
|
||||
|
||||
const artifactUi = updateFields.artifactUi as IDataObject;
|
||||
|
||||
delete updateFields.artifactUi;
|
||||
|
||||
const body: IDataObject = {};
|
||||
const body: IDataObject = {
|
||||
customFields,
|
||||
};
|
||||
|
||||
Object.assign(body, updateFields);
|
||||
|
||||
|
@ -1149,10 +1198,17 @@ export class TheHive implements INodeType {
|
|||
|
||||
if (resource === 'case') {
|
||||
if (operation === 'count') {
|
||||
const countQueryAttributs: any = prepareOptional(this.getNodeParameter('filters', i, {}) as INodeParameters); // tslint:disable-line:no-any
|
||||
const filters = this.getNodeParameter('filters', i, {}) as INodeParameters;
|
||||
const countQueryAttributs: any = prepareOptional(filters); // tslint:disable-line:no-any
|
||||
|
||||
const _countSearchQuery: IQueryObject = And();
|
||||
|
||||
if ('customFieldsUi' in filters) {
|
||||
const customFields = await prepareCustomFields.call(this, filters) as IDataObject;
|
||||
const searchQueries = buildCustomFieldSearch(customFields);
|
||||
(_countSearchQuery['_and'] as IQueryObject[]).push(...searchQueries);
|
||||
}
|
||||
|
||||
for (const key of Object.keys(countQueryAttributs)) {
|
||||
if (key === 'tags') {
|
||||
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
||||
|
@ -1258,6 +1314,9 @@ export class TheHive implements INodeType {
|
|||
}
|
||||
|
||||
if (operation === 'create') {
|
||||
const options = this.getNodeParameter('options', i, {}) as INodeParameters;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
const customFields = await prepareCustomFields.call(this, options, jsonParameters);
|
||||
|
||||
const body: IDataObject = {
|
||||
title: this.getNodeParameter('title', i),
|
||||
|
@ -1268,7 +1327,8 @@ export class TheHive implements INodeType {
|
|||
flag: this.getNodeParameter('flag', i),
|
||||
tlp: this.getNodeParameter('tlp', i),
|
||||
tags: splitTags(this.getNodeParameter('tags', i) as string),
|
||||
...prepareOptional(this.getNodeParameter('options', i, {}) as INodeParameters),
|
||||
customFields,
|
||||
...prepareOptional(options),
|
||||
};
|
||||
|
||||
responseData = await theHiveApiRequest.call(
|
||||
|
@ -1333,12 +1393,19 @@ export class TheHive implements INodeType {
|
|||
|
||||
const version = credentials.apiVersion;
|
||||
|
||||
const queryAttributs: any = prepareOptional(this.getNodeParameter('filters', i, {}) as INodeParameters); // tslint:disable-line:no-any
|
||||
|
||||
const filters = this.getNodeParameter('filters', i, {}) as INodeParameters;
|
||||
const queryAttributs: any = prepareOptional(filters); // tslint:disable-line:no-any
|
||||
|
||||
const _searchQuery: IQueryObject = And();
|
||||
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if ('customFieldsUi' in filters) {
|
||||
const customFields = await prepareCustomFields.call(this, filters) as IDataObject;
|
||||
const searchQueries = buildCustomFieldSearch(customFields);
|
||||
(_searchQuery['_and'] as IQueryObject[]).push(...searchQueries);
|
||||
}
|
||||
|
||||
for (const key of Object.keys(queryAttributs)) {
|
||||
if (key === 'tags') {
|
||||
(_searchQuery['_and'] as IQueryObject[]).push(
|
||||
|
@ -1419,9 +1486,14 @@ export class TheHive implements INodeType {
|
|||
|
||||
if (operation === 'update') {
|
||||
const id = this.getNodeParameter('id', i) as string;
|
||||
const updateFields = this.getNodeParameter('updateFields', i, {}) as INodeParameters;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
|
||||
const customFields = await prepareCustomFields.call(this, updateFields, jsonParameters);
|
||||
|
||||
const body: IDataObject = {
|
||||
...prepareOptional(this.getNodeParameter('updateFields', i, {}) as INodeParameters),
|
||||
customFields,
|
||||
...prepareOptional(updateFields),
|
||||
};
|
||||
|
||||
responseData = await theHiveApiRequest.call(
|
||||
|
|
|
@ -468,6 +468,24 @@ export const alertFields = [
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'alert',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// optional attributs (Create, Promote operations)
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
|
@ -483,6 +501,89 @@ export const alertFields = [
|
|||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Case Template',
|
||||
name: 'caseTemplate',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Case template to use when a case is created from this alert.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Fields',
|
||||
name: 'customFieldsUi',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/jsonParameters': [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
placeholder: 'Add Custom Field',
|
||||
options: [
|
||||
{
|
||||
name: 'customFields',
|
||||
displayName: 'Custom Field',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'loadCustomFields',
|
||||
},
|
||||
default: 'Custom Field',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Custom Field value. Use an expression if the type is not a string.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Fields (JSON)',
|
||||
name: 'customFieldsJson',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/jsonParameters': [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Custom fields in JSON format. Overrides Custom Fields UI if set.',
|
||||
},
|
||||
],
|
||||
},
|
||||
// optional attributs (Promote operation)
|
||||
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
placeholder: 'Add Field',
|
||||
type: 'collection',
|
||||
required: false,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'alert',
|
||||
],
|
||||
operation: [
|
||||
'promote',
|
||||
],
|
||||
},
|
||||
|
@ -581,6 +682,61 @@ export const alertFields = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Fields',
|
||||
name: 'customFieldsUi',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/jsonParameters': [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Add Custom Field',
|
||||
options: [
|
||||
{
|
||||
name: 'customFields',
|
||||
displayName: 'Custom Field',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'loadCustomFields',
|
||||
},
|
||||
default: 'Custom Field',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Custom Field value. Use an expression if the type is not a string.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Fields (JSON)',
|
||||
name: 'customFieldsJson',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/jsonParameters': [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Custom fields in JSON format. Overrides Custom Fields UI if set.',
|
||||
},
|
||||
{
|
||||
displayName: 'Case Template',
|
||||
name: 'caseTemplate',
|
||||
|
@ -737,6 +893,40 @@ export const alertFields = [
|
|||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Fields',
|
||||
name: 'customFieldsUi',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
placeholder: 'Add Custom Field',
|
||||
options: [
|
||||
{
|
||||
name: 'customFields',
|
||||
displayName: 'Custom Field',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'loadCustomFields',
|
||||
},
|
||||
default: 'Custom Field',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Custom Field value. Use an expression if the type is not a string.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
|
|
|
@ -295,6 +295,23 @@ export const caseFields = [
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'case',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
// Optional fields (Create operation)
|
||||
{
|
||||
displayName: 'Options',
|
||||
|
@ -314,6 +331,61 @@ export const caseFields = [
|
|||
required: false,
|
||||
default: '',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Fields',
|
||||
name: 'customFieldsUi',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/jsonParameters': [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Add Custom Field',
|
||||
options: [
|
||||
{
|
||||
name: 'customFields',
|
||||
displayName: 'Custom Field',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'loadCustomFields',
|
||||
},
|
||||
default: 'Custom Field',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Custom Field value. Use an expression if the type is not a string.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Fields (JSON)',
|
||||
name: 'customFieldsJson',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/jsonParameters': [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Custom fields in JSON format. Overrides Custom Fields UI if set.',
|
||||
},
|
||||
{
|
||||
displayName: 'End Date',
|
||||
name: 'endDate',
|
||||
|
@ -333,6 +405,13 @@ export const caseFields = [
|
|||
name: 'metrics',
|
||||
default: '[]',
|
||||
type: 'json',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/jsonParameters': [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'List of metrics',
|
||||
},
|
||||
],
|
||||
|
@ -356,6 +435,61 @@ export const caseFields = [
|
|||
required: false,
|
||||
default: '',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Fields',
|
||||
name: 'customFieldsUi',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/jsonParameters': [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Add Custom Field',
|
||||
options: [
|
||||
{
|
||||
name: 'customFields',
|
||||
displayName: 'Custom Field',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'loadCustomFields',
|
||||
},
|
||||
default: 'Custom Field',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Custom Field value. Use an expression if the type is not a string.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Fields (JSON)',
|
||||
name: 'customFieldsJson',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/jsonParameters': [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Custom fields in JSON format. Overrides Custom Fields UI if set.',
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
|
@ -403,6 +537,13 @@ export const caseFields = [
|
|||
name: 'metrics',
|
||||
type: 'json',
|
||||
default: '[]',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/jsonParameters': [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'List of metrics',
|
||||
},
|
||||
{
|
||||
|
@ -583,6 +724,40 @@ export const caseFields = [
|
|||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Fields',
|
||||
name: 'customFieldsUi',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
placeholder: 'Add Custom Field',
|
||||
options: [
|
||||
{
|
||||
name: 'customFields',
|
||||
displayName: 'Custom Field',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'loadCustomFields',
|
||||
},
|
||||
default: 'Custom Field',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Custom Field value. Use an expression if the type is not a string.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
|
|
Loading…
Reference in a new issue