/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.module.extension.internal.loader.java; import static java.util.Arrays.asList; import static java.util.Collections.emptySet; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertThat; import static org.mule.metadata.java.api.utils.JavaTypeUtils.getType; import static org.mule.runtime.api.dsl.DslResolvingContext.getDefault; import static org.mule.runtime.api.meta.Category.COMMUNITY; import static org.mule.runtime.api.meta.Category.SELECT; import static org.mule.runtime.api.meta.ExpressionSupport.NOT_SUPPORTED; import static org.mule.runtime.api.meta.ExpressionSupport.REQUIRED; import static org.mule.runtime.api.meta.ExpressionSupport.SUPPORTED; import static org.mule.runtime.extension.api.ExtensionConstants.TLS_PARAMETER_NAME; import static org.mule.runtime.extension.api.annotation.Extension.DEFAULT_CONFIG_NAME; import static org.mule.runtime.extension.api.annotation.param.Optional.PAYLOAD; import static org.mule.runtime.module.extension.internal.ExtensionProperties.DEFAULT_CONNECTION_PROVIDER_NAME; import static org.mule.test.heisenberg.extension.HeisenbergConnectionProvider.SAUL_OFFICE_NUMBER; import static org.mule.test.heisenberg.extension.HeisenbergExtension.AGE; import static org.mule.test.heisenberg.extension.HeisenbergExtension.EXTENSION_DESCRIPTION; import static org.mule.test.heisenberg.extension.HeisenbergExtension.HEISENBERG; import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.TYPE_BUILDER; import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.TYPE_LOADER; import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.arrayOf; import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.assertMessageType; import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.objectTypeBuilder; import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.toMetadataType; import static org.mule.test.vegan.extension.VeganExtension.APPLE; import static org.mule.test.vegan.extension.VeganExtension.BANANA; import org.mule.metadata.api.model.AnyType; import org.mule.metadata.api.model.ArrayType; import org.mule.metadata.api.model.MetadataType; import org.mule.metadata.api.model.VoidType; import org.mule.metadata.java.api.annotation.ClassInformationAnnotation; import org.mule.runtime.api.message.Attributes; import org.mule.runtime.api.meta.ExpressionSupport; import org.mule.runtime.api.meta.MuleVersion; import org.mule.runtime.api.meta.model.declaration.fluent.ComponentDeclaration; import org.mule.runtime.api.meta.model.declaration.fluent.ConfigurationDeclaration; import org.mule.runtime.api.meta.model.declaration.fluent.ConnectionProviderDeclaration; import org.mule.runtime.api.meta.model.declaration.fluent.ExtensionDeclaration; import org.mule.runtime.api.meta.model.declaration.fluent.ExtensionDeclarer; import org.mule.runtime.api.meta.model.declaration.fluent.NamedDeclaration; import org.mule.runtime.api.meta.model.declaration.fluent.OperationDeclaration; import org.mule.runtime.api.meta.model.declaration.fluent.OutputDeclaration; import org.mule.runtime.api.meta.model.declaration.fluent.ParameterDeclaration; import org.mule.runtime.api.meta.model.declaration.fluent.SourceDeclaration; import org.mule.runtime.api.meta.model.declaration.fluent.WithOperationsDeclaration; import org.mule.runtime.api.tls.TlsContextFactory; import org.mule.runtime.extension.api.annotation.Configuration; import org.mule.runtime.extension.api.annotation.Configurations; import org.mule.runtime.extension.api.annotation.Extension; import org.mule.runtime.extension.api.annotation.Operations; import org.mule.runtime.extension.api.annotation.connectivity.ConnectionProviders; import org.mule.runtime.extension.api.annotation.param.Optional; import org.mule.runtime.extension.api.annotation.param.Parameter; import org.mule.runtime.extension.api.annotation.param.ParameterGroup; import org.mule.runtime.extension.api.annotation.param.Config; import org.mule.runtime.extension.api.exception.IllegalConfigurationModelDefinitionException; import org.mule.runtime.extension.api.exception.IllegalModelDefinitionException; import org.mule.runtime.extension.api.exception.IllegalOperationModelDefinitionException; import org.mule.runtime.extension.api.exception.IllegalParameterModelDefinitionException; import org.mule.runtime.extension.api.runtime.exception.ExceptionHandlerFactory; import org.mule.runtime.extension.api.runtime.operation.Result; import org.mule.runtime.extension.internal.loader.DefaultExtensionLoadingContext; import org.mule.runtime.extension.internal.property.PagedOperationModelProperty; import org.mule.runtime.module.extension.internal.loader.java.property.ExceptionHandlerModelProperty; import org.mule.runtime.module.extension.internal.loader.java.property.ImplementingTypeModelProperty; import org.mule.tck.size.SmallTest; import org.mule.tck.testmodels.fruit.Fruit; import org.mule.test.heisenberg.extension.HeisenbergConnectionProvider; import org.mule.test.heisenberg.extension.HeisenbergExtension; import org.mule.test.heisenberg.extension.HeisenbergOperations; import org.mule.test.heisenberg.extension.HeisenbergSource; import org.mule.test.heisenberg.extension.MoneyLaunderingOperation; import org.mule.test.heisenberg.extension.exception.CureCancerExceptionEnricher; import org.mule.test.heisenberg.extension.model.HealthStatus; import org.mule.test.heisenberg.extension.model.Investment; import org.mule.test.heisenberg.extension.model.KnockeableDoor; import org.mule.test.heisenberg.extension.model.Methylamine; import org.mule.test.heisenberg.extension.model.PersonalInfo; import org.mule.test.heisenberg.extension.model.Ricin; import org.mule.test.heisenberg.extension.model.SaleInfo; import org.mule.test.heisenberg.extension.model.Weapon; import org.mule.test.heisenberg.extension.model.types.DEAOfficerAttributes; import org.mule.test.heisenberg.extension.model.types.IntegerAttributes; import org.mule.test.heisenberg.extension.model.types.WeaponType; import org.mule.test.petstore.extension.PetStoreConnector; import org.mule.test.vegan.extension.PaulMcCartneySource; import org.mule.test.vegan.extension.VeganExtension; import com.google.common.reflect.TypeToken; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; @SmallTest @RunWith(MockitoJUnitRunner.class) public class JavaDeclarationDelegateTestCase extends AbstractJavaExtensionDeclarationTestCase { private static final String GET_GRAMS_IN_STORAGE = "getGramsInStorage"; private static final String EXTENDED_CONFIG_NAME = "extendedConfig"; private static final String EXTENDED_CONFIG_DESCRIPTION = "extendedDescription"; private static final String SOURCE_NAME = "ListenPayments"; private static final String DEA_SOURCE_NAME = "dea-radio"; private static final String SOURCE_PARAMETER = "initialBatchNumber"; private static final String SOURCE_CALLBACK_PARAMETER = "payment"; private static final String SOURCE_REPEATED_CALLBACK_PARAMETER = "sameNameParameter"; private static final String SAY_MY_NAME_OPERATION = "sayMyName"; private static final String GET_ENEMY_OPERATION = "getEnemy"; private static final String GET_ALL_ENEMIES_OPERATION = "getAllEnemies"; private static final String KILL_OPERATION = "kill"; private static final String KILL_CUSTOM_OPERATION = "killWithCustomMessage"; private static final String KILL_WITH_WEAPON = "killWithWeapon"; private static final String KILL_WITH_RICINS = "killWithRicins"; private static final String KILL_WITH_MULTIPLES_WEAPONS = "killWithMultiplesWeapons"; private static final String KILL_WITH_MULTIPLE_WILDCARD_WEAPONS = "killWithMultipleWildCardWeapons"; private static final String GET_PAGED_PERSONAL_INFO_OPERATION = "getPagedPersonalInfo"; private static final String EMPTY_PAGED_OPERATION = "emptyPagedOperation"; private static final String FAILING_PAGED_OPERATION = "failingPagedOperation"; private static final String CONNECTION_PAGED_OPERATION = "pagedOperationUsingConnection"; private static final String DIE = "die"; private static final String KILL_MANY = "killMany"; private static final String KILL_ONE = "killOne"; private static final String LAUNDER_MONEY = "launder"; private static final String INJECTED_EXTENSION_MANAGER = "getInjectedExtensionManager"; private static final String ALIAS = "alias"; private static final String KNOCK = "knock"; private static final String KNOCK_MANY = "knockMany"; private static final String CALL_SAUL = "callSaul"; private static final String CALL_GUS_FRING = "callGusFring"; private static final String CURE_CANCER = "cureCancer"; private static final String GET_SAUL_PHONE = "getSaulPhone"; private static final String GET_MEDICAL_HISTORY = "getMedicalHistory"; private static final String APPROVE_INVESTMENT = "approve"; private static final String IGNORED_OPERATION = "ignoredOperation"; private static final String EXTENSION_VERSION = getMavenProjectVersionProperty(); private static final String OTHER_HEISENBERG = "OtherHeisenberg"; private static final String PROCESS_WEAPON = "processWeapon"; private static final String PROCESS_WEAPON_LIST = "processWeaponList"; private static final String PROCESS_WEAPON_WITH_DEFAULT_VALUE = "processWeaponWithDefaultValue"; private static final String PROCESS_INFO = "processSale"; private static final String FAIL_TO_EXECUTE = "failToExecute"; private static final String THROW_ERROR = "throwError"; @Before public void setUp() { setLoader(loaderFor(HeisenbergExtension.class)); } @Test public void describeTestModule() throws Exception { ExtensionDeclarer declarer = declareExtension(); ExtensionDeclaration extensionDeclaration = declarer.getDeclaration(); assertExtensionProperties(extensionDeclaration, HEISENBERG); assertTestModuleConfiguration(extensionDeclaration); assertTestModuleOperations(extensionDeclaration); assertTestModuleConnectionProviders(extensionDeclaration); assertTestModuleMessageSource(extensionDeclaration); assertModelProperties(extensionDeclaration); } @Test public void heisenbergPointer() throws Exception { setLoader(loaderFor(HeisenbergPointer.class)); ExtensionDeclarer declarer = declareExtension(); ExtensionDeclaration extensionDeclaration = declarer.getDeclaration(); assertExtensionProperties(extensionDeclaration, OTHER_HEISENBERG); assertTestModuleConfiguration(extensionDeclaration); assertTestModuleOperations(extensionDeclaration); assertTestModuleConnectionProviders(extensionDeclaration); assertTestModuleMessageSource(extensionDeclaration); assertModelProperties(extensionDeclaration); } @Test public void heisenbergPointerPlusExternalConfig() throws Exception { setLoader(loaderFor(HeisenbergPointerPlusExternalConfig.class)); ExtensionDeclaration extensionDeclaration = declareExtension().getDeclaration(); assertExtensionProperties(extensionDeclaration, OTHER_HEISENBERG); assertThat(extensionDeclaration.getConfigurations().size(), equalTo(2)); ConfigurationDeclaration configuration = extensionDeclaration.getConfigurations().get(1); assertThat(configuration, is(notNullValue())); assertThat(configuration.getName(), equalTo(EXTENDED_CONFIG_NAME)); assertThat(configuration.getAllParameters(), hasSize(30)); assertParameter(configuration.getAllParameters(), "extendedProperty", "", toMetadataType(String.class), true, SUPPORTED, null); } @Test(expected = IllegalConfigurationModelDefinitionException.class) public void heisenbergWithOperationsConfig() throws Exception { loaderFor(HeisenbergWithSameOperationsAndConfigs.class) .declare(new DefaultExtensionLoadingContext(HeisenbergWithSameOperationsAndConfigs.class.getClassLoader(), getDefault(emptySet()))); } @Test(expected = IllegalParameterModelDefinitionException.class) public void heisenbergWithParameterGroupAsOptional() throws Exception { loaderFor(HeisenbergWithParameterGroupAsOptional.class) .declare(new DefaultExtensionLoadingContext(HeisenbergWithSameOperationsAndConfigs.class.getClassLoader(), getDefault(emptySet()))); } @Test(expected = IllegalParameterModelDefinitionException.class) public void heisenbergWithRecursiveParameterGroup() throws Exception { loaderFor(HeisenbergWithRecursiveParameterGroup.class) .declare(new DefaultExtensionLoadingContext(HeisenbergWithSameOperationsAndConfigs.class.getClassLoader(), getDefault(emptySet()))); } @Test(expected = IllegalModelDefinitionException.class) public void heisenbergWithMoreThanOneConfigInOperation() throws Exception { loaderFor(HeisenbergWithInvalidOperation.class) .declare(new DefaultExtensionLoadingContext(HeisenbergWithSameOperationsAndConfigs.class.getClassLoader(), getDefault(emptySet()))); } @Test(expected = IllegalOperationModelDefinitionException.class) public void heisenbergWithOperationPointingToExtension() throws Exception { loaderFor(HeisenbergWithOperationsPointingToExtension.class) .declare(new DefaultExtensionLoadingContext(HeisenbergWithSameOperationsAndConfigs.class.getClassLoader(), getDefault(emptySet()))); } @Test(expected = IllegalConfigurationModelDefinitionException.class) public void heisenbergWithOperationPointingToExtensionAndDefaultConfig() throws Exception { loaderFor(HeisenbergWithOperationsPointingToExtensionAndDefaultConfig.class) .declare(new DefaultExtensionLoadingContext(HeisenbergWithSameOperationsAndConfigs.class.getClassLoader(), getDefault(emptySet()))); } @Test public void messageOperationWithoutGenerics() throws Exception { ExtensionDeclarer declarer = loaderFor(HeisenbergWithGenericlessMessageOperation.class) .declare(new DefaultExtensionLoadingContext(HeisenbergWithSameOperationsAndConfigs.class.getClassLoader(), getDefault(emptySet()))); OperationDeclaration operation = getOperation(declarer.getDeclaration(), "noGenerics"); assertThat(operation.getOutput().getType(), is(instanceOf(AnyType.class))); assertThat(operation.getOutputAttributes().getType(), is(instanceOf(VoidType.class))); } @Test public void listOfResultsOperation() throws Exception { ExtensionDeclarer declarer = loaderFor(HeisenbergWithListOfResultOperations.class) .declare(new DefaultExtensionLoadingContext(getClass().getClassLoader(), getDefault(emptySet()))); OperationDeclaration operation = getOperation(declarer.getDeclaration(), "listOfResults"); MetadataType outputType = operation.getOutput().getType(); assertThat(outputType, is(instanceOf(ArrayType.class))); assertMessageType(((ArrayType) outputType).getType(), TYPE_LOADER.load(Integer.class), TYPE_LOADER.load(IntegerAttributes.class)); assertThat(operation.getOutputAttributes().getType(), is(instanceOf(VoidType.class))); } @Test public void listOfResultsOperationWithoutGenerics() throws Exception { ExtensionDeclarer declarer = loaderFor(HeisenbergWithListOfResultOperations.class) .declare(new DefaultExtensionLoadingContext(getClass().getClassLoader(), getDefault(emptySet()))); OperationDeclaration operation = getOperation(declarer.getDeclaration(), "listOfResultsWithoutGenerics"); MetadataType outputType = operation.getOutput().getType(); assertThat(outputType, is(instanceOf(ArrayType.class))); assertMessageType(((ArrayType) outputType).getType(), TYPE_BUILDER.anyType().build(), TYPE_BUILDER.voidType().build()); } @Test public void flyweight() { setLoader(loaderFor(VeganExtension.class)); ExtensionDeclarer declarer = declareExtension(); final ExtensionDeclaration declaration = declarer.getDeclaration(); final ConfigurationDeclaration appleConfiguration = findDeclarationByName(declaration.getConfigurations(), APPLE); final ConfigurationDeclaration bananaConfiguration = findDeclarationByName(declaration.getConfigurations(), BANANA); final String sourceName = PaulMcCartneySource.class.getSimpleName(); SourceDeclaration appleSource = findDeclarationByName(appleConfiguration.getMessageSources(), sourceName); SourceDeclaration bananaSource = findDeclarationByName(bananaConfiguration.getMessageSources(), sourceName); assertThat(appleSource, is(sameInstance(bananaSource))); final String operationName = "spreadTheWord"; OperationDeclaration appleOperation = findDeclarationByName(appleConfiguration.getOperations(), operationName); OperationDeclaration bananaOperation = findDeclarationByName(bananaConfiguration.getOperations(), operationName); assertThat(appleOperation, is(sameInstance(bananaOperation))); } @Test public void minMuleVersionIsDescribedCorrectly() { setLoader(loaderFor(HeisenbergExtension.class)); ExtensionDeclarer declarer = declareExtension(); final ExtensionDeclaration declaration = declarer.getDeclaration(); assertThat(declaration.getMinMuleVersion(), is(new MuleVersion("4.1"))); } @Test public void categoryIsDescribedCorrectly() { setLoader(loaderFor(HeisenbergExtension.class)); ExtensionDeclarer declarer = declareExtension(); final ExtensionDeclaration declaration = declarer.getDeclaration(); assertThat(declaration.getCategory(), is(SELECT)); } @Test public void minMuleVersionDefaultValueIsDescribedCorrectly() { setLoader(loaderFor(PetStoreConnector.class)); ExtensionDeclarer declarer = declareExtension(); final ExtensionDeclaration declaration = declarer.getDeclaration(); assertThat(declaration.getMinMuleVersion(), is(new MuleVersion("4.0"))); } @Test public void categoryDefaultValueIsDescribedCorrectly() { setLoader(loaderFor(PetStoreConnector.class)); ExtensionDeclarer declarer = declareExtension(); final ExtensionDeclaration declaration = declarer.getDeclaration(); assertThat(declaration.getCategory(), is(COMMUNITY)); } @Test public void flowListeningOperationWithoutAttributes() { setLoader(loaderFor(VeganExtension.class)); ExtensionDeclarer declarer = declareExtension(); final ExtensionDeclaration declaration = declarer.getDeclaration(); OperationDeclaration operation = getOperation(getConfiguration(declaration, BANANA), "getLunch"); assertThat(operation, is(notNullValue())); assertOutputType(operation.getOutput(), toMetadataType(Fruit.class), false); assertOutputType(operation.getOutputAttributes(), TYPE_BUILDER.voidType().build(), false); } private <T extends NamedDeclaration> T findDeclarationByName(Collection<T> declarations, String name) { return declarations.stream().filter(decl -> decl.getName().equals(name)).findFirst() .orElseThrow(() -> new NoSuchElementException()); } private void assertTestModuleConfiguration(ExtensionDeclaration extensionDeclaration) throws Exception { assertThat(extensionDeclaration.getConfigurations(), hasSize(1)); ConfigurationDeclaration conf = extensionDeclaration.getConfigurations().get(0); assertThat(conf.getName(), equalTo(DEFAULT_CONFIG_NAME)); List<ParameterDeclaration> parameters = conf.getAllParameters(); assertThat(parameters, hasSize(29)); assertParameter(parameters, "myName", "", toMetadataType(String.class), false, SUPPORTED, HEISENBERG); assertParameter(parameters, "age", "", toMetadataType(Integer.class), false, SUPPORTED, AGE); assertParameter(parameters, "enemies", "", listOfString(), true, SUPPORTED, null); assertParameter(parameters, "money", "", toMetadataType(BigDecimal.class), true, SUPPORTED, null); assertParameter(parameters, "cancer", "", toMetadataType(boolean.class), true, SUPPORTED, null); assertParameter(parameters, "cancer", "", toMetadataType(boolean.class), true, SUPPORTED, null); assertParameter(parameters, "dateOfBirth", "", toMetadataType(Date.class), false, SUPPORTED, null); assertParameter(parameters, "dateOfDeath", "", toMetadataType(Calendar.class), false, SUPPORTED, null); assertParameter(parameters, "dateOfConception", "", toMetadataType(LocalDateTime.class), false, SUPPORTED, null); assertParameter(parameters, "dateOfGraduation", "", toMetadataType(Calendar.class), false, NOT_SUPPORTED, null); assertParameter(parameters, "recipe", "", TYPE_BUILDER.objectType() .id(Map.class.getName()) .openWith(TYPE_BUILDER.numberType().id(Long.class.getName())) .with(new ClassInformationAnnotation(Map.class, asList(String.class, Long.class))) .build(), false, SUPPORTED, null); assertParameter(parameters, "ricinPacks", "", arrayOf(Set.class, objectTypeBuilder(Ricin.class)), false, SUPPORTED, null); assertParameter(parameters, "nextDoor", "", toMetadataType(KnockeableDoor.class), false, SUPPORTED, null); assertParameter(parameters, "candidateDoors", "", TYPE_BUILDER.objectType().id(Map.class.getName()) .openWith((objectTypeBuilder(KnockeableDoor.class)).build()) .with(new ClassInformationAnnotation(Map.class, asList(String.class, KnockeableDoor.class))) .build(), false, SUPPORTED, null); assertParameter(parameters, "initialHealth", "", toMetadataType(HealthStatus.class), false, SUPPORTED, "CANCER"); assertParameter(parameters, "finalHealth", "", toMetadataType(HealthStatus.class), true, SUPPORTED, null); assertParameter(parameters, "labAddress", "", toMetadataType(String.class), false, REQUIRED, null); assertParameter(parameters, "firstEndevour", "", toMetadataType(String.class), false, NOT_SUPPORTED, null); assertParameter(parameters, "weapon", "", toMetadataType(Weapon.class), false, SUPPORTED, null); assertParameter(parameters, "wildCardWeapons", "", arrayOf(List.class, objectTypeBuilder(Weapon.class)), false, SUPPORTED, null); assertParameter(parameters, "wildCards", "", arrayOf(List.class, objectTypeBuilder(Object.class)), false, SUPPORTED, null); assertParameter(parameters, "monthlyIncomes", "", arrayOf(List.class, TYPE_BUILDER.numberType().id(Long.class.getName())), true, SUPPORTED, null); assertParameter(parameters, "labeledRicin", "", TYPE_BUILDER.objectType().id(Map.class.getName()) .openWith(objectTypeBuilder(Ricin.class)) .with(new ClassInformationAnnotation(Map.class, asList(String.class, Ricin.class))) .build(), false, SUPPORTED, null); assertParameter(parameters, "deathsBySeasons", "", TYPE_BUILDER.objectType().id(Map.class.getName()) .with(new ClassInformationAnnotation(Map.class, asList(String.class, List.class))) .openWith(TYPE_BUILDER.arrayType() .id(List.class.getName()) .of(TYPE_BUILDER.stringType() .id(String.class .getName()))) .build(), false, SUPPORTED, null); assertParameter(parameters, "weaponValueMap", "", TYPE_BUILDER.objectType().id(Map.class.getName()) .with(new ClassInformationAnnotation(Map.class, asList(String.class, Weapon.class))) .openWith(TYPE_LOADER.load(Weapon.class)) .build(), false, SUPPORTED, null); assertParameter(parameters, "healthProgressions", "", TYPE_BUILDER.arrayType().id(List.class.getName()).of(TYPE_LOADER.load(HealthStatus.class)).build(), false, SUPPORTED, null); } private void assertExtensionProperties(ExtensionDeclaration extensionDeclaration, String expectedName) { assertThat(extensionDeclaration, is(notNullValue())); assertThat(extensionDeclaration.getName(), is(expectedName)); assertThat(extensionDeclaration.getDescription(), is(EXTENSION_DESCRIPTION)); assertThat(extensionDeclaration.getVersion(), is(EXTENSION_VERSION)); } private void assertTestModuleOperations(ExtensionDeclaration extensionDeclaration) throws Exception { assertThat(extensionDeclaration.getOperations(), hasSize(34)); WithOperationsDeclaration withOperationsDeclaration = extensionDeclaration.getConfigurations().get(0); assertThat(withOperationsDeclaration.getOperations().size(), is(8)); assertOperation(withOperationsDeclaration, SAY_MY_NAME_OPERATION, ""); assertOperation(withOperationsDeclaration, GET_ENEMY_OPERATION, ""); assertOperation(withOperationsDeclaration, GET_ALL_ENEMIES_OPERATION, ""); assertOperation(extensionDeclaration, KILL_OPERATION, ""); assertOperation(extensionDeclaration, KILL_CUSTOM_OPERATION, ""); assertOperation(extensionDeclaration, KILL_WITH_WEAPON, ""); assertOperation(extensionDeclaration, KILL_WITH_RICINS, ""); assertOperation(extensionDeclaration, KILL_WITH_MULTIPLES_WEAPONS, ""); assertOperation(extensionDeclaration, KILL_WITH_MULTIPLE_WILDCARD_WEAPONS, ""); assertOperation(withOperationsDeclaration, DIE, ""); assertOperation(extensionDeclaration, KILL_MANY, ""); assertOperation(extensionDeclaration, KILL_ONE, ""); assertOperation(withOperationsDeclaration, LAUNDER_MONEY, ""); assertOperation(extensionDeclaration, INJECTED_EXTENSION_MANAGER, ""); assertOperation(extensionDeclaration, ALIAS, ""); assertOperation(withOperationsDeclaration, CALL_SAUL, ""); assertOperation(extensionDeclaration, CALL_GUS_FRING, ""); assertOperation(withOperationsDeclaration, GET_SAUL_PHONE, ""); assertOperation(extensionDeclaration, GET_MEDICAL_HISTORY, ""); assertOperation(extensionDeclaration, GET_GRAMS_IN_STORAGE, ""); assertOperation(extensionDeclaration, APPROVE_INVESTMENT, ""); assertOperation(extensionDeclaration, GET_PAGED_PERSONAL_INFO_OPERATION, ""); assertOperation(extensionDeclaration, EMPTY_PAGED_OPERATION, ""); assertOperation(extensionDeclaration, FAILING_PAGED_OPERATION, ""); assertOperation(extensionDeclaration, CONNECTION_PAGED_OPERATION, ""); assertOperation(extensionDeclaration, PROCESS_INFO, ""); assertOperation(extensionDeclaration, PROCESS_WEAPON, ""); assertOperation(extensionDeclaration, PROCESS_WEAPON_LIST, ""); assertOperation(extensionDeclaration, PROCESS_WEAPON_WITH_DEFAULT_VALUE, ""); assertOperation(extensionDeclaration, FAIL_TO_EXECUTE, ""); assertOperation(extensionDeclaration, THROW_ERROR, ""); OperationDeclaration operation = getOperation(withOperationsDeclaration, SAY_MY_NAME_OPERATION); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters().isEmpty(), is(true)); operation = getOperation(withOperationsDeclaration, GET_ENEMY_OPERATION); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters(), hasSize(1)); assertThat(operation.getOutput().getType(), equalTo(toMetadataType(String.class))); assertThat(operation.getOutputAttributes().getType(), equalTo(toMetadataType(IntegerAttributes.class))); assertParameter(operation.getAllParameters(), "index", "", toMetadataType(int.class), false, SUPPORTED, "0"); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(withOperationsDeclaration, GET_ALL_ENEMIES_OPERATION); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters(), hasSize(0)); assertThat(operation.getOutput().getType(), is(instanceOf(ArrayType.class))); assertMessageType(((ArrayType) operation.getOutput().getType()).getType(), TYPE_LOADER.load(String.class), TYPE_LOADER.load(IntegerAttributes.class)); assertThat(operation.getOutputAttributes().getType(), is(instanceOf(VoidType.class))); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, KILL_OPERATION); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters(), hasSize(2)); assertThat(operation.getOutput().getType(), equalTo(toMetadataType(String.class))); assertThat(operation.getOutputAttributes().getType(), is(instanceOf(VoidType.class))); assertParameter(operation.getAllParameters(), "victim", "", toMetadataType(String.class), false, SUPPORTED, "#[payload]"); assertParameter(operation.getAllParameters(), "goodbyeMessage", "", toMetadataType(String.class), true, SUPPORTED, null); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, KILL_WITH_WEAPON); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters(), hasSize(3)); assertParameter(operation.getAllParameters(), "weapon", "", toMetadataType(Weapon.class), true, SUPPORTED, null); assertParameter(operation.getAllParameters(), "type", "", toMetadataType(WeaponType.class), true, SUPPORTED, null); assertParameter(operation.getAllParameters(), "attributesOfWeapon", "", toMetadataType(Weapon.WeaponAttributes.class), true, SUPPORTED, null); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, KILL_WITH_RICINS); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters(), hasSize(1)); assertParameter(operation.getAllParameters(), "ricins", "", arrayOf(List.class, objectTypeBuilder(Ricin.class)), false, SUPPORTED, "#[payload]"); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, KILL_WITH_MULTIPLES_WEAPONS); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters(), hasSize(1)); assertParameter(operation.getAllParameters(), "weapons", "", arrayOf(List.class, objectTypeBuilder(Weapon.class)), false, SUPPORTED, "#[payload]"); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, KILL_WITH_MULTIPLE_WILDCARD_WEAPONS); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters(), hasSize(1)); assertParameter(operation.getAllParameters(), "wildCardWeapons", "", arrayOf(List.class, objectTypeBuilder(Weapon.class)), true, SUPPORTED, null); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, KILL_CUSTOM_OPERATION); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters(), hasSize(2)); assertParameter(operation.getAllParameters(), "victim", "", toMetadataType(String.class), false, SUPPORTED, "#[payload]"); assertParameter(operation.getAllParameters(), "goodbyeMessage", "", toMetadataType(String.class), true, SUPPORTED, null); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(withOperationsDeclaration, LAUNDER_MONEY); assertParameter(operation.getAllParameters(), "amount", "", toMetadataType(long.class), true, SUPPORTED, null); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, INJECTED_EXTENSION_MANAGER); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters().isEmpty(), is(true)); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, ALIAS); assertParameter(operation.getAllParameters(), "greeting", "", toMetadataType(String.class), true, SUPPORTED, null); assertParameter(operation.getAllParameters(), "myName", "", toMetadataType(String.class), false, SUPPORTED, HEISENBERG); assertParameter(operation.getAllParameters(), "age", "", toMetadataType(Integer.class), false, SUPPORTED, AGE); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, KNOCK); assertParameter(operation.getAllParameters(), "knockedDoor", "", toMetadataType(KnockeableDoor.class), true, SUPPORTED, null); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, KNOCK_MANY); assertParameter(operation.getAllParameters(), "doors", "", arrayOf(List.class, objectTypeBuilder(KnockeableDoor.class)), true, SUPPORTED, null); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(withOperationsDeclaration, CALL_SAUL); assertThat(operation.getAllParameters(), is(empty())); assertConnected(operation, true); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, CURE_CANCER); assertThat(operation.getAllParameters(), is(empty())); assertConnected(operation, false); assertTransactional(operation, false); java.util.Optional<ExceptionHandlerFactory> exceptionEnricherFactory = operation .getModelProperty(ExceptionHandlerModelProperty.class) .map(ExceptionHandlerModelProperty::getExceptionHandlerFactory); assertThat(exceptionEnricherFactory.isPresent(), is(true)); assertThat(exceptionEnricherFactory.get().createHandler(), instanceOf(CureCancerExceptionEnricher.class)); operation = getOperation(extensionDeclaration, GET_MEDICAL_HISTORY); assertParameter(operation.getAllParameters(), "healthByYear", "", TYPE_BUILDER.objectType().id(Map.class.getName()) .with(new ClassInformationAnnotation(Map.class, asList(String.class, HealthStatus.class))) .openWith(TYPE_LOADER.load(HealthStatus.class)) .build(), true, SUPPORTED, null); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, GET_GRAMS_IN_STORAGE); assertThat(operation, is(notNullValue())); assertParameter(operation.getAllParameters(), "grams", "", TYPE_LOADER.load(int[][].class), false, SUPPORTED, "#[payload]"); assertThat(operation.getOutput().getType(), is(TYPE_LOADER.load(int[][].class))); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, APPROVE_INVESTMENT); assertThat(operation, is(notNullValue())); assertParameter(operation.getAllParameters(), "investment", "", TYPE_LOADER.load(Investment.class), true, SUPPORTED, null); assertThat(getType(operation.getOutput().getType()), equalTo(getType(TYPE_LOADER.load(Investment.class)))); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, IGNORED_OPERATION); assertThat(operation, is(nullValue())); operation = getOperation(extensionDeclaration, GET_PAGED_PERSONAL_INFO_OPERATION); assertThat(operation.getModelProperty(PagedOperationModelProperty.class).isPresent(), is(true)); assertThat(operation.getOutput().getType(), is(instanceOf(ArrayType.class))); ArrayType outputType = (ArrayType) operation.getOutput().getType(); assertThat(outputType.getType(), is(TYPE_LOADER.load(PersonalInfo.class))); assertConnected(operation, true); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, PROCESS_INFO); assertThat(operation, is(notNullValue())); assertConnected(operation, false); assertTransactional(operation, false); assertParameter(operation.getAllParameters(), "sales", "", TYPE_LOADER.load(new TypeToken<Map<String, SaleInfo>>() { }.getType()), true, SUPPORTED, null); operation = getOperation(extensionDeclaration, PROCESS_WEAPON); assertThat(operation, is(notNullValue())); assertParameter(operation.getAllParameters(), "weapon", "", TYPE_LOADER.load(Weapon.class), false, SUPPORTED, null); assertConnected(operation, false); operation = getOperation(extensionDeclaration, PROCESS_WEAPON_LIST); assertThat(operation, is(notNullValue())); assertParameter(operation.getAllParameters(), "weapons", "", TYPE_LOADER.load(new TypeToken<List<Weapon>>() {}.getType()), false, SUPPORTED, null); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, PROCESS_WEAPON_WITH_DEFAULT_VALUE); assertThat(operation, is(notNullValue())); assertParameter(operation.getAllParameters(), "weapon", "", TYPE_LOADER.load(Weapon.class), false, SUPPORTED, PAYLOAD); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, "processWeaponListWithDefaultValue"); assertThat(operation, is(notNullValue())); assertParameter(operation.getAllParameters(), "weapons", "", TYPE_LOADER.load(new TypeToken<List<Weapon>>() {}.getType()), false, SUPPORTED, PAYLOAD); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, "processAddressBook"); assertThat(operation, is(notNullValue())); assertParameter(operation.getAllParameters(), "phoneNumbers", "", TYPE_LOADER.load(new TypeToken<List<String>>() {}.getType()), true, SUPPORTED, null); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, FAIL_TO_EXECUTE); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters(), is(empty())); assertThat(operation.getOutput().getType(), is(instanceOf(VoidType.class))); assertConnected(operation, false); assertTransactional(operation, false); operation = getOperation(extensionDeclaration, THROW_ERROR); assertThat(operation, is(notNullValue())); assertThat(operation.getAllParameters(), is(empty())); assertThat(operation.getOutput().getType(), is(instanceOf(VoidType.class))); assertConnected(operation, false); assertTransactional(operation, false); } private void assertTestModuleConnectionProviders(ExtensionDeclaration extensionDeclaration) throws Exception { assertThat(extensionDeclaration.getConnectionProviders(), hasSize(1)); ConnectionProviderDeclaration connectionProvider = extensionDeclaration.getConnectionProviders().get(0); assertThat(connectionProvider.getName(), is(DEFAULT_CONNECTION_PROVIDER_NAME)); List<ParameterDeclaration> parameters = connectionProvider.getAllParameters(); assertThat(parameters, hasSize(2)); assertParameter(parameters, "saulPhoneNumber", "", toMetadataType(String.class), false, SUPPORTED, SAUL_OFFICE_NUMBER); assertParameter(parameters, TLS_PARAMETER_NAME, "", toMetadataType(TlsContextFactory.class), false, NOT_SUPPORTED, null); ImplementingTypeModelProperty typeModelProperty = connectionProvider.getModelProperty(ImplementingTypeModelProperty.class).get(); assertThat(typeModelProperty.getType(), equalTo(HeisenbergConnectionProvider.class)); } private void assertTestModuleMessageSource(ExtensionDeclaration extensionDeclaration) throws Exception { assertThat(extensionDeclaration.getMessageSources(), hasSize(1)); SourceDeclaration source = extensionDeclaration.getMessageSources().get(0); assertThat(source.getName(), is(DEA_SOURCE_NAME)); final MetadataType outputType = source.getOutput().getType(); assertThat(outputType, is(instanceOf(ArrayType.class))); assertMessageType(((ArrayType) outputType).getType(), TYPE_LOADER.load(String.class), TYPE_LOADER.load(DEAOfficerAttributes.class)); assertThat(source.getOutputAttributes().getType(), equalTo(TYPE_LOADER.load(Attributes.class))); source = extensionDeclaration.getConfigurations().get(0).getMessageSources().get(0); assertThat(source.getName(), is(SOURCE_NAME)); List<ParameterDeclaration> parameters = source.getAllParameters(); assertThat(parameters, hasSize(26)); assertParameter(parameters, SOURCE_PARAMETER, "", toMetadataType(int.class), true, NOT_SUPPORTED, null); assertParameter(parameters, SOURCE_CALLBACK_PARAMETER, "", toMetadataType(Long.class), false, SUPPORTED, "#[payload]"); assertParameter(parameters, SOURCE_REPEATED_CALLBACK_PARAMETER, "", toMetadataType(String.class), false, SUPPORTED, null); assertParameter(parameters, "methylamine", "", toMetadataType(Methylamine.class), false, SUPPORTED, null); ImplementingTypeModelProperty typeModelProperty = source.getModelProperty(ImplementingTypeModelProperty.class).get(); assertThat(typeModelProperty.getType(), equalTo(HeisenbergSource.class)); } private void assertOperation(WithOperationsDeclaration declaration, String operationName, String operationDescription) throws Exception { OperationDeclaration operation = getOperation(declaration, operationName); assertThat(operation, is(notNullValue())); assertThat(operation.getDescription(), equalTo(operationDescription)); } private void assertParameter(List<ParameterDeclaration> parameters, String name, String description, MetadataType metadataType, boolean required, ExpressionSupport expressionSupport, Object defaultValue) { ParameterDeclaration param = findParameter(parameters, name); assertThat(param, is(notNullValue())); assertThat(param.getName(), equalTo(name)); assertThat(param.getDescription(), equalTo(description)); assertThat(param.getType(), is(metadataType)); assertThat(param.isRequired(), is(required)); assertThat(param.getExpressionSupport(), is(expressionSupport)); assertThat(param.getDefaultValue(), equalTo(defaultValue)); } private void assertConnected(ComponentDeclaration declaration, boolean connected) { assertThat(declaration.isRequiresConnection(), is(connected)); } private void assertTransactional(ComponentDeclaration declaration, boolean transactional) { assertThat(declaration.isTransactional(), is(transactional)); } private void assertOutputType(OutputDeclaration output, MetadataType type, boolean isDynamic) { assertThat(output.getType(), equalTo(type)); assertThat(output.hasDynamicType(), is(isDynamic)); } private MetadataType listOfString() { return TYPE_BUILDER.arrayType().id(List.class.getName()).of(TYPE_BUILDER.stringType().id(String.class.getName())).build(); } protected void assertModelProperties(ExtensionDeclaration extensionDeclaration) { ImplementingTypeModelProperty implementingTypeModelProperty = extensionDeclaration.getModelProperty(ImplementingTypeModelProperty.class).get(); assertThat(implementingTypeModelProperty, is(notNullValue())); assertThat(HeisenbergExtension.class.isAssignableFrom(implementingTypeModelProperty.getType()), is(true)); } @Extension(name = OTHER_HEISENBERG, description = EXTENSION_DESCRIPTION) @Configurations(HeisenbergExtension.class) @ConnectionProviders(HeisenbergConnectionProvider.class) public static class HeisenbergPointer extends HeisenbergExtension { } @Extension(name = OTHER_HEISENBERG, description = EXTENSION_DESCRIPTION) @Configurations({HeisenbergExtension.class, NamedHeisenbergAlternateConfig.class}) public static class HeisenbergPointerPlusExternalConfig { } @Configuration(name = EXTENDED_CONFIG_NAME, description = EXTENDED_CONFIG_DESCRIPTION) @Operations({HeisenbergOperations.class, MoneyLaunderingOperation.class}) public static class NamedHeisenbergAlternateConfig extends HeisenbergAlternateConfig { } @Extension(name = OTHER_HEISENBERG, description = EXTENSION_DESCRIPTION) @Operations({DuplicateConfigOperation.class}) public static class HeisenbergWithInvalidOperation extends HeisenbergExtension { } @Extension(name = OTHER_HEISENBERG, description = EXTENSION_DESCRIPTION) public static class HeisenbergWithParameterGroupAsOptional extends HeisenbergExtension { @ParameterGroup(name = "personalInfo") @Optional private PersonalInfo personalInfo; } @Extension(name = OTHER_HEISENBERG, description = EXTENSION_DESCRIPTION) public static class HeisenbergWithRecursiveParameterGroup extends HeisenbergExtension { @ParameterGroup(name = "recursive") private RecursiveParameterGroup group; } @Extension(name = OTHER_HEISENBERG, description = EXTENSION_DESCRIPTION) @Operations(HeisenbergAlternateConfig.class) @Configurations(HeisenbergAlternateConfig.class) public static class HeisenbergWithSameOperationsAndConfigs extends HeisenbergExtension { } @Extension(name = OTHER_HEISENBERG, description = EXTENSION_DESCRIPTION) @Configurations(HeisenbergIsolatedConfig.class) public static class HeisenbergWithOperationsPointingToExtension extends HeisenbergExtension { } @Extension(name = OTHER_HEISENBERG, description = EXTENSION_DESCRIPTION) @Operations(HeisenbergExtension.class) public static class HeisenbergWithOperationsPointingToExtensionAndDefaultConfig extends HeisenbergExtension { } @Extension(name = OTHER_HEISENBERG, description = EXTENSION_DESCRIPTION) @Operations({HeisenbergExtension.class, GenericlessMessageOperation.class}) public static class HeisenbergWithGenericlessMessageOperation { } @Extension(name = OTHER_HEISENBERG, description = EXTENSION_DESCRIPTION) @Operations({HeisenbergExtension.class, ListOfResultsOperations.class}) public static class HeisenbergWithListOfResultOperations { } public static class DuplicateConfigOperation { public Long launder(@Config HeisenbergExtension config, @Config HeisenbergExtension config2) { return 10L; } } public static class GenericlessMessageOperation { public Result noGenerics() { return null; } } public static class ListOfResultsOperations { public List<Result<Integer, IntegerAttributes>> listOfResults() { return null; } public List<Result> listOfResultsWithoutGenerics() { return null; } } public static class HeisenbergAlternateConfig extends HeisenbergExtension { @Parameter private String extendedProperty; public String getExtendedProperty() { return extendedProperty; } public void setExtendedProperty(String extendedProperty) { this.extendedProperty = extendedProperty; } } @Operations(HeisenbergExtension.class) public static class HeisenbergIsolatedConfig { @Parameter private String extendedProperty; public String getExtendedProperty() { return extendedProperty; } public void setExtendedProperty(String extendedProperty) { this.extendedProperty = extendedProperty; } } private static class RecursiveParameterGroup { @ParameterGroup(name = "recursive") private RecursiveParameterGroup recursiveParameterGroup; } }