-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(restConfiguration): Add restConfiguration entity
This commit adds support for RestConfiguration entity. Note: The node validation is still pending due to an issue with the JSONSchema, please see #969 for more details. fix: #562 relates: #969
- Loading branch information
Showing
4 changed files
with
600 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
211 changes: 211 additions & 0 deletions
211
packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
import * as catalogIndex from '@kaoto-next/camel-catalog/index.json'; | ||
import { RestConfiguration } from '@kaoto-next/camel-catalog/types'; | ||
import { restConfigurationSchema, restConfigurationStub } from '../../../stubs/rest-configuration'; | ||
import { ICamelProcessorDefinition } from '../../camel-processors-catalog'; | ||
import { EntityType } from '../../camel/entities'; | ||
import { CatalogKind } from '../../catalog-kind'; | ||
import { CamelCatalogService } from './camel-catalog.service'; | ||
import { CamelRestConfigurationVisualEntity } from './camel-rest-configuration-visual-entity'; | ||
|
||
describe('CamelRestConfigurationVisualEntity', () => { | ||
const REST_CONFIGURATION_ID_REGEXP = /^restConfiguration-[a-zA-Z0-9]{4}$/; | ||
let restConfigurationDef: { restConfiguration: RestConfiguration }; | ||
|
||
beforeAll(async () => { | ||
const entityCatalogMap = await import('@kaoto-next/camel-catalog/' + catalogIndex.catalogs.entities.file); | ||
CamelCatalogService.setCatalogKey( | ||
CatalogKind.Entity, | ||
entityCatalogMap as unknown as Record<string, ICamelProcessorDefinition>, | ||
); | ||
}); | ||
|
||
afterAll(() => { | ||
CamelCatalogService.clearCatalogs(); | ||
}); | ||
|
||
beforeEach(() => { | ||
restConfigurationDef = { | ||
restConfiguration: { | ||
...restConfigurationStub.restConfiguration, | ||
}, | ||
}; | ||
}); | ||
|
||
describe('isApplicable', () => { | ||
it.each([ | ||
[true, { restConfiguration: {} }], | ||
[true, { restConfiguration: { bindingMode: 'off' } }], | ||
[true, restConfigurationStub], | ||
[false, { from: { id: 'from-1234', steps: [] } }], | ||
[false, { restConfiguration: { bindingMode: 'off' }, anotherProperty: true }], | ||
])('should return %s for %s', (result, definition) => { | ||
expect(CamelRestConfigurationVisualEntity.isApplicable(definition)).toEqual(result); | ||
}); | ||
}); | ||
|
||
describe('constructor', () => { | ||
it('should set id to generated id', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
expect(entity.id).toMatch(REST_CONFIGURATION_ID_REGEXP); | ||
}); | ||
}); | ||
|
||
it('should return id', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
expect(entity.getId()).toMatch(REST_CONFIGURATION_ID_REGEXP); | ||
}); | ||
|
||
it('should set id', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
const newId = 'newId'; | ||
entity.setId(newId); | ||
|
||
expect(entity.getId()).toEqual(newId); | ||
}); | ||
|
||
it('should return node label', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
expect(entity.getNodeLabel()).toEqual('restConfiguration'); | ||
}); | ||
|
||
it('should return tooltip content', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
expect(entity.getTooltipContent()).toEqual('restConfiguration'); | ||
}); | ||
|
||
describe('getComponentSchema', () => { | ||
it('should return entity current definition', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
expect(entity.getComponentSchema().definition).toEqual(restConfigurationDef.restConfiguration); | ||
}); | ||
|
||
it.skip('should return schema from store', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
expect(entity.getComponentSchema().schema).toEqual(restConfigurationSchema); | ||
}); | ||
|
||
it('should return hardcoded schema title', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
expect(entity.getComponentSchema().title).toEqual('Rest Configuration'); | ||
}); | ||
}); | ||
|
||
describe('updateModel', () => { | ||
it('should update model', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
const path = 'restConfiguration.bindingMode'; | ||
const value = 'json'; | ||
|
||
entity.updateModel(path, value); | ||
|
||
expect(restConfigurationDef.restConfiguration.bindingMode).toEqual(value); | ||
}); | ||
|
||
it('should not update model if path is not defined', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
const value = 'json_xml'; | ||
|
||
entity.updateModel(undefined, value); | ||
|
||
expect(restConfigurationDef.restConfiguration.bindingMode).toEqual('off'); | ||
}); | ||
|
||
it('should reset the restConfiguration object if it is not defined', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
entity.updateModel('restConfiguration', {}); | ||
|
||
expect(restConfigurationDef.restConfiguration).toEqual({}); | ||
}); | ||
}); | ||
|
||
it('return no interactions', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
expect(entity.getNodeInteraction()).toEqual({ | ||
canHavePreviousStep: false, | ||
canHaveNextStep: false, | ||
canHaveChildren: false, | ||
canHaveSpecialChildren: false, | ||
canRemoveStep: false, | ||
canReplaceStep: false, | ||
}); | ||
}); | ||
|
||
/** Skip until https://github.com/KaotoIO/kaoto-next/issues/969 gets resolved */ | ||
describe.skip('getNodeValidationText', () => { | ||
it('should return undefined for valid definitions', () => { | ||
const entity = new CamelRestConfigurationVisualEntity({ | ||
...restConfigurationDef.restConfiguration, | ||
useXForwardHeaders: true, | ||
apiVendorExtension: true, | ||
skipBindingOnErrorCode: true, | ||
clientRequestValidation: true, | ||
enableCORS: true, | ||
enableNoContentResponse: true, | ||
inlineRoutes: true, | ||
}); | ||
|
||
expect(entity.getNodeValidationText()).toBeUndefined(); | ||
}); | ||
|
||
it('should not modify the original definition when validating', () => { | ||
const originalRestConfigurationDef: RestConfiguration = { ...restConfigurationDef.restConfiguration }; | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
entity.getNodeValidationText(); | ||
|
||
expect(restConfigurationDef.restConfiguration).toEqual(originalRestConfigurationDef); | ||
}); | ||
|
||
it('should return errors when there is an invalid property', () => { | ||
const invalidRestConfigurationDef: RestConfiguration = { | ||
...restConfigurationDef.restConfiguration, | ||
bindingMode: 'WildModeOn', | ||
}; | ||
const entity = new CamelRestConfigurationVisualEntity(invalidRestConfigurationDef); | ||
|
||
expect(entity.getNodeValidationText()).toEqual(`'/useXForwardHeaders' must be boolean, | ||
'/apiVendorExtension' must be boolean, | ||
'/skipBindingOnErrorCode' must be boolean, | ||
'/clientRequestValidation' must be boolean, | ||
'/enableCORS' must be boolean, | ||
'/enableNoContentResponse' must be boolean, | ||
'/inlineRoutes' must be boolean`); | ||
}); | ||
}); | ||
|
||
describe('toVizNode', () => { | ||
it('should return visualization node', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
const vizNode = entity.toVizNode(); | ||
|
||
expect(vizNode.data).toEqual({ | ||
componentName: undefined, | ||
entity: { | ||
id: entity.getId(), | ||
restConfigurationDef, | ||
type: EntityType.RestConfiguration, | ||
}, | ||
icon: '', | ||
isGroup: true, | ||
path: 'restConfiguration', | ||
processorName: 'restConfiguration', | ||
}); | ||
}); | ||
}); | ||
|
||
it('should serialize the restConfiguration definition', () => { | ||
const entity = new CamelRestConfigurationVisualEntity(restConfigurationDef); | ||
|
||
expect(entity.toJSON()).toEqual(restConfigurationDef); | ||
}); | ||
}); |
149 changes: 149 additions & 0 deletions
149
packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import { ProcessorDefinition, RestConfiguration } from '@kaoto-next/camel-catalog/types'; | ||
import Ajv, { ValidateFunction } from 'ajv-draft-04'; | ||
import addFormats from 'ajv-formats'; | ||
import { getCamelRandomId } from '../../../camel-utils/camel-random-id'; | ||
import { isDefined, setValue } from '../../../utils'; | ||
import { EntityType } from '../../camel/entities/base-entity'; | ||
import { CatalogKind } from '../../catalog-kind'; | ||
import { | ||
BaseVisualCamelEntity, | ||
IVisualizationNode, | ||
IVisualizationNodeData, | ||
NodeInteraction, | ||
VisualComponentSchema, | ||
} from '../base-visual-entity'; | ||
import { CamelCatalogService } from './camel-catalog.service'; | ||
import { CamelStepsService } from './support/camel-steps.service'; | ||
|
||
export class CamelRestConfigurationVisualEntity implements BaseVisualCamelEntity { | ||
id: string; | ||
readonly type = EntityType.RestConfiguration; | ||
private schemaValidator: ValidateFunction<RestConfiguration> | undefined; | ||
|
||
constructor(public restConfigurationDef: { restConfiguration: RestConfiguration }) { | ||
const id = getCamelRandomId('restConfiguration'); | ||
this.id = id; | ||
} | ||
|
||
static isApplicable(restConfigurationDef: unknown): restConfigurationDef is { restConfiguration: RestConfiguration } { | ||
if ( | ||
!isDefined(restConfigurationDef) || | ||
Array.isArray(restConfigurationDef) || | ||
typeof restConfigurationDef !== 'object' | ||
) { | ||
return false; | ||
} | ||
|
||
const objectKeys = Object.keys(restConfigurationDef!); | ||
|
||
return ( | ||
objectKeys.length === 1 && | ||
'restConfiguration' in restConfigurationDef! && | ||
typeof restConfigurationDef.restConfiguration === 'object' | ||
); | ||
} | ||
|
||
getId(): string { | ||
return this.id; | ||
} | ||
|
||
setId(id: string): void { | ||
this.id = id; | ||
} | ||
|
||
getNodeLabel(): string { | ||
return 'restConfiguration'; | ||
} | ||
|
||
getTooltipContent(): string { | ||
return 'restConfiguration'; | ||
} | ||
|
||
addStep(): void { | ||
return; | ||
} | ||
|
||
removeStep(): void { | ||
return; | ||
} | ||
|
||
getComponentSchema(): VisualComponentSchema { | ||
const schema = CamelCatalogService.getComponent(CatalogKind.Entity, 'restConfiguration'); | ||
|
||
return { | ||
definition: Object.assign({}, this.restConfigurationDef.restConfiguration), | ||
schema: schema?.propertiesSchema || {}, | ||
title: 'Rest Configuration', | ||
}; | ||
} | ||
|
||
updateModel(path: string | undefined, value: unknown): void { | ||
if (!path) return; | ||
|
||
setValue(this.restConfigurationDef, path, value); | ||
|
||
if (!isDefined(this.restConfigurationDef.restConfiguration)) { | ||
this.restConfigurationDef.restConfiguration = {}; | ||
} | ||
} | ||
|
||
getNodeInteraction(): NodeInteraction { | ||
return { | ||
canHavePreviousStep: false, | ||
canHaveNextStep: false, | ||
canHaveChildren: false, | ||
canHaveSpecialChildren: false, | ||
canRemoveStep: false, | ||
canReplaceStep: false, | ||
}; | ||
} | ||
|
||
getNodeValidationText(): string | undefined { | ||
const componentVisualSchema = this.getComponentSchema(); | ||
if (!componentVisualSchema) return undefined; | ||
|
||
if (!this.schemaValidator) { | ||
this.schemaValidator = this.getValidatorFunction(componentVisualSchema); | ||
} | ||
|
||
this.schemaValidator?.({ ...this.restConfigurationDef.restConfiguration }); | ||
|
||
return this.schemaValidator?.errors?.map((error) => `'${error.instancePath}' ${error.message}`).join(',\n'); | ||
} | ||
|
||
toVizNode(): IVisualizationNode<IVisualizationNodeData> { | ||
const restConfigurationGroupNode = CamelStepsService.getVizNodeFromProcessor( | ||
'restConfiguration', | ||
{ processorName: 'restConfiguration' as keyof ProcessorDefinition }, | ||
this.restConfigurationDef, | ||
); | ||
restConfigurationGroupNode.data.entity = this; | ||
restConfigurationGroupNode.data.isGroup = true; | ||
|
||
return restConfigurationGroupNode; | ||
} | ||
|
||
toJSON(): unknown { | ||
return this.restConfigurationDef; | ||
} | ||
|
||
private getValidatorFunction( | ||
componentVisualSchema: VisualComponentSchema, | ||
): ValidateFunction<RestConfiguration> | undefined { | ||
const ajv = new Ajv({ | ||
strict: false, | ||
allErrors: true, | ||
useDefaults: 'empty', | ||
}); | ||
addFormats(ajv); | ||
|
||
let schemaValidator: ValidateFunction<RestConfiguration> | undefined; | ||
try { | ||
schemaValidator = ajv.compile<RestConfiguration>(componentVisualSchema.schema); | ||
} catch (error) { | ||
console.error('Could not compile schema', error); | ||
} | ||
|
||
return schemaValidator; | ||
} | ||
} |
Oops, something went wrong.