/* * 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.metadata; import static java.util.Arrays.asList; import static java.util.Optional.empty; import static java.util.Optional.of; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mule.runtime.module.extension.internal.metadata.MultilevelMetadataKeyBuilder.newKey; import static org.mule.runtime.module.extension.internal.util.IntrospectionUtils.getField; import org.mule.metadata.java.api.JavaTypeLoader; import org.mule.runtime.api.meta.model.ComponentModel; import org.mule.runtime.api.meta.model.parameter.ParameterModel; import org.mule.runtime.api.metadata.MetadataKey; import org.mule.runtime.api.metadata.MetadataResolvingException; import org.mule.runtime.extension.api.dsql.DsqlQuery; import org.mule.runtime.extension.api.dsql.QueryTranslator; import org.mule.runtime.extension.internal.property.MetadataKeyIdModelProperty; import org.mule.runtime.extension.internal.property.MetadataKeyPartModelProperty; import org.mule.runtime.module.extension.internal.loader.java.property.DeclaringMemberModelProperty; import org.mule.runtime.module.extension.internal.loader.java.property.QueryParameterModelProperty; import org.mule.test.metadata.extension.LocationKey; import java.lang.reflect.Field; import java.util.Optional; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class MetadataKeyIdObjectResolverTestCase { private static final String CITY = "city"; private static final String SFO = "SFO"; private static final String NY = "NY"; private static final String COUNTRY = "country"; private static final String USA = "USA"; private static final String CONTINENT = "continent"; private static final String AMERICA = "AMERICA"; private static final MetadataKey MULTILEVEL_KEY = newKey(AMERICA, CONTINENT).withChild(newKey(USA, COUNTRY) .withChild(newKey(SFO, CITY))).build(); private static final MetadataKey INCOMPLETE_MULTILEVEL_KEY = newKey(AMERICA, CONTINENT).withChild(newKey(USA, COUNTRY)).build(); private static final String OPERATION_NAME = "SomeOperation"; private static final String EMPTY = ""; private MetadataKeyIdObjectResolver keyIdObjectResolver; @Mock public ComponentModel componentModel; @Mock public ParameterModel continentParam; @Mock public ParameterModel countryParam; @Mock public ParameterModel cityParam; @Rule public final ExpectedException exception = ExpectedException.none(); @Before public void setUp() { mockMetadataKeyModelProp(continentParam, 1); mockMetadataKeyModelProp(countryParam, 2); mockMetadataKeyModelProp(cityParam, 3); mockDeclaringMemberModelProp(continentParam, CONTINENT); mockDeclaringMemberModelProp(countryParam, COUNTRY); mockDeclaringMemberModelProp(cityParam, CITY); mockQueryModelProp(continentParam); mockQueryModelProp(countryParam); mockQueryModelProp(cityParam); when(componentModel.getName()).thenReturn(OPERATION_NAME); } private void mockMetadataKeyModelProp(ParameterModel param, int pos) { when(param.getModelProperty(MetadataKeyPartModelProperty.class)).thenReturn(of(new MetadataKeyPartModelProperty(pos))); } private void mockDeclaringMemberModelProp(ParameterModel param, String name) { Field f = getField(LocationKey.class, name).get(); when(param.getModelProperty(DeclaringMemberModelProperty.class)).thenReturn(of(new DeclaringMemberModelProperty(f))); } private void mockQueryModelProp(ParameterModel param) { when(param.getModelProperty(QueryParameterModelProperty.class)).thenReturn(empty()); } @Test public void resolveSingleLevelKey() throws MetadataResolvingException { setParameters(continentParam); setMetadataKeyIdModelProperty(String.class); keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); final Object key = keyIdObjectResolver.resolve(newKey(AMERICA, CONTINENT).build()); assertThat(key, is(instanceOf(String.class))); String stringKey = (String) key; assertThat(stringKey, is(AMERICA)); } @Test public void resolveMultiLevelKey() throws MetadataResolvingException { setParameters(continentParam, countryParam, cityParam); setMetadataKeyIdModelProperty(LocationKey.class); keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); final Object key = keyIdObjectResolver.resolve(MULTILEVEL_KEY); assertThat(key, is(instanceOf(LocationKey.class))); LocationKey locationKey = (LocationKey) key; assertThat(locationKey, hasProperty(CONTINENT, is(AMERICA))); assertThat(locationKey, hasProperty(COUNTRY, is(USA))); assertThat(locationKey, hasProperty(CITY, is(SFO))); } @Test public void resolveDefaultMultiLevelKey() throws MetadataResolvingException { setParameters(continentParam, countryParam, cityParam); setMetadataKeyIdModelProperty(LocationKey.class); when(continentParam.getDefaultValue()).thenReturn(AMERICA); when(countryParam.getDefaultValue()).thenReturn(USA); when(cityParam.getDefaultValue()).thenReturn(SFO); keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); final Object key = keyIdObjectResolver.resolve(); assertThat(key, is(instanceOf(LocationKey.class))); LocationKey locationKey = (LocationKey) key; assertThat(locationKey, hasProperty(CONTINENT, is(AMERICA))); assertThat(locationKey, hasProperty(COUNTRY, is(USA))); assertThat(locationKey, hasProperty(CITY, is(SFO))); } @Test public void resolveDefaultSingleKey() throws MetadataResolvingException { setParameters(continentParam); setMetadataKeyIdModelProperty(String.class); when(continentParam.getDefaultValue()).thenReturn(AMERICA); keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); final Object key = keyIdObjectResolver.resolve(); assertThat(key, is(instanceOf(String.class))); assertThat(key, is(AMERICA)); } @Test public void resolveNoKeyParam() throws MetadataResolvingException { keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); final Object key = keyIdObjectResolver.resolve(); assertThat(key, is(instanceOf(String.class))); assertThat(key, is("")); } @Test public void resolveDsql() throws MetadataResolvingException { ParameterModel queryParam = mock(ParameterModel.class); when(queryParam.getModelProperty(QueryParameterModelProperty.class)) .thenReturn(Optional.of(new QueryParameterModelProperty(QueryTranslator.class))); mockMetadataKeyModelProp(queryParam, 1); setParameters(queryParam); setMetadataKeyIdModelProperty(String.class); MetadataKey dsqlKey = newKey("dsql:SELECT id FROM Circle WHERE (diameter < 18)").build(); keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); final Object resolvedKey = keyIdObjectResolver.resolve(dsqlKey); assertThat(resolvedKey, is(instanceOf(DsqlQuery.class))); DsqlQuery query = (DsqlQuery) resolvedKey; assertThat(query.getFields(), hasSize(1)); assertThat(query.getType().getName(), is("Circle")); } @Test public void failToResolveWithNotInstantiableKey() throws MetadataResolvingException { exception.expect(MetadataResolvingException.class); exception.expectMessage( is("MetadataKey object of type 'NotInstantiableClass' from the component 'SomeOperation' could not be instantiated")); exception.expectCause(is(instanceOf(IllegalArgumentException.class))); setParameters(continentParam, countryParam, cityParam); setMetadataKeyIdModelProperty(NotInstantiableClass.class); keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); keyIdObjectResolver.resolve(MULTILEVEL_KEY); } @Test public void failToResolveWithMissingLevels() throws MetadataResolvingException { exception.expect(MetadataResolvingException.class); exception.expectMessage(is("The given MetadataKey does not provide all the required levels. Missing levels: [city]")); setParameters(continentParam, countryParam, cityParam); setMetadataKeyIdModelProperty(LocationKey.class); keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); keyIdObjectResolver.resolve(INCOMPLETE_MULTILEVEL_KEY); } @Test public void failToResolveWithOutMetadataKeyId() throws MetadataResolvingException { exception.expect(MetadataResolvingException.class); exception.expectMessage(is("Component 'SomeOperation' doesn't have a MetadataKeyId parameter associated")); setParameters(continentParam, countryParam, cityParam); when(componentModel.getModelProperty(MetadataKeyIdModelProperty.class)).thenReturn(empty()); keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); keyIdObjectResolver.resolve(MULTILEVEL_KEY); } @Test public void failToResolveWithNoDefaultValues() throws MetadataResolvingException { exception.expect(IllegalArgumentException.class); exception.expectMessage(containsString("does not have a default value for all it's components.")); setParameters(continentParam, countryParam, cityParam); setMetadataKeyIdModelProperty(LocationKey.class); when(continentParam.getDefaultValue()).thenReturn(AMERICA); when(cityParam.getDefaultValue()).thenReturn(SFO); keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); keyIdObjectResolver.resolve(); } @Test public void failToResolveWithMultipleChildren() throws MetadataResolvingException { exception.expect(MetadataResolvingException.class); exception .expectMessage( is("MetadataKey used for Metadata resolution must only have one child per level. Key 'USA' has [SFO, NY] as children.")); final MetadataKey invalidMetadataKey = newKey(AMERICA, CONTINENT) .withChild(newKey(USA, COUNTRY).withChild(newKey(SFO, CITY)).withChild(newKey(NY, CITY))).build(); setParameters(continentParam, countryParam, cityParam); setMetadataKeyIdModelProperty(LocationKey.class); keyIdObjectResolver = new MetadataKeyIdObjectResolver(componentModel); keyIdObjectResolver.resolve(invalidMetadataKey); } public void setParameters(ParameterModel... parameterModels) { when(componentModel.getAllParameterModels()).thenReturn(asList(parameterModels)); } private void setMetadataKeyIdModelProperty(Class<?> type) { when(componentModel.getModelProperty(MetadataKeyIdModelProperty.class)) .thenReturn(of(new MetadataKeyIdModelProperty(new JavaTypeLoader(this.getClass().getClassLoader()).load(type), EMPTY))); } private class NotInstantiableClass { } }