diff --git a/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4-spec-http-expects.ts b/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4-spec-http-expects.ts index 89bcf5e1c22..bb42c65ded5 100644 --- a/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4-spec-http-expects.ts +++ b/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4-spec-http-expects.ts @@ -106,16 +106,8 @@ export class ApiCreationV4SpecHttpExpects { }); } - expectCallsForApiCreation(apiId: string, planId: string) { - const createApiRequest = this.httpTestingController.expectOne({ url: `${CONSTANTS_TESTING.env.v2BaseURL}/apis`, method: 'POST' }); - - expect(createApiRequest.request.body).toEqual( - expect.objectContaining({ - definitionVersion: 'V4', - name: 'API name', - }), - ); - createApiRequest.flush(fakeApiV4({ id: apiId })); + expectCallsForApiAndPlanCreation(apiId: string, planId: string) { + this.expectCallsForApiCreation(apiId); const createPlansRequest = this.httpTestingController.expectOne({ url: `${CONSTANTS_TESTING.env.v2BaseURL}/apis/${apiId}/plans`, @@ -130,8 +122,20 @@ export class ApiCreationV4SpecHttpExpects { createPlansRequest.flush(fakePlanV4({ apiId: apiId, id: planId })); } + expectCallsForApiCreation(apiId: string) { + const createApiRequest = this.httpTestingController.expectOne({ url: `${CONSTANTS_TESTING.env.v2BaseURL}/apis`, method: 'POST' }); + + expect(createApiRequest.request.body).toEqual( + expect.objectContaining({ + definitionVersion: 'V4', + name: 'API name', + }), + ); + createApiRequest.flush(fakeApiV4({ id: apiId })); + } + expectCallsForApiDeployment(apiId: string, planId: string) { - this.expectCallsForApiCreation(apiId, planId); + this.expectCallsForApiAndPlanCreation(apiId, planId); const publishPlansRequest = this.httpTestingController.expectOne({ url: `${CONSTANTS_TESTING.env.v2BaseURL}/apis/${apiId}/plans/${planId}/_publish`, diff --git a/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.component.ts b/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.component.ts index ca24e625d15..a93284d2982 100644 --- a/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.component.ts +++ b/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.component.ts @@ -33,7 +33,7 @@ import { Step4MenuItemComponent } from './steps/step-4-menu-item/step-4-menu-ite import { ApiV2Service } from '../../../services-ngx/api-v2.service'; import { SnackBarService } from '../../../services-ngx/snack-bar.service'; import { ApiPlanV2Service } from '../../../services-ngx/api-plan-v2.service'; -import { PlanV4, Api, CreateApiV4, EndpointGroupV4, Entrypoint, Listener } from '../../../entities/management-api-v2'; +import { PlanV4, Api, CreateApiV4, EndpointGroupV4, Entrypoint, Listener, ListenerType } from '../../../entities/management-api-v2'; import { ApiReviewV2Service } from '../../../services-ngx/api-review-v2.service'; export interface Result { @@ -190,12 +190,7 @@ export class ApiCreationV4Component implements OnInit, OnDestroy { const listenerConfig = { type: listenersType, - ...(listenersType === 'HTTP' - ? { paths: apiCreationPayload.paths } - : listenersType === 'TCP' - ? // api is expecting hosts as a list of strings - { hosts: apiCreationPayload.hosts.map((host) => host.host) } - : {}), + ...this.getListenerSpecificConfig(listenersType, apiCreationPayload), entrypoints, }; return [...listeners, listenerConfig]; @@ -320,4 +315,17 @@ export class ApiCreationV4Component implements OnInit, OnDestroy { }), ); } + + private getListenerSpecificConfig(listenerType: ListenerType, apiCreationPayload: ApiCreationPayload): object { + switch (listenerType) { + case 'HTTP': + return { paths: apiCreationPayload.paths }; + case 'TCP': + return { hosts: apiCreationPayload.hosts.map((host) => host.host) }; + case 'KAFKA': + return { host: apiCreationPayload.host?.host, port: apiCreationPayload.port?.port }; + default: + return {}; + } + } } diff --git a/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.native-kafka.component.spec.ts b/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.native-kafka.component.spec.ts new file mode 100644 index 00000000000..891ce05c4c5 --- /dev/null +++ b/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.native-kafka.component.spec.ts @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2015 The Gravitee team (http://gravitee.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed } from '@angular/core/testing'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { LICENSE_CONFIGURATION_TESTING } from '@gravitee/ui-particles-angular'; +import { HttpTestingController } from '@angular/common/http/testing'; +import { MatIconTestingModule } from '@angular/material/icon/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { InteractivityChecker } from '@angular/cdk/a11y'; +import { set } from 'lodash'; + +import { ApiCreationV4Component } from './api-creation-v4.component'; +import { ApiCreationV4Module } from './api-creation-v4.module'; +import { Step2Entrypoints2ConfigHarness } from './steps/step-2-entrypoints/step-2-entrypoints-2-config.harness'; +import { Step5SummaryHarness } from './steps/step-5-summary/step-5-summary.harness'; +import { ApiCreationV4SpecStepperHelper } from './api-creation-v4-spec-stepper-helper'; +import { ApiCreationV4SpecHttpExpects } from './api-creation-v4-spec-http-expects'; + +import { CONSTANTS_TESTING, GioTestingModule } from '../../../shared/testing'; +import { ConnectorPlugin } from '../../../entities/management-api-v2'; +import { Constants } from '../../../entities/Constants'; + +describe('ApiCreationV4Component - Native Kafka', () => { + const nativeKafkaEntrypoint: Partial[] = [ + { id: 'native-kafka', supportedApiType: 'NATIVE', name: 'Native Kafka Entrypoint', supportedListenerType: 'KAFKA' }, + ]; + + let fixture: ComponentFixture; + let harnessLoader: HarnessLoader; + let httpTestingController: HttpTestingController; + + let enabledReviewMode = false; + let httpExpects: ApiCreationV4SpecHttpExpects; + let stepperHelper: ApiCreationV4SpecStepperHelper; + + const init = async () => { + await TestBed.configureTestingModule({ + declarations: [ApiCreationV4Component], + providers: [ + { + provide: Constants, + useFactory: () => { + const constants = CONSTANTS_TESTING; + set(constants, 'env.settings.plan.security', { + apikey: { + enabled: true, + }, + jwt: { + enabled: true, + }, + keyless: { + enabled: true, + }, + oauth2: { + enabled: true, + }, + customApiKey: { + enabled: true, + }, + sharedApiKey: { + enabled: true, + }, + push: { + enabled: true, + }, + }); + + set(constants, 'env.settings.apiReview', { + get enabled() { + return enabledReviewMode; + }, + }); + return constants; + }, + }, + { + provide: 'LicenseConfiguration', + useValue: LICENSE_CONFIGURATION_TESTING, + }, + ], + imports: [NoopAnimationsModule, ApiCreationV4Module, GioTestingModule, MatIconTestingModule], + }) + .overrideProvider(InteractivityChecker, { + useValue: { + isFocusable: () => true, // This traps focus checks and so avoid warnings when dealing with + }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(ApiCreationV4Component); + httpTestingController = TestBed.inject(HttpTestingController); + + harnessLoader = await TestbedHarnessEnvironment.loader(fixture); + httpExpects = new ApiCreationV4SpecHttpExpects(httpTestingController); + stepperHelper = new ApiCreationV4SpecStepperHelper(harnessLoader, httpExpects, httpTestingController); + }; + + beforeEach(async () => await init()); + + afterEach(() => { + jest.clearAllMocks(); + httpTestingController.verify(); + enabledReviewMode = false; + }); + + describe('Entrypoint validation', () => { + it('should not continue when host and port not specified', fakeAsync(async () => { + await stepperHelper.fillAndValidateStep1_ApiDetails('API', '1.0', 'Description'); + await stepperHelper.fillAndValidateStep2_0_EntrypointsArchitecture('KAFKA'); + + httpExpects.expectRestrictedDomainsGetRequest([]); + httpExpects.expectSchemaGetRequest(nativeKafkaEntrypoint); + + const entrypointsConfig = await harnessLoader.getHarness(Step2Entrypoints2ConfigHarness); + expect(await entrypointsConfig.hasKafkaListenersForm()).toEqual(true); + + await entrypointsConfig.fillHost('my-lovely-host'); + httpExpects.expectVerifyHosts(['my-lovely-host'], 1); + + expect(await entrypointsConfig.hasValidationDisabled()).toBeTruthy(); + + await entrypointsConfig.fillPort(1000); + expect(await entrypointsConfig.hasValidationDisabled()).toBeFalsy(); + + discardPeriodicTasks(); + })); + + it.each(['', 'path!', '//path'])( + "should not validate when host equals: ' %s '", + fakeAsync(async (contextPath: string) => { + await stepperHelper.fillAndValidateStep1_ApiDetails('API', '1.0', 'Description'); + await stepperHelper.fillAndValidateStep2_0_EntrypointsArchitecture('KAFKA'); + + const entrypointsConfig = await harnessLoader.getHarness(Step2Entrypoints2ConfigHarness); + + httpExpects.expectRestrictedDomainsGetRequest([]); + httpExpects.expectSchemaGetRequest(nativeKafkaEntrypoint); + + await entrypointsConfig.fillPort(1000); + await entrypointsConfig.fillHost(contextPath); + httpExpects.expectVerifyContextPath(); + expect(await entrypointsConfig.hasValidationDisabled()).toBeTruthy(); + + discardPeriodicTasks(); + }), + ); + }); + describe('API Creation', () => { + it('should create the API', fakeAsync(async () => { + await stepperHelper.fillAndValidateStep1_ApiDetails('API name', '1.0', 'Description'); + await stepperHelper.fillAndValidateStep2_0_EntrypointsArchitecture('KAFKA'); + await stepperHelper.fillAndValidateStep2_2_EntrypointsConfig(nativeKafkaEntrypoint); + + discardPeriodicTasks(); + + const step5Harness = await harnessLoader.getHarness(Step5SummaryHarness); + const step1Summary = await step5Harness.getStepSummaryTextContent(1); + expect(step1Summary).toContain('API name:' + 'API'); + expect(step1Summary).toContain('Version:' + '1.0'); + expect(step1Summary).toContain('Description:' + ' Description'); + + const step2Summary = await step5Harness.getStepSummaryTextContent(2); + // expect(step2Summary).toContain('Path:' + '/api/my-api-3'); + expect(step2Summary).toContain('Type:' + 'KAFKA'); + expect(step2Summary).toContain('Entrypoints:' + ' Native Kafka Entrypoint'); + + const step3Summary = await step5Harness.getStepSummaryTextContent(3); + expect(step3Summary).toContain('Endpoints' + 'Endpoints: ' + 'Native Kafka Endpoint'); + + const step4Summary = await step5Harness.getStepSummaryTextContent(4); + expect(step4Summary).toContain('No plans are selected.'); + + await step5Harness.clickCreateMyApiButton(); + httpExpects.expectCallsForApiCreation('api-id'); + + flush(); + })); + }); +}); diff --git a/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.navigation.component.spec.ts b/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.navigation.component.spec.ts index e6b8d791173..2abc5d07d43 100644 --- a/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.navigation.component.spec.ts +++ b/gravitee-apim-console-webui/src/management/api/creation-v4/api-creation-v4.navigation.component.spec.ts @@ -1415,7 +1415,7 @@ describe('ApiCreationV4Component - Navigation', () => { it('should go to confirmation page after clicking Save API & Ask for review', async () => { await step5Harness.clickCreateAndAskForReviewMyApiButton(); - httpExpects.expectCallsForApiCreation(API_ID, PLAN_ID); + httpExpects.expectCallsForApiAndPlanCreation(API_ID, PLAN_ID); const publishPlansRequest = httpTestingController.expectOne({ url: `${CONSTANTS_TESTING.env.v2BaseURL}/apis/${API_ID}/plans/${PLAN_ID}/_publish`,