Skip to content

Commit

Permalink
Remove Contains, ContainsAll and ContainsAny operator from OpenAPI sp…
Browse files Browse the repository at this point in the history
…ec (#319)

* Remove Contains, ContainsAll and ContainsAny operators

This operators have no implementations and can't be listed in
the OpenAPI spec.

Signed-off-by: Gloria Ciavarrini <[email protected]>

* Improve error message for unsupported operators

Signed-off-by: Gloria Ciavarrini <[email protected]>

* Update changeset

Signed-off-by: Gloria Ciavarrini <[email protected]>

* Update report api

Signed-off-by: Gloria Ciavarrini <[email protected]>

* update api reports

Signed-off-by: Marek Libra <[email protected]>

---------

Signed-off-by: Gloria Ciavarrini <[email protected]>
Signed-off-by: Marek Libra <[email protected]>
Co-authored-by: Marek Libra <[email protected]>
  • Loading branch information
gciavarrini and mareklibra authored Jan 24, 2025
1 parent 113ff0d commit 967c377
Show file tree
Hide file tree
Showing 11 changed files with 442 additions and 397 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@red-hat-developer-hub/backstage-plugin-orchestrator-backend': minor
'@red-hat-developer-hub/backstage-plugin-orchestrator-common': minor
---

Fixed unsupported filter operators
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,41 @@ import {

type ProcessType = 'ProcessDefinition' | 'ProcessInstance';

const supportedOperators = [
FieldFilterOperatorEnum.Eq,
FieldFilterOperatorEnum.Like,
FieldFilterOperatorEnum.In,
FieldFilterOperatorEnum.IsNull,
FieldFilterOperatorEnum.Gt,
FieldFilterOperatorEnum.Gte,
FieldFilterOperatorEnum.Lt,
FieldFilterOperatorEnum.Lte,
FieldFilterOperatorEnum.Between,
];

const supportedOperatorsByType: Record<TypeName, FieldFilterOperatorEnum[]> = {
[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,
],
};

function isLogicalFilter(filter: Filter): filter is LogicalFilter {
return (filter as LogicalFilter).filters !== undefined;
}
Expand Down Expand Up @@ -113,7 +148,9 @@ export function buildFilterCondition(
}

if (!isOperatorSupported(filters.operator)) {
throw new Error(`Unsupported operator ${filters.operator}`);
throw new Error(
`Unsupported operator ${filters.operator}. Supported operators are: ${supportedOperators.join(', ')}`,
);
}

const fieldDef = introspection.find(f => f.name === filters.field);
Expand All @@ -122,8 +159,9 @@ export function buildFilterCondition(
}

if (!isOperatorAllowedForField(filters.operator, fieldDef, type)) {
const allowedOperators = supportedOperatorsByType[fieldDef.type.name] || [];
throw new Error(
`Unsupported operator ${filters.operator} for field "${fieldDef.name}" of type "${fieldDef.type.name}"`,
`Unsupported operator ${filters.operator} for field "${fieldDef.name}" of type "${fieldDef.type.name}". Allowed operators are: ${allowedOperators.join(', ')}`,
);
}

Expand All @@ -147,55 +185,23 @@ export function buildFilterCondition(
}

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
);
return supportedOperators.includes(operator);
}

function isFieldFilterSupported(fieldDef: IntrospectionField): boolean {
return fieldDef?.type.name === TypeName.String;
}

function isOperatorAllowedForField(
export function isOperatorAllowedForField(
operator: FieldFilterOperatorEnum,
fieldDef: IntrospectionField,
type: ProcessType,
): boolean {
if (isEnumFilter(fieldDef.name, type) && isValidEnumOperator(operator)) {
return true;
}
const allowedOperators: Record<TypeName, FieldFilterOperatorEnum[]> = {
[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];

const allowedForType = supportedOperatorsByType[fieldDef.type.name];
return allowedForType ? allowedForType.includes(operator) : false;
}

Expand Down Expand Up @@ -255,10 +261,6 @@ function getGraphQLOperator(operator: FieldFilterOperatorEnum): string {
return 'lessThan';
case 'LTE':
return 'lessThanEqual';
// case 'CONTAINS':
// return "contains"
// case 'CONTAINS_ALL':
// case 'CONTAINS_ANY':
case 'BETWEEN':
return 'between';
default:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 The Backstage Authors
* Copyright Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {
FieldFilterOperatorEnum,
Filter,
Expand All @@ -23,7 +24,80 @@ import {
TypeName,
} from '@red-hat-developer-hub/backstage-plugin-orchestrator-common';

import { buildFilterCondition } from './filterBuilder';
import {
buildFilterCondition,
isOperatorAllowedForField,
} from './filterBuilder';

describe('isOperatorAllowedForField', () => {
const testIsValidOperator = (
operator: FieldFilterOperatorEnum,
fieldDef: IntrospectionField,
expected: boolean,
) => {
const result = isOperatorAllowedForField(
operator,
fieldDef,
'ProcessInstance',
);

expect(result).toBe(expected);
};
beforeEach(() => {
jest.clearAllMocks(); // Clear mock calls before each test
});

describe('String', () => {
const fieldDef: IntrospectionField = {
name: 'testField',
type: {
name: TypeName.String,
kind: TypeKind.InputObject,
ofType: null,
},
};
it('should return true for valid operator in String type', () => {
testIsValidOperator(FieldFilterOperatorEnum.Like, fieldDef, true);
});
it('should return false for invalid operator in String type', () => {
testIsValidOperator(FieldFilterOperatorEnum.Gt, fieldDef, false);
});
});
describe('Id', () => {
const fieldDef: IntrospectionField = {
name: 'testField',
type: {
name: TypeName.Id,
kind: TypeKind.InputObject,
ofType: null,
},
};
it('should return true for valid operator in Id type', () => {
testIsValidOperator(FieldFilterOperatorEnum.Eq, fieldDef, true);
});

it('should return false for invalid operator in Id type', () => {
testIsValidOperator(FieldFilterOperatorEnum.Like, fieldDef, false);
});
});
describe('Date', () => {
const fieldDef: IntrospectionField = {
name: 'testField',
type: {
name: TypeName.Date,
kind: TypeKind.InputObject,
ofType: null,
},
};
it('should return true for valid operator in Date type', () => {
testIsValidOperator(FieldFilterOperatorEnum.Eq, fieldDef, true);
});

it('should return false for invalid operator in Date type', () => {
testIsValidOperator(FieldFilterOperatorEnum.Like, fieldDef, false);
});
});
});

describe('column filters', () => {
const createIntrospectionField = (
Expand Down
Loading

0 comments on commit 967c377

Please sign in to comment.