diff --git a/package.json b/package.json index e106115241..4014b6b629 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "root", - "version": "3.3.0", + "version": "3.4.0", "private": true, "engines": { "node": "20" diff --git a/plugins/keycloak-backend/dist-dynamic/package.json b/plugins/keycloak-backend/dist-dynamic/package.json index d5e66ea18c..8de0f2211e 100644 --- a/plugins/keycloak-backend/dist-dynamic/package.json +++ b/plugins/keycloak-backend/dist-dynamic/package.json @@ -1,6 +1,6 @@ { "name": "@janus-idp/backstage-plugin-keycloak-backend-dynamic", - "version": "2.0.7", + "version": "2.0.8", "description": "A Backend backend plugin for Keycloak", "main": "./dist/index.cjs.js", "types": "./dist/index.d.ts", diff --git a/plugins/keycloak-backend/package.json b/plugins/keycloak-backend/package.json index e9f4684e92..8492b4150b 100644 --- a/plugins/keycloak-backend/package.json +++ b/plugins/keycloak-backend/package.json @@ -1,6 +1,6 @@ { "name": "@janus-idp/backstage-plugin-keycloak-backend", - "version": "2.0.7", + "version": "2.0.8", "description": "A Backend backend plugin for Keycloak", "main": "src/index.ts", "types": "src/index.ts", diff --git a/plugins/orchestrator-backend/CHANGELOG.md b/plugins/orchestrator-backend/CHANGELOG.md index 0a25f3d2f2..6243c37900 100644 --- a/plugins/orchestrator-backend/CHANGELOG.md +++ b/plugins/orchestrator-backend/CHANGELOG.md @@ -1,5 +1,9 @@ ### Dependencies +* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.21.0 + +### Dependencies + * **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.20.0 * **@janus-idp/cli:** upgraded to 1.15.2 diff --git a/plugins/orchestrator-backend/dist-dynamic/package.json b/plugins/orchestrator-backend/dist-dynamic/package.json index 607c5896f7..ee08c1b095 100644 --- a/plugins/orchestrator-backend/dist-dynamic/package.json +++ b/plugins/orchestrator-backend/dist-dynamic/package.json @@ -1,6 +1,6 @@ { "name": "@janus-idp/backstage-plugin-orchestrator-backend-dynamic", - "version": "2.2.2", + "version": "2.3.1", "license": "Apache-2.0", "main": "./dist/index.cjs.js", "types": "src/index.ts", diff --git a/plugins/orchestrator-backend/package.json b/plugins/orchestrator-backend/package.json index 586573bff1..d726bef7d1 100644 --- a/plugins/orchestrator-backend/package.json +++ b/plugins/orchestrator-backend/package.json @@ -1,6 +1,6 @@ { "name": "@janus-idp/backstage-plugin-orchestrator-backend", - "version": "2.2.2", + "version": "2.3.1", "license": "Apache-2.0", "main": "src/index.ts", "types": "src/index.ts", @@ -78,7 +78,7 @@ "@backstage/plugin-permission-node": "^0.8.0", "@backstage/plugin-scaffolder-backend": "^1.23.0", "@backstage/plugin-scaffolder-node": "^0.4.8", - "@janus-idp/backstage-plugin-orchestrator-common": "*", + "@janus-idp/backstage-plugin-orchestrator-common": "1.21.0", "@urql/core": "^4.1.4", "ajv-formats": "^2.1.1", "cloudevents": "^8.0.0", diff --git a/plugins/orchestrator-backend/src/helpers/filterBuilder.ts b/plugins/orchestrator-backend/src/helpers/filterBuilder.ts new file mode 100644 index 0000000000..e6142f9082 --- /dev/null +++ b/plugins/orchestrator-backend/src/helpers/filterBuilder.ts @@ -0,0 +1,203 @@ +import { + FieldFilter, + FieldFilterOperatorEnum, + Filter, + IntrospectionField, + LogicalFilter, + TypeName, +} from '@janus-idp/backstage-plugin-orchestrator-common'; + +function isLogicalFilter(filter: Filter): filter is LogicalFilter { + return (filter as LogicalFilter).filters !== undefined; +} + +function handleLogicalFilter( + introspection: IntrospectionField[], + filter: LogicalFilter, +): string { + if (!filter.operator) return ''; + + const subClauses = filter.filters.map(f => + buildFilterCondition(introspection, f), + ); + + return `${filter.operator.toLowerCase()}: {${subClauses.join(', ')}}`; +} + +function handleBetweenOperator(filter: FieldFilter): string { + if (!Array.isArray(filter.value) || filter.value.length !== 2) { + throw new Error('Between operator requires an array of two elements'); + } + return `${filter.field}: {${getGraphQLOperator(FieldFilterOperatorEnum.Between)}: {from: "${filter.value[0]}", to: "${filter.value[1]}"}}`; +} + +function handleIsNullOperator(filter: FieldFilter): string { + return `${filter.field}: {${getGraphQLOperator(FieldFilterOperatorEnum.IsNull)}: ${convertToBoolean(filter.value)}}`; +} + +function handleBinaryOperator( + binaryFilter: FieldFilter, + fieldDef: IntrospectionField, +): string { + const formattedValue = Array.isArray(binaryFilter.value) + ? `[${binaryFilter.value.map(v => formatValue(binaryFilter.field, v, fieldDef)).join(', ')}]` + : formatValue(binaryFilter.field, binaryFilter.value, fieldDef); + + return `${binaryFilter.field}: {${getGraphQLOperator(binaryFilter.operator)}: ${formattedValue}}`; +} + +export function buildFilterCondition( + introspection: IntrospectionField[], + filters?: Filter, +): string { + if (!filters) { + return ''; + } + + if (isLogicalFilter(filters)) { + return handleLogicalFilter(introspection, filters); + } + + if (!isOperatorSupported(filters.operator)) { + throw new Error(`Unsopported operator ${filters.operator}`); + } + + const fieldDef = introspection.find(f => f.name === filters.field); + if (!fieldDef) { + throw new Error(`Can't find field "${filters.field}" definition`); + } + + if (!isOperatorAllowedForField(filters.operator, fieldDef)) { + throw new Error(`Unsupported field type ${fieldDef.type.name}`); + } + + switch (filters.operator) { + case FieldFilterOperatorEnum.IsNull: + return handleIsNullOperator(filters); + case FieldFilterOperatorEnum.Between: + return handleBetweenOperator(filters); + case FieldFilterOperatorEnum.Eq: + case FieldFilterOperatorEnum.Like: + case FieldFilterOperatorEnum.In: + case FieldFilterOperatorEnum.Gt: + case FieldFilterOperatorEnum.Gte: + case FieldFilterOperatorEnum.Lt: + case FieldFilterOperatorEnum.Lte: + return handleBinaryOperator(filters, fieldDef); + + default: + throw new Error(`Can't build filter condition`); + } +} + +function isOperatorSupported(operator: FieldFilterOperatorEnum): boolean { + return ( + operator === FieldFilterOperatorEnum.Eq || + operator === FieldFilterOperatorEnum.Like || + operator === FieldFilterOperatorEnum.In || + operator === FieldFilterOperatorEnum.IsNull || + operator === FieldFilterOperatorEnum.Gt || + operator === FieldFilterOperatorEnum.Gte || + operator === FieldFilterOperatorEnum.Lt || + operator === FieldFilterOperatorEnum.Lte || + operator === FieldFilterOperatorEnum.Between + ); +} + +function isFieldFilterSupported(fieldDef: IntrospectionField): boolean { + return fieldDef?.type.name === TypeName.String; +} + +function isOperatorAllowedForField( + operator: FieldFilterOperatorEnum, + fieldDef: IntrospectionField, +): boolean { + const allowedOperators: Record = { + [TypeName.String]: [ + FieldFilterOperatorEnum.In, + FieldFilterOperatorEnum.Like, + FieldFilterOperatorEnum.IsNull, + FieldFilterOperatorEnum.Eq, + ], + [TypeName.Id]: [ + FieldFilterOperatorEnum.In, + FieldFilterOperatorEnum.IsNull, + FieldFilterOperatorEnum.Eq, + ], + [TypeName.Date]: [ + FieldFilterOperatorEnum.IsNull, + FieldFilterOperatorEnum.Eq, + FieldFilterOperatorEnum.Gt, + FieldFilterOperatorEnum.Gte, + FieldFilterOperatorEnum.Lt, + FieldFilterOperatorEnum.Lte, + FieldFilterOperatorEnum.Between, + ], + [TypeName.StringArray]: [], + }; + const allowedForType = allowedOperators[fieldDef.type.name]; + return allowedForType ? allowedForType.includes(operator) : false; +} + +function convertToBoolean(value: any): boolean { + if (typeof value === 'boolean') { + return value; + } + if (typeof value === 'string') { + return value.toLowerCase() === 'true'; + } + if (typeof value === 'number') { + return value === 1; + } + return false; // Default to false for unsupported types +} + +function formatValue( + fieldName: string, + fieldValue: any, + fieldDef: IntrospectionField, +): string { + if (!isFieldFilterSupported) { + throw new Error(`Unsupported field type ${fieldDef.type.name}`); + } + + if ( + fieldDef.type.name === TypeName.String || + fieldDef.type.name === TypeName.Id || + fieldDef.type.name === TypeName.Date + ) { + return `"${fieldValue}"`; + } + throw new Error( + `Failed to format value for ${fieldName} ${fieldValue} with type ${fieldDef.type.name}`, + ); +} + +function getGraphQLOperator(operator: FieldFilterOperatorEnum): string { + switch (operator) { + case 'EQ': + return 'equal'; + case 'LIKE': + return 'like'; + case 'IN': + return 'in'; + case 'IS_NULL': + return 'isNull'; + case 'GT': + return 'greaterThan'; + case 'GTE': + return 'greaterThanEqual'; + case 'LT': + return 'lessThan'; + case 'LTE': + return 'lessThanEqual'; + // case 'CONTAINS': + // return "contains" + // case 'CONTAINS_ALL': + // case 'CONTAINS_ANY': + case 'BETWEEN': + return 'between'; + default: + throw new Error(`Operation "${operator}" not supported`); + } +} diff --git a/plugins/orchestrator-backend/src/helpers/filterBuilders.test.ts b/plugins/orchestrator-backend/src/helpers/filterBuilders.test.ts new file mode 100644 index 0000000000..3d9d81542c --- /dev/null +++ b/plugins/orchestrator-backend/src/helpers/filterBuilders.test.ts @@ -0,0 +1,506 @@ +import { + FieldFilterOperatorEnum, + Filter, + IntrospectionField, + TypeKind, + TypeName, +} from '@janus-idp/backstage-plugin-orchestrator-common'; + +import { buildFilterCondition } from './filterBuilder'; + +describe('column filters', () => { + const createIntrospectionField = ( + name: string, + type: TypeName, + ): IntrospectionField => ({ + name, + type: { + name: type, + kind: TypeKind.InputObject, + ofType: null, + }, + }); + + const createFieldFilter = ( + field: string, + operator: FieldFilterOperatorEnum, + value: any, + ): Filter => ({ + field, + operator, + value, + }); + + type FilterTestCase = { + name: string; + introspectionFields: IntrospectionField[]; + filter: Filter | undefined; + expectedResult: string; + }; + describe('empty filter testcases', () => { + const emptyFilterTestCases: FilterTestCase[] = [ + { + name: 'returns empty string when filters are null or undefined', + introspectionFields: [], + filter: undefined, + expectedResult: '', + }, + ]; + emptyFilterTestCases.forEach( + ({ name, introspectionFields, filter, expectedResult }) => { + it(`${name}`, () => { + const result = buildFilterCondition(introspectionFields, filter); + expect(result).toBe(expectedResult); + }); + }, + ); + }); + describe('stringArgument testcases', () => { + const stringTestCases: FilterTestCase[] = [ + { + name: 'returns correct filter for single string field with equal operator', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + ], + filter: createFieldFilter( + 'name', + FieldFilterOperatorEnum.Eq, + 'Hello World Workflow', + ), + expectedResult: 'name: {equal: "Hello World Workflow"}', + }, + { + name: 'returns correct filter for single string field with like operator', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + ], + filter: createFieldFilter( + 'name', + FieldFilterOperatorEnum.Like, + 'Hello%', + ), + expectedResult: 'name: {like: "Hello%"}', + }, + { + name: 'returns correct filter for string field with isNull operator (true)', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + ], + filter: createFieldFilter('name', FieldFilterOperatorEnum.IsNull, true), + expectedResult: 'name: {isNull: true}', + }, + { + name: 'returns correct filter for string field with isNull operator (false)', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + ], + filter: createFieldFilter( + 'name', + FieldFilterOperatorEnum.IsNull, + false, + ), + expectedResult: 'name: {isNull: false}', + }, + { + name: 'returns correct filter for string field with isNull operator ("true" as string)', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + ], + filter: createFieldFilter( + 'name', + FieldFilterOperatorEnum.IsNull, + 'True', + ), + expectedResult: 'name: {isNull: true}', + }, + { + name: 'returns correct filter for string field with isNull operator ("false" as string)', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + ], + filter: createFieldFilter( + 'name', + FieldFilterOperatorEnum.IsNull, + 'FALSE', + ), + expectedResult: 'name: {isNull: false}', + }, + { + name: 'returns correct filter for string field with in operator (single value)', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + ], + filter: createFieldFilter('name', FieldFilterOperatorEnum.In, [ + 'Test String', + ]), + expectedResult: 'name: {in: ["Test String"]}', + }, + { + name: 'returns correct filter for string field with in operator (multiple values)', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + ], + filter: createFieldFilter('name', FieldFilterOperatorEnum.In, [ + 'Test String 1', + 'Test String 2', + 'Test String 3', + ]), + expectedResult: + 'name: {in: ["Test String 1", "Test String 2", "Test String 3"]}', + }, + { + name: 'returns correct OR filter for two string fields with equal operator', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + createIntrospectionField('processName', TypeName.String), + ], + filter: { + operator: 'OR', + filters: [ + createFieldFilter( + 'name', + FieldFilterOperatorEnum.Eq, + 'Hello World Workflow', + ), + createFieldFilter( + 'processName', + FieldFilterOperatorEnum.Eq, + 'Greeting workflow', + ), + ], + }, + expectedResult: + 'or: {name: {equal: "Hello World Workflow"}, processName: {equal: "Greeting workflow"}}', + }, + { + name: 'returns correct filter for string field with like and isNull operators', + introspectionFields: [ + createIntrospectionField('description', TypeName.String), + ], + filter: { + operator: 'OR', + filters: [ + createFieldFilter( + 'description', + FieldFilterOperatorEnum.Like, + '%Test%', + ), + createFieldFilter( + 'description', + FieldFilterOperatorEnum.IsNull, + true, + ), + ], + }, + expectedResult: + 'or: {description: {like: "%Test%"}, description: {isNull: true}}', + }, + { + name: 'returns correct filter for string field with in, like, equal, and isNull operators', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + ], + filter: { + operator: 'OR', + filters: [ + createFieldFilter('name', FieldFilterOperatorEnum.In, [ + 'Test String 1', + 'Test String 2', + ]), + createFieldFilter('name', FieldFilterOperatorEnum.Like, '%Test%'), + createFieldFilter( + 'name', + FieldFilterOperatorEnum.Eq, + 'Exact Match', + ), + createFieldFilter('name', FieldFilterOperatorEnum.IsNull, false), + ], + }, + expectedResult: + 'or: {name: {in: ["Test String 1", "Test String 2"]}, name: {like: "%Test%"}, name: {equal: "Exact Match"}, name: {isNull: false}}', + }, + { + name: 'returns correct filter for string field with in, like, equal, and isNull operators', + introspectionFields: [ + createIntrospectionField('name', TypeName.String), + ], + filter: { + operator: 'AND', + filters: [ + createFieldFilter('name', FieldFilterOperatorEnum.In, [ + 'Test String 1', + 'Test String 2', + ]), + createFieldFilter('name', FieldFilterOperatorEnum.Like, '%Test%'), + createFieldFilter( + 'name', + FieldFilterOperatorEnum.Eq, + 'Exact Match', + ), + createFieldFilter('name', FieldFilterOperatorEnum.IsNull, false), + ], + }, + expectedResult: + 'and: {name: {in: ["Test String 1", "Test String 2"]}, name: {like: "%Test%"}, name: {equal: "Exact Match"}, name: {isNull: false}}', + }, + ]; + stringTestCases.forEach( + ({ name, introspectionFields, filter, expectedResult }) => { + it(`${name}`, () => { + const result = buildFilterCondition(introspectionFields, filter); + expect(result).toBe(expectedResult); + }); + }, + ); + }); + describe('idArgument testcases', () => { + const idTestCases: FilterTestCase[] = [ + { + name: 'returns correct filter for single id field with equal operator', + introspectionFields: [createIntrospectionField('id', TypeName.Id)], + filter: createFieldFilter('id', FieldFilterOperatorEnum.Eq, 'idA'), + expectedResult: 'id: {equal: "idA"}', + }, + { + name: 'returns correct filter for single id field with isNull operator (false as boolean)', + introspectionFields: [createIntrospectionField('id', TypeName.Id)], + filter: createFieldFilter('id', FieldFilterOperatorEnum.IsNull, false), + expectedResult: 'id: {isNull: false}', + }, + { + name: 'returns correct filter for single id field with isNull operator (false as string)', + introspectionFields: [createIntrospectionField('id', TypeName.Id)], + filter: createFieldFilter( + 'id', + FieldFilterOperatorEnum.IsNull, + 'false', + ), + expectedResult: 'id: {isNull: false}', + }, + { + name: 'returns correct filter for single id field with IN operator', + introspectionFields: [createIntrospectionField('id', TypeName.Id)], + filter: createFieldFilter('id', FieldFilterOperatorEnum.In, [ + 'idA', + 'idB', + 'idC', + ]), + expectedResult: 'id: {in: ["idA", "idB", "idC"]}', + }, + { + name: 'returns correct OR filter for multiple id fields with equal, isNull, and IN operators', + introspectionFields: [ + createIntrospectionField('processId', TypeName.Id), + createIntrospectionField('id', TypeName.Id), + ], + filter: { + operator: 'OR', + filters: [ + createFieldFilter('id', FieldFilterOperatorEnum.Eq, 'idA'), + createFieldFilter( + 'processId', + FieldFilterOperatorEnum.IsNull, + 'True', + ), + createFieldFilter('id', 'IN', ['idA', 'idB', 'idC']), + ], + }, + expectedResult: + 'or: {id: {equal: "idA"}, processId: {isNull: true}, id: {in: ["idA", "idB", "idC"]}}', + }, + { + name: 'returns correct AND filter for multiple id fields with equal, isNull, and IN operators', + introspectionFields: [ + createIntrospectionField('processId', TypeName.Id), + createIntrospectionField('id', TypeName.Id), + ], + filter: { + operator: 'AND', + filters: [ + createFieldFilter('id', FieldFilterOperatorEnum.Eq, 'idA'), + createFieldFilter( + 'processId', + FieldFilterOperatorEnum.IsNull, + 'True', + ), + createFieldFilter('id', 'IN', ['idA', 'idB', 'idC']), + ], + }, + expectedResult: + 'and: {id: {equal: "idA"}, processId: {isNull: true}, id: {in: ["idA", "idB", "idC"]}}', + }, + ]; + + idTestCases.forEach( + ({ name, introspectionFields, filter, expectedResult }) => { + it(`${name}`, () => { + const result = buildFilterCondition(introspectionFields, filter); + expect(result).toBe(expectedResult); + }); + }, + ); + }); + describe('dateArgument testcases', () => { + const testDate1 = '2024-10-10T09:54:40.759Z'; + const testDate2 = '2025-10-10T09:54:40.759Z'; + + const idTestCases: FilterTestCase[] = [ + { + name: 'returns correct filter for single date field with equal operator', + introspectionFields: [createIntrospectionField('start', TypeName.Date)], + filter: createFieldFilter( + 'start', + FieldFilterOperatorEnum.Eq, + testDate1, + ), + expectedResult: `start: {equal: "${testDate1}"}`, + }, + { + name: 'returns correct filter for single date field with isNull operator (false as boolean)', + introspectionFields: [createIntrospectionField('start', TypeName.Date)], + filter: createFieldFilter( + 'start', + FieldFilterOperatorEnum.IsNull, + false, + ), + expectedResult: 'start: {isNull: false}', + }, + { + name: 'returns correct filter for single date field with isNull operator (false as string)', + introspectionFields: [createIntrospectionField('start', TypeName.Date)], + filter: createFieldFilter( + 'start', + FieldFilterOperatorEnum.IsNull, + 'false', + ), + expectedResult: 'start: {isNull: false}', + }, + { + name: 'returns correct filter for single date field with GT operator', + introspectionFields: [createIntrospectionField('start', TypeName.Date)], + filter: createFieldFilter( + 'start', + FieldFilterOperatorEnum.Gt, + testDate1, + ), + expectedResult: `start: {greaterThan: "${testDate1}"}`, + }, + { + name: 'returns correct filter for single date field with GTE operator', + introspectionFields: [createIntrospectionField('start', TypeName.Date)], + filter: createFieldFilter( + 'start', + FieldFilterOperatorEnum.Gte, + testDate1, + ), + expectedResult: `start: {greaterThanEqual: "${testDate1}"}`, + }, + { + name: 'returns correct filter for single date field with LT operator', + introspectionFields: [createIntrospectionField('start', TypeName.Date)], + filter: createFieldFilter( + 'start', + FieldFilterOperatorEnum.Lt, + testDate1, + ), + expectedResult: `start: {lessThan: "${testDate1}"}`, + }, + { + name: 'returns correct filter for single date field with LTE operator', + introspectionFields: [createIntrospectionField('start', TypeName.Date)], + filter: createFieldFilter( + 'start', + FieldFilterOperatorEnum.Lte, + testDate1, + ), + expectedResult: `start: {lessThanEqual: "${testDate1}"}`, + }, + { + name: 'returns correct filter for single date field with BETWEEN operator', + introspectionFields: [createIntrospectionField('start', TypeName.Date)], + filter: createFieldFilter('start', FieldFilterOperatorEnum.Between, [ + testDate1, + testDate2, + ]), + expectedResult: `start: {between: {from: "${testDate1}", to: "${testDate2}"}}`, + }, + { + name: 'returns correct OR filter for multiple id fields with equal, isNull, and GT operators', + introspectionFields: [ + createIntrospectionField('start', TypeName.Date), + createIntrospectionField('end', TypeName.Date), + ], + filter: { + operator: 'OR', + filters: [ + createFieldFilter('start', FieldFilterOperatorEnum.Eq, testDate1), + createFieldFilter('end', FieldFilterOperatorEnum.IsNull, 'False'), + createFieldFilter('end', FieldFilterOperatorEnum.Gt, testDate1), + ], + }, + expectedResult: `or: {start: {equal: "${testDate1}"}, end: {isNull: false}, end: {greaterThan: "${testDate1}"}}`, + }, + { + name: 'returns correct OR filter for multiple id fields with equal, isNull, and GTE operators', + introspectionFields: [ + createIntrospectionField('start', TypeName.Date), + createIntrospectionField('end', TypeName.Date), + ], + filter: { + operator: 'OR', + filters: [ + createFieldFilter('start', FieldFilterOperatorEnum.Eq, testDate1), + createFieldFilter('end', FieldFilterOperatorEnum.IsNull, 'False'), + createFieldFilter('end', FieldFilterOperatorEnum.Gte, testDate1), + ], + }, + expectedResult: `or: {start: {equal: "${testDate1}"}, end: {isNull: false}, end: {greaterThanEqual: "${testDate1}"}}`, + }, + { + name: 'returns correct AND filter for multiple id fields with equal, isNull, and LTE operators', + introspectionFields: [ + createIntrospectionField('start', TypeName.Date), + createIntrospectionField('end', TypeName.Date), + ], + filter: { + operator: 'AND', + filters: [ + createFieldFilter('start', FieldFilterOperatorEnum.Eq, testDate1), + createFieldFilter('end', FieldFilterOperatorEnum.IsNull, 'False'), + createFieldFilter('end', FieldFilterOperatorEnum.Lte, testDate1), + ], + }, + expectedResult: `and: {start: {equal: "${testDate1}"}, end: {isNull: false}, end: {lessThanEqual: "${testDate1}"}}`, + }, + { + name: 'returns correct AND filter for multiple id fields with equal, isNull, LTE, and between operators', + introspectionFields: [ + createIntrospectionField('start', TypeName.Date), + createIntrospectionField('end', TypeName.Date), + ], + filter: { + operator: 'AND', + filters: [ + createFieldFilter('start', FieldFilterOperatorEnum.Eq, testDate1), + createFieldFilter('end', FieldFilterOperatorEnum.IsNull, 'False'), + createFieldFilter('end', FieldFilterOperatorEnum.Lte, testDate1), + createFieldFilter('start', FieldFilterOperatorEnum.Between, [ + testDate1, + testDate2, + ]), + ], + }, + expectedResult: `and: {start: {equal: "${testDate1}"}, end: {isNull: false}, end: {lessThanEqual: "${testDate1}"}, start: {between: {from: "${testDate1}", to: "${testDate2}"}}}`, + }, + ]; + + idTestCases.forEach( + ({ name, introspectionFields, filter, expectedResult }) => { + it(`${name}`, () => { + const result = buildFilterCondition(introspectionFields, filter); + expect(result).toBe(expectedResult); + }); + }, + ); + }); +}); diff --git a/plugins/orchestrator-backend/src/helpers/queryBuilder.test.ts b/plugins/orchestrator-backend/src/helpers/queryBuilder.test.ts index 7e494ac950..ac1f41c600 100644 --- a/plugins/orchestrator-backend/src/helpers/queryBuilder.test.ts +++ b/plugins/orchestrator-backend/src/helpers/queryBuilder.test.ts @@ -1,13 +1,5 @@ -import { - FieldFilterOperatorEnum, - Filter, - IntrospectionField, - TypeKind, - TypeName, -} from '@janus-idp/backstage-plugin-orchestrator-common'; - import { Pagination } from '../types/pagination'; -import { buildFilterCondition, buildGraphQlQuery } from './queryBuilder'; +import { buildGraphQlQuery } from './queryBuilder'; describe('buildGraphQlQuery', () => { const defaultTestParams = { @@ -88,324 +80,3 @@ describe('buildGraphQlQuery', () => { }); }); }); - -describe('column filters', () => { - const createStringIntrospectionField = ( - name: string, - ): IntrospectionField => ({ - name, - type: { - name: TypeName.String, - kind: TypeKind.InputObject, - ofType: null, - }, - }); - - const createIdIntrospectionField = (name: string): IntrospectionField => ({ - name, - type: { - name: TypeName.Id, - kind: TypeKind.InputObject, - ofType: null, - }, - }); - - const createFieldFilter = ( - field: string, - operator: FieldFilterOperatorEnum, - value: any, - ): Filter => ({ - field, - operator, - value, - }); - - type FilterTestCase = { - name: string; - introspectionFields: IntrospectionField[]; - filter: Filter | undefined; - expectedResult: string; - }; - describe('empty filter testcases', () => { - const emptyFilterTestCases: FilterTestCase[] = [ - { - name: 'returns empty string when filters are null or undefined', - introspectionFields: [], - filter: undefined, - expectedResult: '', - }, - ]; - emptyFilterTestCases.forEach( - ({ name, introspectionFields, filter, expectedResult }) => { - it(`${name}`, () => { - const result = buildFilterCondition(introspectionFields, filter); - expect(result).toBe(expectedResult); - }); - }, - ); - }); - describe('stringArgument testcases', () => { - const stringTestCases: FilterTestCase[] = [ - { - name: 'returns correct filter for single string field with equal operator', - introspectionFields: [createStringIntrospectionField('name')], - filter: createFieldFilter( - 'name', - FieldFilterOperatorEnum.Eq, - 'Hello World Workflow', - ), - expectedResult: 'name: {equal: "Hello World Workflow"}', - }, - { - name: 'returns correct filter for single string field with like operator', - introspectionFields: [createStringIntrospectionField('name')], - filter: createFieldFilter( - 'name', - FieldFilterOperatorEnum.Like, - 'Hello%', - ), - expectedResult: 'name: {like: "Hello%"}', - }, - { - name: 'returns correct filter for string field with isNull operator (true)', - introspectionFields: [createStringIntrospectionField('name')], - filter: createFieldFilter('name', FieldFilterOperatorEnum.IsNull, true), - expectedResult: 'name: {isNull: true}', - }, - { - name: 'returns correct filter for string field with isNull operator (false)', - introspectionFields: [createStringIntrospectionField('name')], - filter: createFieldFilter( - 'name', - FieldFilterOperatorEnum.IsNull, - false, - ), - expectedResult: 'name: {isNull: false}', - }, - { - name: 'returns correct filter for string field with isNull operator ("true" as string)', - introspectionFields: [createStringIntrospectionField('name')], - filter: createFieldFilter( - 'name', - FieldFilterOperatorEnum.IsNull, - 'True', - ), - expectedResult: 'name: {isNull: true}', - }, - { - name: 'returns correct filter for string field with isNull operator ("false" as string)', - introspectionFields: [createStringIntrospectionField('name')], - filter: createFieldFilter( - 'name', - FieldFilterOperatorEnum.IsNull, - 'FALSE', - ), - expectedResult: 'name: {isNull: false}', - }, - { - name: 'returns correct filter for string field with in operator (single value)', - introspectionFields: [createStringIntrospectionField('name')], - filter: createFieldFilter('name', FieldFilterOperatorEnum.In, [ - 'Test String', - ]), - expectedResult: 'name: {in: ["Test String"]}', - }, - { - name: 'returns correct filter for string field with in operator (multiple values)', - introspectionFields: [createStringIntrospectionField('name')], - filter: createFieldFilter('name', FieldFilterOperatorEnum.In, [ - 'Test String 1', - 'Test String 2', - 'Test String 3', - ]), - expectedResult: - 'name: {in: ["Test String 1", "Test String 2", "Test String 3"]}', - }, - { - name: 'returns correct OR filter for two string fields with equal operator', - introspectionFields: [ - createStringIntrospectionField('name'), - createStringIntrospectionField('processName'), - ], - filter: { - operator: 'OR', - filters: [ - createFieldFilter( - 'name', - FieldFilterOperatorEnum.Eq, - 'Hello World Workflow', - ), - createFieldFilter( - 'processName', - FieldFilterOperatorEnum.Eq, - 'Greeting workflow', - ), - ], - }, - expectedResult: - 'or: {name: {equal: "Hello World Workflow"}, processName: {equal: "Greeting workflow"}}', - }, - { - name: 'returns correct filter for string field with like and isNull operators', - introspectionFields: [createStringIntrospectionField('description')], - filter: { - operator: 'OR', - filters: [ - createFieldFilter( - 'description', - FieldFilterOperatorEnum.Like, - '%Test%', - ), - createFieldFilter( - 'description', - FieldFilterOperatorEnum.IsNull, - true, - ), - ], - }, - expectedResult: - 'or: {description: {like: "%Test%"}, description: {isNull: true}}', - }, - { - name: 'returns correct filter for string field with in, like, equal, and isNull operators', - introspectionFields: [createStringIntrospectionField('name')], - filter: { - operator: 'OR', - filters: [ - createFieldFilter('name', FieldFilterOperatorEnum.In, [ - 'Test String 1', - 'Test String 2', - ]), - createFieldFilter('name', FieldFilterOperatorEnum.Like, '%Test%'), - createFieldFilter( - 'name', - FieldFilterOperatorEnum.Eq, - 'Exact Match', - ), - createFieldFilter('name', FieldFilterOperatorEnum.IsNull, false), - ], - }, - expectedResult: - 'or: {name: {in: ["Test String 1", "Test String 2"]}, name: {like: "%Test%"}, name: {equal: "Exact Match"}, name: {isNull: false}}', - }, - { - name: 'returns correct filter for string field with in, like, equal, and isNull operators', - introspectionFields: [createStringIntrospectionField('name')], - filter: { - operator: 'AND', - filters: [ - createFieldFilter('name', FieldFilterOperatorEnum.In, [ - 'Test String 1', - 'Test String 2', - ]), - createFieldFilter('name', FieldFilterOperatorEnum.Like, '%Test%'), - createFieldFilter( - 'name', - FieldFilterOperatorEnum.Eq, - 'Exact Match', - ), - createFieldFilter('name', FieldFilterOperatorEnum.IsNull, false), - ], - }, - expectedResult: - 'and: {name: {in: ["Test String 1", "Test String 2"]}, name: {like: "%Test%"}, name: {equal: "Exact Match"}, name: {isNull: false}}', - }, - ]; - stringTestCases.forEach( - ({ name, introspectionFields, filter, expectedResult }) => { - it(`${name}`, () => { - const result = buildFilterCondition(introspectionFields, filter); - expect(result).toBe(expectedResult); - }); - }, - ); - }); - - describe('idArgument testcases', () => { - const idTestCases: FilterTestCase[] = [ - { - name: 'returns correct filter for single id field with equal operator', - introspectionFields: [createIdIntrospectionField('id')], - filter: createFieldFilter('id', FieldFilterOperatorEnum.Eq, 'idA'), - expectedResult: 'id: {equal: "idA"}', - }, - { - name: 'returns correct filter for single id field with isNull operator (false as boolean)', - introspectionFields: [createIdIntrospectionField('id')], - filter: createFieldFilter('id', FieldFilterOperatorEnum.IsNull, false), - expectedResult: 'id: {isNull: false}', - }, - { - name: 'returns correct filter for single id field with isNull operator (false as string)', - introspectionFields: [createIdIntrospectionField('id')], - filter: createFieldFilter( - 'id', - FieldFilterOperatorEnum.IsNull, - 'false', - ), - expectedResult: 'id: {isNull: false}', - }, - { - name: 'returns correct filter for single id field with IN operator', - introspectionFields: [createIdIntrospectionField('id')], - filter: createFieldFilter('id', FieldFilterOperatorEnum.In, [ - 'idA', - 'idB', - 'idC', - ]), - expectedResult: 'id: {in: ["idA", "idB", "idC"]}', - }, - { - name: 'returns correct OR filter for multiple id fields with equal, isNull, and IN operators', - introspectionFields: [ - createIdIntrospectionField('processId'), - createIdIntrospectionField('id'), - ], - filter: { - operator: 'OR', - filters: [ - createFieldFilter('id', FieldFilterOperatorEnum.Eq, 'idA'), - createFieldFilter( - 'processId', - FieldFilterOperatorEnum.IsNull, - 'True', - ), - createFieldFilter('id', 'IN', ['idA', 'idB', 'idC']), - ], - }, - expectedResult: - 'or: {id: {equal: "idA"}, processId: {isNull: true}, id: {in: ["idA", "idB", "idC"]}}', - }, - { - name: 'returns correct AND filter for multiple id fields with equal, isNull, and IN operators', - introspectionFields: [ - createIdIntrospectionField('processId'), - createIdIntrospectionField('id'), - ], - filter: { - operator: 'AND', - filters: [ - createFieldFilter('id', FieldFilterOperatorEnum.Eq, 'idA'), - createFieldFilter( - 'processId', - FieldFilterOperatorEnum.IsNull, - 'True', - ), - createFieldFilter('id', 'IN', ['idA', 'idB', 'idC']), - ], - }, - expectedResult: - 'and: {id: {equal: "idA"}, processId: {isNull: true}, id: {in: ["idA", "idB", "idC"]}}', - }, - ]; - - idTestCases.forEach( - ({ name, introspectionFields, filter, expectedResult }) => { - it(`${name}`, () => { - const result = buildFilterCondition(introspectionFields, filter); - expect(result).toBe(expectedResult); - }); - }, - ); - }); -}); diff --git a/plugins/orchestrator-backend/src/helpers/queryBuilder.ts b/plugins/orchestrator-backend/src/helpers/queryBuilder.ts index 03163a93e1..01c28ef285 100644 --- a/plugins/orchestrator-backend/src/helpers/queryBuilder.ts +++ b/plugins/orchestrator-backend/src/helpers/queryBuilder.ts @@ -1,11 +1,3 @@ -import { - FieldFilterOperatorEnum, - Filter, - IntrospectionField, - LogicalFilter, - TypeName, -} from '@janus-idp/backstage-plugin-orchestrator-common'; - import { Pagination } from '../types/pagination'; export function buildGraphQlQuery(args: { @@ -58,159 +50,3 @@ function buildPaginationClause(pagination?: Pagination): string { return parts.join(', '); } - -function isLogicalFilter(filter: Filter): filter is LogicalFilter { - return (filter as LogicalFilter).filters !== undefined; -} - -export function buildFilterCondition( - introspection: IntrospectionField[], - filters?: Filter, -): string { - if (!filters) { - return ''; - } - - if (isLogicalFilter(filters)) { - if (filters.operator) { - const subClauses = - filters.filters.map(f => buildFilterCondition(introspection, f)) ?? []; - const joinedSubClauses = `${filters.operator.toLowerCase()}: {${subClauses.join(', ')}}`; - return joinedSubClauses; - } - } - - if (!isOperatorSupported(filters.operator)) { - throw new Error(`Unsopported operator ${filters.operator}`); - } - - const fieldDef = introspection.find(f => f.name === filters.field); - if (!fieldDef) { - throw new Error(`Can't find field "${filters.field}" definition`); - } - - if (!isOperatorAllowedForField(filters.operator, fieldDef)) { - throw new Error(`Unsupported field type ${fieldDef.type.name}`); - } - - let value = filters.value; - - if (filters.operator === FieldFilterOperatorEnum.IsNull) { - return `${filters.field}: {${getGraphQLOperator(filters.operator)}: ${convertToBoolean(value)}}`; - } - - if (Array.isArray(value)) { - value = `[${value.map(v => formatValue(filters.field, v, fieldDef)).join(', ')}]`; - } else { - value = formatValue(filters.field, value, fieldDef); - } - - if ( - filters.operator === FieldFilterOperatorEnum.Eq || - filters.operator === FieldFilterOperatorEnum.Like || - filters.operator === FieldFilterOperatorEnum.In - ) { - return `${filters.field}: {${getGraphQLOperator(filters.operator)}: ${value}}`; - } - - throw new Error(`Can't build filter condition`); -} - -function isOperatorSupported(operator: FieldFilterOperatorEnum): boolean { - return ( - operator === FieldFilterOperatorEnum.Eq || - operator === FieldFilterOperatorEnum.Like || - operator === FieldFilterOperatorEnum.In || - operator === FieldFilterOperatorEnum.IsNull - ); -} - -function isFieldFilterSupported(fieldDef: IntrospectionField): boolean { - return fieldDef?.type.name === TypeName.String; -} - -function isOperatorAllowedForField( - operator: FieldFilterOperatorEnum, - fieldDef: IntrospectionField, -): boolean { - const allowedOperators: Record = { - [TypeName.String]: [ - FieldFilterOperatorEnum.In, - FieldFilterOperatorEnum.Like, - FieldFilterOperatorEnum.IsNull, - FieldFilterOperatorEnum.Eq, - ], - [TypeName.Id]: [ - FieldFilterOperatorEnum.In, - FieldFilterOperatorEnum.IsNull, - FieldFilterOperatorEnum.Eq, - ], - [TypeName.Date]: [], - [TypeName.StringArray]: [], - }; - const allowedForType = allowedOperators[fieldDef.type.name]; - return allowedForType ? allowedForType.includes(operator) : false; -} - -function convertToBoolean(value: any): boolean { - if (typeof value === 'boolean') { - return value; - } - if (typeof value === 'string') { - return value.toLowerCase() === 'true'; - } - if (typeof value === 'number') { - return value === 1; - } - return false; // Default to false for unsupported types -} - -function formatValue( - fieldName: string, - fieldValue: any, - fieldDef: IntrospectionField, -): string { - if (!isFieldFilterSupported) { - throw new Error(`Unsupported field type ${fieldDef.type.name}`); - } - - if ( - fieldDef.type.name === TypeName.String || - fieldDef.type.name === TypeName.Id - ) { - return `"${fieldValue}"`; - } - throw new Error( - `Failed to format value for ${fieldName} ${fieldValue} with type ${fieldDef.type.name}`, - ); -} - -function getGraphQLOperator(operator: FieldFilterOperatorEnum): string { - switch (operator) { - case 'EQ': - return 'equal'; - case 'LIKE': - return 'like'; - case 'IN': - return 'in'; - case 'IS_NULL': - return 'isNull'; - // case 'GT': - // // return "greaterThan" - // case 'GTE': - // return "greaterThanEqual" - // case 'LT': - // return "lessThan" - // case 'LTE': - // return "lessThanEqual" - // case 'CONTAINS': - // return "contains" - // case 'CONTAINS_ALL': - // case 'CONTAINS_ANY': - // case 'BETWEEN': - // case 'FROM': - // case 'TO': - default: - throw new Error(`Operation "${operator}" not supported`); - } -} diff --git a/plugins/orchestrator-backend/src/service/DataIndexService.test.ts b/plugins/orchestrator-backend/src/service/DataIndexService.test.ts index 4fceb218ea..58c83eb341 100644 --- a/plugins/orchestrator-backend/src/service/DataIndexService.test.ts +++ b/plugins/orchestrator-backend/src/service/DataIndexService.test.ts @@ -12,7 +12,8 @@ import { WorkflowInfo, } from '@janus-idp/backstage-plugin-orchestrator-common'; -import * as graphqlUtils from '../helpers/queryBuilder'; +import * as buildGrahQLFilterUtils from '../helpers/filterBuilder'; +import * as buildGrahQLQueryUtils from '../helpers/queryBuilder'; import { Pagination } from '../types/pagination'; import { mockProcessDefinitionArguments, @@ -31,6 +32,13 @@ jest.mock('../helpers/queryBuilder', () => { }; }); +jest.mock('../helpers/filterBuilder', () => { + return { + __esModule: true, + ...jest.requireActual('../helpers/filterBuilder'), + }; +}); + jest.mock('@urql/core', () => { return { Client: jest.fn().mockImplementation(() => ({ @@ -193,8 +201,14 @@ describe('fetchWorkflowInfos', () => { dataIndexService = new DataIndexService('fakeUrl', loggerMock); // Set up spies on the graphql utility functions - buildGraphQlQuerySpy = jest.spyOn(graphqlUtils, 'buildGraphQlQuery'); - buildFilterConditionSpy = jest.spyOn(graphqlUtils, 'buildFilterCondition'); + buildGraphQlQuerySpy = jest.spyOn( + buildGrahQLQueryUtils, + 'buildGraphQlQuery', + ); + buildFilterConditionSpy = jest.spyOn( + buildGrahQLFilterUtils, + 'buildFilterCondition', + ); }); it('should fetch workflow infos with no parameters', async () => { // Given @@ -219,7 +233,7 @@ describe('fetchWorkflowInfos', () => { }); expect(mockClient.query).toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); }); @@ -256,7 +270,7 @@ describe('fetchWorkflowInfos', () => { expect(buildFilterConditionSpy).not.toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); }); @@ -294,7 +308,7 @@ describe('fetchWorkflowInfos', () => { }); expect(mockClient.query).toHaveBeenCalledTimes(1); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); expect(buildFilterConditionSpy).not.toHaveBeenCalled(); @@ -339,7 +353,7 @@ describe('fetchWorkflowInfos', () => { ); expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); }); @@ -384,7 +398,7 @@ describe('fetchWorkflowInfos', () => { ); expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); expect(result).toBeDefined(); @@ -421,7 +435,7 @@ describe('fetchWorkflowInfos', () => { expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(2); @@ -514,8 +528,14 @@ describe('fetchInstances', () => { // Create a spy for method1 jest.spyOn(dataIndexService, 'fetchWorkflowInfo').mockResolvedValue(wfInfo); // Set up spies on the graphql utility functions - buildGraphQlQuerySpy = jest.spyOn(graphqlUtils, 'buildGraphQlQuery'); - buildFilterConditionSpy = jest.spyOn(graphqlUtils, 'buildFilterCondition'); + buildGraphQlQuerySpy = jest.spyOn( + buildGrahQLQueryUtils, + 'buildGraphQlQuery', + ); + buildFilterConditionSpy = jest.spyOn( + buildGrahQLFilterUtils, + 'buildFilterCondition', + ); // Clear mocks before each test jest.clearAllMocks(); @@ -549,7 +569,7 @@ describe('fetchInstances', () => { expect(buildFilterConditionSpy).not.toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); }); @@ -583,7 +603,7 @@ describe('fetchInstances', () => { expect(buildFilterConditionSpy).not.toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); expect(result).toBeDefined(); @@ -624,7 +644,7 @@ describe('fetchInstances', () => { expect(buildFilterConditionSpy).not.toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalledTimes(1); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); }); @@ -662,7 +682,7 @@ describe('fetchInstances', () => { ); expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); }); @@ -698,7 +718,7 @@ describe('fetchInstances', () => { ); expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); expect(result).toBeDefined(); @@ -739,7 +759,7 @@ describe('fetchInstances', () => { ); expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + buildGrahQLQueryUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); expect(result).toBeDefined(); diff --git a/plugins/orchestrator-backend/src/service/DataIndexService.ts b/plugins/orchestrator-backend/src/service/DataIndexService.ts index ac9201aa11..ee44c19bcd 100644 --- a/plugins/orchestrator-backend/src/service/DataIndexService.ts +++ b/plugins/orchestrator-backend/src/service/DataIndexService.ts @@ -15,10 +15,8 @@ import { } from '@janus-idp/backstage-plugin-orchestrator-common'; import { ErrorBuilder } from '../helpers/errorBuilder'; -import { - buildFilterCondition, - buildGraphQlQuery, -} from '../helpers/queryBuilder'; +import { buildFilterCondition } from '../helpers/filterBuilder'; +import { buildGraphQlQuery } from '../helpers/queryBuilder'; import { Pagination } from '../types/pagination'; import { FETCH_PROCESS_INSTANCES_SORT_FIELD } from './constants'; diff --git a/plugins/orchestrator-backend/src/service/__fixtures__/mockProcessInstanceArgumentsData.ts b/plugins/orchestrator-backend/src/service/__fixtures__/mockProcessInstanceArgumentsData.ts index 089b0c6ec2..3750cebf72 100644 --- a/plugins/orchestrator-backend/src/service/__fixtures__/mockProcessInstanceArgumentsData.ts +++ b/plugins/orchestrator-backend/src/service/__fixtures__/mockProcessInstanceArgumentsData.ts @@ -145,22 +145,22 @@ export const mockProcessInstanceArguments = { // ofType: null, // }, // }, - // { - // name: 'start', - // type: { - // kind: 'INPUT_OBJECT', - // name: 'DateArgument', - // ofType: null, - // }, - // }, - // { - // name: 'end', - // type: { - // kind: 'INPUT_OBJECT', - // name: 'DateArgument', - // ofType: null, - // }, - // }, + { + name: 'start', + type: { + kind: 'INPUT_OBJECT', + name: 'DateArgument', + ofType: null, + }, + }, + { + name: 'end', + type: { + kind: 'INPUT_OBJECT', + name: 'DateArgument', + ofType: null, + }, + }, // { // name: 'addons', // type: { @@ -169,14 +169,14 @@ export const mockProcessInstanceArguments = { // ofType: null, // }, // }, - // { - // name: 'lastUpdate', - // type: { - // kind: 'INPUT_OBJECT', - // name: 'DateArgument', - // ofType: null, - // }, - // }, + { + name: 'lastUpdate', + type: { + kind: 'INPUT_OBJECT', + name: 'DateArgument', + ofType: null, + }, + }, { name: 'businessKey', type: { @@ -295,22 +295,22 @@ export const mockProcessInstanceIntrospection: IntrospectionField[] = [ // ofType: null, // }, // }, - // { - // name: 'start', - // type: { - // kind: TypeKind.InputObject, - // name: TypeName.Date, - // ofType: null, - // }, - // }, - // { - // name: 'end', - // type: { - // kind: TypeKind.InputObject, - // name: TypeName.Date, - // ofType: null, - // }, - // }, + { + name: 'start', + type: { + kind: TypeKind.InputObject, + name: TypeName.Date, + ofType: null, + }, + }, + { + name: 'end', + type: { + kind: TypeKind.InputObject, + name: TypeName.Date, + ofType: null, + }, + }, // { // name: 'addons', // type: { @@ -319,14 +319,14 @@ export const mockProcessInstanceIntrospection: IntrospectionField[] = [ // ofType: null, // }, // }, - // { - // name: 'lastUpdate', - // type: { - // kind: TypeKind.InputObject, - // name: TypeName.Date, - // ofType: null, - // }, - // }, + { + name: 'lastUpdate', + type: { + kind: TypeKind.InputObject, + name: TypeName.Date, + ofType: null, + }, + }, { name: 'businessKey', type: { diff --git a/plugins/orchestrator-common/package.json b/plugins/orchestrator-common/package.json index d428ab530b..f5f42074ec 100644 --- a/plugins/orchestrator-common/package.json +++ b/plugins/orchestrator-common/package.json @@ -4,7 +4,6 @@ "license": "Apache-2.0", "main": "src/index.ts", "types": "src/index.ts", - "private": true, "publishConfig": { "access": "public", "main": "dist/index.cjs.js", diff --git a/plugins/orchestrator-common/src/generated/.METADATA.sha1 b/plugins/orchestrator-common/src/generated/.METADATA.sha1 index 3f69156f77..1db4545fe5 100644 --- a/plugins/orchestrator-common/src/generated/.METADATA.sha1 +++ b/plugins/orchestrator-common/src/generated/.METADATA.sha1 @@ -1 +1 @@ -795882d18a4731f0a4ba9225cf77929ee0ac6628 +eb1b90ece179825f2e691e589da72e4376d3549c diff --git a/plugins/orchestrator-common/src/generated/api/definition.ts b/plugins/orchestrator-common/src/generated/api/definition.ts index 898a8a4cb9..f838e26735 100644 --- a/plugins/orchestrator-common/src/generated/api/definition.ts +++ b/plugins/orchestrator-common/src/generated/api/definition.ts @@ -1,5 +1,5 @@ /* eslint-disable */ /* prettier-ignore */ // GENERATED FILE DO NOT EDIT. -const OPENAPI = `{"openapi":"3.1.0","info":{"title":"Orchestratorplugin","description":"APItointeractwithorchestratorplugin","license":{"name":"Apache2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"version":"0.0.1"},"servers":[{"url":"/"}],"paths":{"/v2/workflows/overview":{"post":{"operationId":"getWorkflowsOverview","description":"Returnsthekeyfieldsoftheworkflowincludingdataonthelastruninstance","requestBody":{"required":false,"description":"Paginationandfilters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowOverviewListResultDTO"}}}},"500":{"description":"Errorfetchingworkflowoverviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/overview":{"get":{"operationId":"getWorkflowOverviewById","description":"Returnsthekeyfieldsoftheworkflowincludingdataonthelastruninstance","parameters":[{"name":"workflowId","in":"path","required":true,"description":"Uniqueidentifieroftheworkflow","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowOverviewDTO"}}}},"500":{"description":"Errorfetchingworkflowoverview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}":{"get":{"operationId":"getWorkflowById","description":"Getfullworkflowinfo","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowDTO"}}}},"500":{"description":"Errorfetchingworkflowbyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/source":{"get":{"operationId":"getWorkflowSourceById","description":"Gettheworkflow'sdefinition","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Errorfetchingworkflowsourcebyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/inputSchema":{"get":{"operationId":"getWorkflowInputSchemaById","description":"Gettheworkflowinputschema.Itdefinestheinputfieldsoftheworkflow","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}},{"name":"instanceId","in":"query","description":"IDofinstance","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InputSchemaResponseDTO"}}}},"500":{"description":"Errorfetchingworkflowinputschemabyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances":{"post":{"operationId":"getInstances","summary":"Getinstances","description":"Retrieveanarrayofworkflowexecutions(instances)","requestBody":{"required":false,"description":"Parametersforretrievinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetInstancesRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessInstanceListResultDTO"}}}},"500":{"description":"Errorfetchinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/instances":{"post":{"operationId":"getWorkflowInstances","summary":"Getinstancesforaspecificworkflow","description":"Retrieveanarrayofworkflowexecutions(instances)forthegivenworkflow","parameters":[{"name":"workflowId","in":"path","required":true,"description":"IDoftheworkflow","schema":{"type":"string"}}],"requestBody":{"required":false,"description":"Parametersforretrievingworkflowinstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessInstanceListResultDTO"}}}},"500":{"description":"Errorfetchinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/{instanceId}":{"get":{"summary":"GetWorkflowInstancebyID","description":"Getaworkflowexecution/run(instance)","operationId":"getInstanceById","parameters":[{"name":"instanceId","in":"path","required":true,"description":"IDoftheworkflowinstance","schema":{"type":"string"}},{"name":"includeAssessment","in":"query","required":false,"description":"Whethertoincludeassessment","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Successfulresponse","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssessedProcessInstanceDTO"}}}},"500":{"description":"Errorfetchinginstance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/statuses":{"get":{"operationId":"getWorkflowStatuses","summary":"Getworkflowstatuslist","description":"Retrievearraywiththestatusofallinstances","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowRunStatusDTO"}}}}},"500":{"description":"Errorfetchingworkflowstatuses","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/execute":{"post":{"summary":"Executeaworkflow","description":"Executeaworkflow","operationId":"executeWorkflow","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtoexecute","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteWorkflowRequestDTO"}}}},"responses":{"200":{"description":"Successfulexecution","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteWorkflowResponseDTO"}}}},"500":{"description":"InternalServerError","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/{instanceId}/abort":{"delete":{"summary":"Abortaworkflowinstance","operationId":"abortWorkflow","description":"AbortsaworkflowinstanceidentifiedbytheprovidedinstanceId.","parameters":[{"name":"instanceId","in":"path","required":true,"description":"Theidentifieroftheworkflowinstancetoabort.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfuloperation","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Errorabortingworkflow","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"ErrorResponse":{"description":"TheErrorResponseobjectrepresentsacommonstructureforhandlingerrorsinAPIresponses.Itincludesessentialinformationabouttheerror,suchastheerrormessageandadditionaloptionaldetails.","type":"object","properties":{"message":{"description":"Astringprovidingaconciseandhuman-readabledescriptionoftheencounterederror.ThisfieldisrequiredintheErrorResponseobject.","type":"string","default":"internalservererror"},"additionalInfo":{"description":"Anoptionalfieldthatcancontainadditionalinformationorcontextabouttheerror.Itprovidesflexibilityforincludingextradetailsbasedonspecificerrorscenarios.","type":"string"}},"required":["message"]},"GetInstancesRequest":{"type":"object","properties":{"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"},"filters":{"$ref":"#/components/schemas/SearchRequest"}}},"GetOverviewsRequestParams":{"type":"object","properties":{"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"},"filters":{"$ref":"#/components/schemas/SearchRequest"}}},"WorkflowOverviewListResultDTO":{"type":"object","properties":{"overviews":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowOverviewDTO"},"minItems":0},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"WorkflowOverviewDTO":{"type":"object","properties":{"workflowId":{"type":"string","description":"Workflowuniqueidentifier","minLength":1},"name":{"type":"string","description":"Workflowname","minLength":1},"format":{"$ref":"#/components/schemas/WorkflowFormatDTO"},"lastRunId":{"type":"string"},"lastTriggeredMs":{"type":"number","minimum":0},"lastRunStatus":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"avgDurationMs":{"type":"number","minimum":0},"description":{"type":"string"}},"required":["workflowId","format"]},"PaginationInfoDTO":{"type":"object","properties":{"pageSize":{"type":"number"},"offset":{"type":"number"},"totalCount":{"type":"number"},"orderDirection":{"enum":["ASC","DESC"]},"orderBy":{"type":"string"}},"additionalProperties":false},"WorkflowFormatDTO":{"type":"string","description":"Formatoftheworkflowdefinition","enum":["yaml","json"]},"WorkflowCategoryDTO":{"type":"string","description":"Categoryoftheworkflow","enum":["assessment","infrastructure"]},"WorkflowListResultDTO":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowDTO"}},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}},"required":["items","paginationInfo"]},"WorkflowDTO":{"type":"object","properties":{"id":{"type":"string","description":"Workflowuniqueidentifier","minLength":1},"name":{"type":"string","description":"Workflowname","minLength":1},"format":{"$ref":"#/components/schemas/WorkflowFormatDTO"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"description":{"type":"string","description":"Descriptionoftheworkflow"},"annotations":{"type":"array","items":{"type":"string"}}},"required":["id","category","format"]},"ProcessInstanceListResultDTO":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ProcessInstanceDTO"}},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"AssessedProcessInstanceDTO":{"type":"object","properties":{"instance":{"$ref":"#/components/schemas/ProcessInstanceDTO"},"assessedBy":{"$ref":"#/components/schemas/ProcessInstanceDTO"}},"required":["instance"]},"ProcessInstanceDTO":{"type":"object","properties":{"id":{"type":"string"},"processId":{"type":"string"},"processName":{"type":"string"},"status":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"endpoint":{"type":"string"},"serviceUrl":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"},"duration":{"type":"string"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"description":{"type":"string"},"workflowdata":{"$ref":"#/components/schemas/WorkflowDataDTO"},"businessKey":{"type":"string"},"nodes":{"type":"array","items":{"$ref":"#/components/schemas/NodeInstanceDTO"}},"error":{"$ref":"#/components/schemas/ProcessInstanceErrorDTO"}},"required":["id","processId","nodes"]},"WorkflowDataDTO":{"type":"object","properties":{"result":{"$ref":"#/components/schemas/WorkflowResultDTO"}},"additionalProperties":true},"WorkflowResultDTO":{"description":"Resultofaworkflowexecution","type":"object","properties":{"completedWith":{"description":"Thestateofworkflowcompletion.","type":"string","enum":["error","success"]},"message":{"description":"High-levelsummaryofthecurrentstatus,free-formtext,humanreadable.","type":"string"},"nextWorkflows":{"description":"Listofworkflowssuggestedtorunnext.Itemsatlowerindexesareofhigherpriority.","type":"array","items":{"type":"object","properties":{"id":{"description":"Workflowidentifier","type":"string"},"name":{"description":"Humanreadabletitledescribingtheworkflow.","type":"string"}},"required":["id","name"]}},"outputs":{"description":"Additionalstructuredoutputofworkflowprocessing.Thiscancontainidentifiersofcreatedresources,linkstoresources,logsorotheroutput.","type":"array","items":{"type":"object","properties":{"key":{"description":"Uniqueidentifieroftheoption.Preferablyhuman-readable.","type":"string"},"value":{"description":"Freeformvalueoftheoption.","anyOf":[{"type":"string"},{"type":"number"}]},"format":{"description":"Moredetailedtypeofthe'value'property.Defaultsto'text'.","enum":["text","number","link"]}},"required":["key","value"]}}}},"ProcessInstanceStatusDTO":{"type":"string","description":"Statusoftheworkflowrun","enum":["Active","Error","Completed","Aborted","Suspended","Pending"]},"WorkflowRunStatusDTO":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"ExecuteWorkflowRequestDTO":{"type":"object","properties":{"inputData":{"type":"object","additionalProperties":true}},"required":["inputData"]},"ExecuteWorkflowResponseDTO":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"WorkflowProgressDTO":{"allOf":[{"$ref":"#/components/schemas/NodeInstanceDTO"},{"type":"object","properties":{"status":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"error":{"$ref":"#/components/schemas/ProcessInstanceErrorDTO"}}}]},"NodeInstanceDTO":{"type":"object","properties":{"__typename":{"type":"string","default":"NodeInstance","description":"Typename"},"id":{"type":"string","description":"NodeinstanceID"},"name":{"type":"string","description":"Nodename"},"type":{"type":"string","description":"Nodetype"},"enter":{"type":"string","description":"Datewhenthenodewasentered"},"exit":{"type":"string","description":"Datewhenthenodewasexited(optional)"},"definitionId":{"type":"string","description":"DefinitionID"},"nodeId":{"type":"string","description":"NodeID"}},"required":["id"]},"ProcessInstanceErrorDTO":{"type":"object","properties":{"__typename":{"type":"string","default":"ProcessInstanceError","description":"Typename"},"nodeDefinitionId":{"type":"string","description":"NodedefinitionID"},"message":{"type":"string","description":"Errormessage(optional)"}},"required":["nodeDefinitionId"]},"SearchRequest":{"type":"object","properties":{"filters":{"$ref":"#/components/schemas/Filter"},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"Filter":{"oneOf":[{"$ref":"#/components/schemas/LogicalFilter"},{"$ref":"#/components/schemas/FieldFilter"}]},"LogicalFilter":{"type":"object","required":["operator","filters"],"properties":{"operator":{"type":"string","enum":["AND","OR","NOT"]},"filters":{"type":"array","items":{"$ref":"#/components/schemas/Filter"}}}},"FieldFilter":{"type":"object","required":["field","operator","value"],"properties":{"field":{"type":"string"},"operator":{"type":"string","enum":["EQ","GT","GTE","LT","LTE","IN","IS_NULL","CONTAINS","CONTAINS_ALL","CONTAINS_ANY","LIKE","BETWEEN","FROM","TO"]},"value":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}]}}},"InputSchemaResponseDTO":{"type":"object","properties":{"inputSchema":{"type":"object"},"data":{"type":"object"}}}}}}`; +const OPENAPI = `{"openapi":"3.1.0","info":{"title":"Orchestratorplugin","description":"APItointeractwithorchestratorplugin","license":{"name":"Apache2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"version":"0.0.1"},"servers":[{"url":"/"}],"paths":{"/v2/workflows/overview":{"post":{"operationId":"getWorkflowsOverview","description":"Returnsthekeyfieldsoftheworkflowincludingdataonthelastruninstance","requestBody":{"required":false,"description":"Paginationandfilters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowOverviewListResultDTO"}}}},"500":{"description":"Errorfetchingworkflowoverviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/overview":{"get":{"operationId":"getWorkflowOverviewById","description":"Returnsthekeyfieldsoftheworkflowincludingdataonthelastruninstance","parameters":[{"name":"workflowId","in":"path","required":true,"description":"Uniqueidentifieroftheworkflow","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowOverviewDTO"}}}},"500":{"description":"Errorfetchingworkflowoverview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}":{"get":{"operationId":"getWorkflowById","description":"Getfullworkflowinfo","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowDTO"}}}},"500":{"description":"Errorfetchingworkflowbyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/source":{"get":{"operationId":"getWorkflowSourceById","description":"Gettheworkflow'sdefinition","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Errorfetchingworkflowsourcebyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/inputSchema":{"get":{"operationId":"getWorkflowInputSchemaById","description":"Gettheworkflowinputschema.Itdefinestheinputfieldsoftheworkflow","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}},{"name":"instanceId","in":"query","description":"IDofinstance","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InputSchemaResponseDTO"}}}},"500":{"description":"Errorfetchingworkflowinputschemabyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances":{"post":{"operationId":"getInstances","summary":"Getinstances","description":"Retrieveanarrayofworkflowexecutions(instances)","requestBody":{"required":false,"description":"Parametersforretrievinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetInstancesRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessInstanceListResultDTO"}}}},"500":{"description":"Errorfetchinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/instances":{"post":{"operationId":"getWorkflowInstances","summary":"Getinstancesforaspecificworkflow","description":"Retrieveanarrayofworkflowexecutions(instances)forthegivenworkflow","parameters":[{"name":"workflowId","in":"path","required":true,"description":"IDoftheworkflow","schema":{"type":"string"}}],"requestBody":{"required":false,"description":"Parametersforretrievingworkflowinstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessInstanceListResultDTO"}}}},"500":{"description":"Errorfetchinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/{instanceId}":{"get":{"summary":"GetWorkflowInstancebyID","description":"Getaworkflowexecution/run(instance)","operationId":"getInstanceById","parameters":[{"name":"instanceId","in":"path","required":true,"description":"IDoftheworkflowinstance","schema":{"type":"string"}},{"name":"includeAssessment","in":"query","required":false,"description":"Whethertoincludeassessment","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Successfulresponse","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssessedProcessInstanceDTO"}}}},"500":{"description":"Errorfetchinginstance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/statuses":{"get":{"operationId":"getWorkflowStatuses","summary":"Getworkflowstatuslist","description":"Retrievearraywiththestatusofallinstances","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowRunStatusDTO"}}}}},"500":{"description":"Errorfetchingworkflowstatuses","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/execute":{"post":{"summary":"Executeaworkflow","description":"Executeaworkflow","operationId":"executeWorkflow","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtoexecute","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteWorkflowRequestDTO"}}}},"responses":{"200":{"description":"Successfulexecution","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteWorkflowResponseDTO"}}}},"500":{"description":"InternalServerError","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/{instanceId}/abort":{"delete":{"summary":"Abortaworkflowinstance","operationId":"abortWorkflow","description":"AbortsaworkflowinstanceidentifiedbytheprovidedinstanceId.","parameters":[{"name":"instanceId","in":"path","required":true,"description":"Theidentifieroftheworkflowinstancetoabort.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfuloperation","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Errorabortingworkflow","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"ErrorResponse":{"description":"TheErrorResponseobjectrepresentsacommonstructureforhandlingerrorsinAPIresponses.Itincludesessentialinformationabouttheerror,suchastheerrormessageandadditionaloptionaldetails.","type":"object","properties":{"message":{"description":"Astringprovidingaconciseandhuman-readabledescriptionoftheencounterederror.ThisfieldisrequiredintheErrorResponseobject.","type":"string","default":"internalservererror"},"additionalInfo":{"description":"Anoptionalfieldthatcancontainadditionalinformationorcontextabouttheerror.Itprovidesflexibilityforincludingextradetailsbasedonspecificerrorscenarios.","type":"string"}},"required":["message"]},"GetInstancesRequest":{"type":"object","properties":{"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"},"filters":{"$ref":"#/components/schemas/SearchRequest"}}},"GetOverviewsRequestParams":{"type":"object","properties":{"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"},"filters":{"$ref":"#/components/schemas/SearchRequest"}}},"WorkflowOverviewListResultDTO":{"type":"object","properties":{"overviews":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowOverviewDTO"},"minItems":0},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"WorkflowOverviewDTO":{"type":"object","properties":{"workflowId":{"type":"string","description":"Workflowuniqueidentifier","minLength":1},"name":{"type":"string","description":"Workflowname","minLength":1},"format":{"$ref":"#/components/schemas/WorkflowFormatDTO"},"lastRunId":{"type":"string"},"lastTriggeredMs":{"type":"number","minimum":0},"lastRunStatus":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"avgDurationMs":{"type":"number","minimum":0},"description":{"type":"string"}},"required":["workflowId","format"]},"PaginationInfoDTO":{"type":"object","properties":{"pageSize":{"type":"number"},"offset":{"type":"number"},"totalCount":{"type":"number"},"orderDirection":{"enum":["ASC","DESC"]},"orderBy":{"type":"string"}},"additionalProperties":false},"WorkflowFormatDTO":{"type":"string","description":"Formatoftheworkflowdefinition","enum":["yaml","json"]},"WorkflowCategoryDTO":{"type":"string","description":"Categoryoftheworkflow","enum":["assessment","infrastructure"]},"WorkflowListResultDTO":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowDTO"}},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}},"required":["items","paginationInfo"]},"WorkflowDTO":{"type":"object","properties":{"id":{"type":"string","description":"Workflowuniqueidentifier","minLength":1},"name":{"type":"string","description":"Workflowname","minLength":1},"format":{"$ref":"#/components/schemas/WorkflowFormatDTO"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"description":{"type":"string","description":"Descriptionoftheworkflow"},"annotations":{"type":"array","items":{"type":"string"}}},"required":["id","category","format"]},"ProcessInstanceListResultDTO":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ProcessInstanceDTO"}},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"AssessedProcessInstanceDTO":{"type":"object","properties":{"instance":{"$ref":"#/components/schemas/ProcessInstanceDTO"},"assessedBy":{"$ref":"#/components/schemas/ProcessInstanceDTO"}},"required":["instance"]},"ProcessInstanceDTO":{"type":"object","properties":{"id":{"type":"string"},"processId":{"type":"string"},"processName":{"type":"string"},"status":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"endpoint":{"type":"string"},"serviceUrl":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"},"duration":{"type":"string"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"description":{"type":"string"},"workflowdata":{"$ref":"#/components/schemas/WorkflowDataDTO"},"businessKey":{"type":"string"},"nodes":{"type":"array","items":{"$ref":"#/components/schemas/NodeInstanceDTO"}},"error":{"$ref":"#/components/schemas/ProcessInstanceErrorDTO"}},"required":["id","processId","nodes"]},"WorkflowDataDTO":{"type":"object","properties":{"result":{"$ref":"#/components/schemas/WorkflowResultDTO"}},"additionalProperties":true},"WorkflowResultDTO":{"description":"Resultofaworkflowexecution","type":"object","properties":{"completedWith":{"description":"Thestateofworkflowcompletion.","type":"string","enum":["error","success"]},"message":{"description":"High-levelsummaryofthecurrentstatus,free-formtext,humanreadable.","type":"string"},"nextWorkflows":{"description":"Listofworkflowssuggestedtorunnext.Itemsatlowerindexesareofhigherpriority.","type":"array","items":{"type":"object","properties":{"id":{"description":"Workflowidentifier","type":"string"},"name":{"description":"Humanreadabletitledescribingtheworkflow.","type":"string"}},"required":["id","name"]}},"outputs":{"description":"Additionalstructuredoutputofworkflowprocessing.Thiscancontainidentifiersofcreatedresources,linkstoresources,logsorotheroutput.","type":"array","items":{"type":"object","properties":{"key":{"description":"Uniqueidentifieroftheoption.Preferablyhuman-readable.","type":"string"},"value":{"description":"Freeformvalueoftheoption.","anyOf":[{"type":"string"},{"type":"number"}]},"format":{"description":"Moredetailedtypeofthe'value'property.Defaultsto'text'.","enum":["text","number","link"]}},"required":["key","value"]}}}},"ProcessInstanceStatusDTO":{"type":"string","description":"Statusoftheworkflowrun","enum":["Active","Error","Completed","Aborted","Suspended","Pending"]},"WorkflowRunStatusDTO":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"ExecuteWorkflowRequestDTO":{"type":"object","properties":{"inputData":{"type":"object","additionalProperties":true}},"required":["inputData"]},"ExecuteWorkflowResponseDTO":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"WorkflowProgressDTO":{"allOf":[{"$ref":"#/components/schemas/NodeInstanceDTO"},{"type":"object","properties":{"status":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"error":{"$ref":"#/components/schemas/ProcessInstanceErrorDTO"}}}]},"NodeInstanceDTO":{"type":"object","properties":{"__typename":{"type":"string","default":"NodeInstance","description":"Typename"},"id":{"type":"string","description":"NodeinstanceID"},"name":{"type":"string","description":"Nodename"},"type":{"type":"string","description":"Nodetype"},"enter":{"type":"string","description":"Datewhenthenodewasentered"},"exit":{"type":"string","description":"Datewhenthenodewasexited(optional)"},"definitionId":{"type":"string","description":"DefinitionID"},"nodeId":{"type":"string","description":"NodeID"}},"required":["id"]},"ProcessInstanceErrorDTO":{"type":"object","properties":{"__typename":{"type":"string","default":"ProcessInstanceError","description":"Typename"},"nodeDefinitionId":{"type":"string","description":"NodedefinitionID"},"message":{"type":"string","description":"Errormessage(optional)"}},"required":["nodeDefinitionId"]},"SearchRequest":{"type":"object","properties":{"filters":{"$ref":"#/components/schemas/Filter"},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"Filter":{"oneOf":[{"$ref":"#/components/schemas/LogicalFilter"},{"$ref":"#/components/schemas/FieldFilter"}]},"LogicalFilter":{"type":"object","required":["operator","filters"],"properties":{"operator":{"type":"string","enum":["AND","OR","NOT"]},"filters":{"type":"array","items":{"$ref":"#/components/schemas/Filter"}}}},"FieldFilter":{"type":"object","required":["field","operator","value"],"properties":{"field":{"type":"string"},"operator":{"type":"string","enum":["EQ","GT","GTE","LT","LTE","IN","IS_NULL","CONTAINS","CONTAINS_ALL","CONTAINS_ANY","LIKE","BETWEEN"]},"value":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}]}}},"InputSchemaResponseDTO":{"type":"object","properties":{"inputSchema":{"type":"object"},"data":{"type":"object"}}}}}}`; export const openApiDocument = JSON.parse(OPENAPI); diff --git a/plugins/orchestrator-common/src/generated/client/api.ts b/plugins/orchestrator-common/src/generated/client/api.ts index 2591512ff0..9691b68412 100644 --- a/plugins/orchestrator-common/src/generated/client/api.ts +++ b/plugins/orchestrator-common/src/generated/client/api.ts @@ -125,9 +125,7 @@ export const FieldFilterOperatorEnum = { ContainsAll: 'CONTAINS_ALL', ContainsAny: 'CONTAINS_ANY', Like: 'LIKE', - Between: 'BETWEEN', - From: 'FROM', - To: 'TO' + Between: 'BETWEEN' } as const; export type FieldFilterOperatorEnum = typeof FieldFilterOperatorEnum[keyof typeof FieldFilterOperatorEnum]; diff --git a/plugins/orchestrator-common/src/generated/docs/html/index.html b/plugins/orchestrator-common/src/generated/docs/html/index.html index fc3baae94b..bf6535a46c 100644 --- a/plugins/orchestrator-common/src/generated/docs/html/index.html +++ b/plugins/orchestrator-common/src/generated/docs/html/index.html @@ -897,7 +897,7 @@ }, "operator" : { "type" : "string", - "enum" : [ "EQ", "GT", "GTE", "LT", "LTE", "IN", "IS_NULL", "CONTAINS", "CONTAINS_ALL", "CONTAINS_ANY", "LIKE", "BETWEEN", "FROM", "TO" ] + "enum" : [ "EQ", "GT", "GTE", "LT", "LTE", "IN", "IS_NULL", "CONTAINS", "CONTAINS_ALL", "CONTAINS_ANY", "LIKE", "BETWEEN" ] }, "value" : { "$ref" : "#/components/schemas/FieldFilter_value" diff --git a/plugins/orchestrator-common/src/openapi/openapi.yaml b/plugins/orchestrator-common/src/openapi/openapi.yaml index e566bba68a..4b4ed2f949 100644 --- a/plugins/orchestrator-common/src/openapi/openapi.yaml +++ b/plugins/orchestrator-common/src/openapi/openapi.yaml @@ -691,8 +691,6 @@ components: CONTAINS_ANY, LIKE, BETWEEN, - FROM, - TO, ] value: oneOf: diff --git a/plugins/orchestrator-form-react/package.json b/plugins/orchestrator-form-react/package.json index 95c6df37f3..6926e1cc8e 100644 --- a/plugins/orchestrator-form-react/package.json +++ b/plugins/orchestrator-form-react/package.json @@ -1,7 +1,7 @@ { "name": "@janus-idp/backstage-plugin-orchestrator-form-react", "description": "Web library for the orchestrator-form plugin", - "version": "1.2.0", + "version": "1.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,7 +35,6 @@ "@backstage/core-components": "^0.14.9", "@backstage/core-plugin-api": "^1.9.3", "@backstage/types": "^1.1.1", - "@janus-idp/backstage-plugin-orchestrator-common": "1.21.0", "@janus-idp/backstage-plugin-orchestrator-form-api": "1.2.0", "@material-ui/core": "^4.12.4", "@rjsf/core": "5.18.5", diff --git a/plugins/orchestrator/CHANGELOG.md b/plugins/orchestrator/CHANGELOG.md index aaaec7fd41..803a31afab 100644 --- a/plugins/orchestrator/CHANGELOG.md +++ b/plugins/orchestrator/CHANGELOG.md @@ -1,5 +1,9 @@ ### Dependencies +* **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.2.1 + +### Dependencies + * **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.21.0 * **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.2.0 diff --git a/plugins/orchestrator/package.json b/plugins/orchestrator/package.json index a61ae71bb5..b5eccab819 100644 --- a/plugins/orchestrator/package.json +++ b/plugins/orchestrator/package.json @@ -1,6 +1,6 @@ { "name": "@janus-idp/backstage-plugin-orchestrator", - "version": "2.1.2", + "version": "2.1.3", "license": "Apache-2.0", "main": "src/index.ts", "types": "src/index.ts", @@ -63,7 +63,7 @@ "@backstage/types": "^1.1.1", "@janus-idp/backstage-plugin-orchestrator-common": "1.21.0", "@janus-idp/backstage-plugin-orchestrator-form-api": "1.2.0", - "@janus-idp/backstage-plugin-orchestrator-form-react": "1.2.0", + "@janus-idp/backstage-plugin-orchestrator-form-react": "1.2.1", "@kie-tools-core/editor": "^0.32.0", "@kie-tools-core/notifications": "^0.32.0", "@kie-tools-core/react-hooks": "^0.32.0",