mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -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';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import * as moment from 'moment';
|
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
|
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');
|
const credentials = this.getCredentials('theHiveApi');
|
||||||
|
@ -77,7 +78,10 @@ export function prepareOptional(optionals: IDataObject): IDataObject {
|
||||||
const response: IDataObject = {};
|
const response: IDataObject = {};
|
||||||
for (const key in optionals) {
|
for (const key in optionals) {
|
||||||
if (optionals[key] !== undefined && optionals[key] !== null && optionals[key] !== '') {
|
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);
|
response[key] = Date.parse(optionals[key] as string);
|
||||||
} else if (key === 'artifacts') {
|
} else if (key === 'artifacts') {
|
||||||
response[key] = JSON.parse(optionals[key] as string);
|
response[key] = JSON.parse(optionals[key] as string);
|
||||||
|
@ -91,6 +95,73 @@ export function prepareOptional(optionals: IDataObject): IDataObject {
|
||||||
return response;
|
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] }) {
|
export function prepareSortQuery(sort: string, body: { query: [IDataObject] }) {
|
||||||
if (sort) {
|
if (sort) {
|
||||||
const field = sort.substring(1);
|
const field = sort.substring(1);
|
||||||
|
|
|
@ -56,7 +56,9 @@ import {
|
||||||
} from './QueryFunctions';
|
} from './QueryFunctions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
buildCustomFieldSearch,
|
||||||
mapResource,
|
mapResource,
|
||||||
|
prepareCustomFields,
|
||||||
prepareOptional,
|
prepareOptional,
|
||||||
prepareRangeQuery,
|
prepareRangeQuery,
|
||||||
prepareSortQuery,
|
prepareSortQuery,
|
||||||
|
@ -180,6 +182,31 @@ export class TheHive implements INodeType {
|
||||||
}
|
}
|
||||||
return returnData;
|
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[]> {
|
async loadObservableOptions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
// if v1 is not used we remove 'count' option
|
// if v1 is not used we remove 'count' option
|
||||||
const version = this.getCredentials('theHiveApi')?.apiVersion;
|
const version = this.getCredentials('theHiveApi')?.apiVersion;
|
||||||
|
@ -296,10 +323,17 @@ export class TheHive implements INodeType {
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
if (resource === 'alert') {
|
if (resource === 'alert') {
|
||||||
if (operation === 'count') {
|
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();
|
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)) {
|
for (const key of Object.keys(countQueryAttributs)) {
|
||||||
if (key === 'tags') {
|
if (key === 'tags') {
|
||||||
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
||||||
|
@ -348,6 +382,10 @@ export class TheHive implements INodeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operation === 'create') {
|
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 = {
|
const body: IDataObject = {
|
||||||
title: this.getNodeParameter('title', i),
|
title: this.getNodeParameter('title', i),
|
||||||
description: this.getNodeParameter('description', i),
|
description: this.getNodeParameter('description', i),
|
||||||
|
@ -360,7 +398,8 @@ export class TheHive implements INodeType {
|
||||||
source: this.getNodeParameter('source', i),
|
source: this.getNodeParameter('source', i),
|
||||||
sourceRef: this.getNodeParameter('sourceRef', i),
|
sourceRef: this.getNodeParameter('sourceRef', i),
|
||||||
follow: this.getNodeParameter('follow', i, true),
|
follow: this.getNodeParameter('follow', i, true),
|
||||||
...prepareOptional(this.getNodeParameter('optionals', i, {}) as INodeParameters),
|
customFields,
|
||||||
|
...prepareOptional(additionalFields),
|
||||||
};
|
};
|
||||||
|
|
||||||
const artifactUi = this.getNodeParameter('artifactUi', i) as IDataObject;
|
const artifactUi = this.getNodeParameter('artifactUi', i) as IDataObject;
|
||||||
|
@ -497,12 +536,18 @@ export class TheHive implements INodeType {
|
||||||
|
|
||||||
const version = credentials.apiVersion;
|
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 options = this.getNodeParameter('options', i) as IDataObject;
|
||||||
|
|
||||||
const _searchQuery: IQueryObject = And();
|
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)) {
|
for (const key of Object.keys(queryAttributs)) {
|
||||||
if (key === 'tags') {
|
if (key === 'tags') {
|
||||||
(_searchQuery['_and'] as IQueryObject[]).push(
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
||||||
|
@ -634,14 +679,18 @@ export class TheHive implements INodeType {
|
||||||
|
|
||||||
if (operation === 'update') {
|
if (operation === 'update') {
|
||||||
const alertId = this.getNodeParameter('id', i) as string;
|
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 updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||||
|
const customFields = await prepareCustomFields.call(this, updateFields, jsonParameters);
|
||||||
|
|
||||||
const artifactUi = updateFields.artifactUi as IDataObject;
|
const artifactUi = updateFields.artifactUi as IDataObject;
|
||||||
|
|
||||||
delete updateFields.artifactUi;
|
delete updateFields.artifactUi;
|
||||||
|
|
||||||
const body: IDataObject = {};
|
const body: IDataObject = {
|
||||||
|
customFields,
|
||||||
|
};
|
||||||
|
|
||||||
Object.assign(body, updateFields);
|
Object.assign(body, updateFields);
|
||||||
|
|
||||||
|
@ -1149,10 +1198,17 @@ export class TheHive implements INodeType {
|
||||||
|
|
||||||
if (resource === 'case') {
|
if (resource === 'case') {
|
||||||
if (operation === 'count') {
|
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();
|
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)) {
|
for (const key of Object.keys(countQueryAttributs)) {
|
||||||
if (key === 'tags') {
|
if (key === 'tags') {
|
||||||
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
||||||
|
@ -1258,6 +1314,9 @@ export class TheHive implements INodeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operation === 'create') {
|
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 = {
|
const body: IDataObject = {
|
||||||
title: this.getNodeParameter('title', i),
|
title: this.getNodeParameter('title', i),
|
||||||
|
@ -1268,7 +1327,8 @@ export class TheHive implements INodeType {
|
||||||
flag: this.getNodeParameter('flag', i),
|
flag: this.getNodeParameter('flag', i),
|
||||||
tlp: this.getNodeParameter('tlp', i),
|
tlp: this.getNodeParameter('tlp', i),
|
||||||
tags: splitTags(this.getNodeParameter('tags', i) as string),
|
tags: splitTags(this.getNodeParameter('tags', i) as string),
|
||||||
...prepareOptional(this.getNodeParameter('options', i, {}) as INodeParameters),
|
customFields,
|
||||||
|
...prepareOptional(options),
|
||||||
};
|
};
|
||||||
|
|
||||||
responseData = await theHiveApiRequest.call(
|
responseData = await theHiveApiRequest.call(
|
||||||
|
@ -1333,12 +1393,19 @@ export class TheHive implements INodeType {
|
||||||
|
|
||||||
const version = credentials.apiVersion;
|
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 _searchQuery: IQueryObject = And();
|
||||||
|
|
||||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
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)) {
|
for (const key of Object.keys(queryAttributs)) {
|
||||||
if (key === 'tags') {
|
if (key === 'tags') {
|
||||||
(_searchQuery['_and'] as IQueryObject[]).push(
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
||||||
|
@ -1419,9 +1486,14 @@ export class TheHive implements INodeType {
|
||||||
|
|
||||||
if (operation === 'update') {
|
if (operation === 'update') {
|
||||||
const id = this.getNodeParameter('id', i) as string;
|
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 = {
|
const body: IDataObject = {
|
||||||
...prepareOptional(this.getNodeParameter('updateFields', i, {}) as INodeParameters),
|
customFields,
|
||||||
|
...prepareOptional(updateFields),
|
||||||
};
|
};
|
||||||
|
|
||||||
responseData = await theHiveApiRequest.call(
|
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)
|
// optional attributs (Create, Promote operations)
|
||||||
{
|
{
|
||||||
displayName: 'Additional Fields',
|
displayName: 'Additional Fields',
|
||||||
|
@ -483,6 +501,89 @@ export const alertFields = [
|
||||||
],
|
],
|
||||||
operation: [
|
operation: [
|
||||||
'create',
|
'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',
|
'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',
|
displayName: 'Case Template',
|
||||||
name: 'caseTemplate',
|
name: 'caseTemplate',
|
||||||
|
@ -737,6 +893,40 @@ export const alertFields = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: [
|
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',
|
displayName: 'Description',
|
||||||
name: '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)
|
// Optional fields (Create operation)
|
||||||
{
|
{
|
||||||
displayName: 'Options',
|
displayName: 'Options',
|
||||||
|
@ -314,6 +331,61 @@ export const caseFields = [
|
||||||
required: false,
|
required: false,
|
||||||
default: '',
|
default: '',
|
||||||
options: [
|
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',
|
displayName: 'End Date',
|
||||||
name: 'endDate',
|
name: 'endDate',
|
||||||
|
@ -333,6 +405,13 @@ export const caseFields = [
|
||||||
name: 'metrics',
|
name: 'metrics',
|
||||||
default: '[]',
|
default: '[]',
|
||||||
type: 'json',
|
type: 'json',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'/jsonParameters': [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
description: 'List of metrics',
|
description: 'List of metrics',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -356,6 +435,61 @@ export const caseFields = [
|
||||||
required: false,
|
required: false,
|
||||||
default: '',
|
default: '',
|
||||||
options: [
|
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',
|
displayName: 'Description',
|
||||||
name: 'description',
|
name: 'description',
|
||||||
|
@ -403,6 +537,13 @@ export const caseFields = [
|
||||||
name: 'metrics',
|
name: 'metrics',
|
||||||
type: 'json',
|
type: 'json',
|
||||||
default: '[]',
|
default: '[]',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'/jsonParameters': [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
description: 'List of metrics',
|
description: 'List of metrics',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -583,6 +724,40 @@ export const caseFields = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: [
|
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',
|
displayName: 'Description',
|
||||||
name: 'description',
|
name: 'description',
|
||||||
|
|
Loading…
Reference in a new issue