Merge branch 'master' into canonical-ui-plugin
Original commit: elastic/x-pack-elasticsearch@953713b2df
This commit is contained in:
commit
4d44ab06dc
|
@ -196,7 +196,7 @@ public class SearchTransformIT extends ESIntegTestCase {
|
|||
assertThat(result.type(), is(SearchTransform.TYPE));
|
||||
assertThat(result.status(), is(Transform.Result.Status.FAILURE));
|
||||
assertThat(result.reason(), notNullValue());
|
||||
assertThat(result.reason(), containsString("No query registered for [_unknown_query_]"));
|
||||
assertThat(result.reason(), containsString("no [query] registered for [_unknown_query_]"));
|
||||
|
||||
// extract the base64 encoded query from the template script, path is: query -> wrapper -> query
|
||||
String jsonQuery = result.executedRequest().template().getScript();
|
||||
|
|
|
@ -50,8 +50,8 @@ public class ShieldNettyHttpServerTransport extends NettyHttpServerTransport {
|
|||
private final boolean ssl;
|
||||
|
||||
@Inject
|
||||
public ShieldNettyHttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays,
|
||||
IPFilter ipFilter, ServerSSLService sslService, ThreadPool threadPool) {
|
||||
public ShieldNettyHttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays, IPFilter ipFilter,
|
||||
ServerSSLService sslService, ThreadPool threadPool) {
|
||||
super(settings, networkService, bigArrays, threadPool);
|
||||
this.ipFilter = ipFilter;
|
||||
this.ssl = SSL_SETTING.get(settings);
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.elasticsearch.common.settings.Setting.Property;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.settings.SettingsModule;
|
||||
import org.elasticsearch.common.util.BigArrays;
|
||||
import org.elasticsearch.indices.breaker.CircuitBreakerService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.shield.ssl.ServerSSLService;
|
||||
import org.elasticsearch.shield.transport.SSLClientAuth;
|
||||
|
@ -79,8 +80,9 @@ public class ShieldNettyTransport extends NettyTransport {
|
|||
@Inject
|
||||
public ShieldNettyTransport(Settings settings, ThreadPool threadPool, NetworkService networkService, BigArrays bigArrays,
|
||||
Version version, @Nullable IPFilter authenticator, @Nullable ServerSSLService serverSSLService,
|
||||
ClientSSLService clientSSLService, NamedWriteableRegistry namedWriteableRegistry) {
|
||||
super(settings, threadPool, networkService, bigArrays, version, namedWriteableRegistry);
|
||||
ClientSSLService clientSSLService, NamedWriteableRegistry namedWriteableRegistry,
|
||||
CircuitBreakerService circuitBreakerService) {
|
||||
super(settings, threadPool, networkService, bigArrays, version, namedWriteableRegistry, circuitBreakerService);
|
||||
this.authenticator = authenticator;
|
||||
this.ssl = SSL_SETTING.get(settings);
|
||||
this.serverSslService = serverSSLService;
|
||||
|
|
|
@ -37,6 +37,6 @@ public class VersionCompatibilityTests extends ESTestCase {
|
|||
*
|
||||
*/
|
||||
assertThat("Remove workaround in LicenseService class when es core supports merging cluster level custom metadata",
|
||||
Version.CURRENT.onOrBefore(Version.V_5_0_0_alpha1), is(true));
|
||||
Version.CURRENT.onOrBefore(Version.V_5_0_0), is(true));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.elasticsearch.common.network.NetworkService;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.BigArrays;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.indices.breaker.CircuitBreakerService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.shield.ssl.ServerSSLService;
|
||||
import org.elasticsearch.shield.transport.SSLClientAuth;
|
||||
|
@ -49,7 +50,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
public void testThatSSLCanBeDisabledByProfile() throws Exception {
|
||||
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class));
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
|
||||
mock(CircuitBreakerService.class));
|
||||
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
|
||||
Settings.builder().put("xpack.security.ssl", false).build());
|
||||
|
@ -59,7 +61,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
public void testThatSSLCanBeEnabledByProfile() throws Exception {
|
||||
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), false).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class));
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
|
||||
mock(CircuitBreakerService.class));
|
||||
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
|
||||
Settings.builder().put("xpack.security.ssl", true).build());
|
||||
|
@ -69,7 +72,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
public void testThatProfileTakesDefaultSSLSetting() throws Exception {
|
||||
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class));
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
|
||||
mock(CircuitBreakerService.class));
|
||||
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
|
||||
assertThat(factory.getPipeline().get(SslHandler.class).getEngine(), notNullValue());
|
||||
|
@ -78,7 +82,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
public void testDefaultClientAuth() throws Exception {
|
||||
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class));
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
|
||||
mock(CircuitBreakerService.class));
|
||||
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
|
||||
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(true));
|
||||
|
@ -91,7 +96,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
.put(ShieldNettyTransport.SSL_SETTING.getKey(), true)
|
||||
.put(ShieldNettyTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class));
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
|
||||
mock(CircuitBreakerService.class));
|
||||
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
|
||||
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(true));
|
||||
|
@ -104,7 +110,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
.put(ShieldNettyTransport.SSL_SETTING.getKey(), true)
|
||||
.put(ShieldNettyTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class));
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
|
||||
mock(CircuitBreakerService.class));
|
||||
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
|
||||
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
|
||||
|
@ -117,7 +124,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
.put(ShieldNettyTransport.SSL_SETTING.getKey(), true)
|
||||
.put(ShieldNettyTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class));
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
|
||||
mock(CircuitBreakerService.class));
|
||||
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
|
||||
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
|
||||
|
@ -128,7 +136,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
String value = randomFrom(SSLClientAuth.REQUIRED.name(), SSLClientAuth.REQUIRED.name().toLowerCase(Locale.ROOT), "true", "TRUE");
|
||||
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class));
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
|
||||
mock(CircuitBreakerService.class));
|
||||
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
|
||||
Settings.builder().put(ShieldNettyTransport.PROFILE_CLIENT_AUTH_SETTING, value).build());
|
||||
|
@ -140,7 +149,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
String value = randomFrom(SSLClientAuth.NO.name(), "false", "FALSE", SSLClientAuth.NO.name().toLowerCase(Locale.ROOT));
|
||||
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class),
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class));
|
||||
mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
|
||||
mock(CircuitBreakerService.class));
|
||||
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
|
||||
Settings.builder().put(ShieldNettyTransport.PROFILE_CLIENT_AUTH_SETTING.getKey(), value).build());
|
||||
|
@ -153,7 +163,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
Settings settings = Settings.builder().put(ShieldNettyTransport.SSL_SETTING.getKey(), true).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class),
|
||||
mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService,
|
||||
mock(NamedWriteableRegistry.class));
|
||||
mock(NamedWriteableRegistry.class),
|
||||
mock(CircuitBreakerService.class));
|
||||
NettyMockUtil.setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
|
||||
Settings.builder().put(ShieldNettyTransport.PROFILE_CLIENT_AUTH_SETTING.getKey(), value).build());
|
||||
|
|
|
@ -149,7 +149,9 @@ public class HttpResponse implements ToXContent {
|
|||
if (!headers.isEmpty()) {
|
||||
builder.startObject(Field.HEADERS.getPreferredName());
|
||||
for (Map.Entry<String, String[]> header : headers.entrySet()) {
|
||||
builder.array(header.getKey(), header.getValue());
|
||||
// in order to prevent dots in field names, that might occur in headers, we simply de_dot those header names
|
||||
// when writing toXContent
|
||||
builder.array(header.getKey().replaceAll("\\.", "_"), header.getValue());
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
|
|
|
@ -136,9 +136,9 @@ public class WatcherUtilsTests extends ESTestCase {
|
|||
builder = WatcherUtils.writeSearchRequest(expectedRequest, builder, ToXContent.EMPTY_PARAMS);
|
||||
XContentParser parser = XContentHelper.createParser(builder.bytes());
|
||||
assertThat(parser.nextToken(), equalTo(XContentParser.Token.START_OBJECT));
|
||||
QueryParser<MatchAllQueryBuilder> termQueryParser = MatchAllQueryBuilder::fromXContent;
|
||||
IndicesQueriesRegistry registry = new IndicesQueriesRegistry(Settings.EMPTY,
|
||||
singletonMap(MatchAllQueryBuilder.NAME, new Tuple<>(MatchAllQueryBuilder.QUERY_NAME_FIELD, termQueryParser)));
|
||||
IndicesQueriesRegistry registry = new IndicesQueriesRegistry();
|
||||
QueryParser<MatchAllQueryBuilder> queryParser = MatchAllQueryBuilder::fromXContent;
|
||||
registry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
|
||||
QueryParseContext context = new QueryParseContext(registry);
|
||||
context.reset(parser);
|
||||
SearchRequest result = WatcherUtils.readSearchRequest(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE, context, null, null);
|
||||
|
@ -226,9 +226,9 @@ public class WatcherUtilsTests extends ESTestCase {
|
|||
|
||||
XContentParser parser = XContentHelper.createParser(builder.bytes());
|
||||
assertThat(parser.nextToken(), equalTo(XContentParser.Token.START_OBJECT));
|
||||
QueryParser<MatchAllQueryBuilder> termQueryParser = MatchAllQueryBuilder::fromXContent;
|
||||
IndicesQueriesRegistry registry = new IndicesQueriesRegistry(Settings.EMPTY,
|
||||
singletonMap(MatchAllQueryBuilder.NAME, new Tuple<>(MatchAllQueryBuilder.QUERY_NAME_FIELD, termQueryParser)));
|
||||
IndicesQueriesRegistry registry = new IndicesQueriesRegistry();
|
||||
QueryParser<MatchAllQueryBuilder> queryParser = MatchAllQueryBuilder::fromXContent;
|
||||
registry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
|
||||
QueryParseContext context = new QueryParseContext(registry);
|
||||
context.reset(parser);
|
||||
SearchRequest result = WatcherUtils.readSearchRequest(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE, context, null, null);
|
||||
|
|
|
@ -5,11 +5,15 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support.http;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
|
@ -21,13 +25,18 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
|||
import static org.elasticsearch.watcher.test.WatcherTestUtils.xContentParser;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.Matchers.arrayContaining;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.hasKey;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class HttpResponseTests extends ESTestCase {
|
||||
|
||||
public void testParseSelfGenerated() throws Exception {
|
||||
int status = randomIntBetween(200, 600);
|
||||
Map<String, String[]> headers = emptyMap();
|
||||
|
@ -91,4 +100,31 @@ public class HttpResponseTests extends ESTestCase {
|
|||
assertThat(response.header("key")[0], is("value"));
|
||||
assertThat(response.contentType(), is("text/html"));
|
||||
}
|
||||
|
||||
public void testThatHeaderNamesDoNotContainDotsOnSerialization() throws Exception {
|
||||
Map<String, String[]> headers = new HashMap<>();
|
||||
headers.put("es.index", new String[] { "value" });
|
||||
headers.put("es.index.2", new String[] { "value" });
|
||||
|
||||
HttpResponse response = new HttpResponse(200, headers);
|
||||
assertThat(response.header("es.index")[0], is("value"));
|
||||
assertThat(response.header("es.index.2")[0], is("value"));
|
||||
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
XContentParser parser = XContentFactory.xContent(builder.string()).createParser(builder.string());
|
||||
Map<String, Object> responseMap = parser.map();
|
||||
parser.close();
|
||||
|
||||
assertThat(responseMap, hasKey("headers"));
|
||||
assertThat(responseMap.get("headers"), instanceOf(Map.class));
|
||||
Map<String, Object> responseHeaders = (Map<String, Object>) responseMap.get("headers");
|
||||
|
||||
assertThat(responseHeaders, not(hasKey("es.index")));
|
||||
assertThat(responseHeaders, hasEntry("es_index", Lists.newArrayList("value")));
|
||||
|
||||
assertThat(responseHeaders, not(hasKey("es.index.2")));
|
||||
assertThat(responseHeaders, hasEntry("es_index_2", Lists.newArrayList("value")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -353,11 +353,11 @@ public class WatchTests extends ESTestCase {
|
|||
Map<String, InputFactory> parsers = new HashMap<>();
|
||||
switch (input.type()) {
|
||||
case SearchInput.TYPE:
|
||||
QueryParser<MatchAllQueryBuilder> termQueryParser = MatchAllQueryBuilder::fromXContent;
|
||||
IndicesQueriesRegistry queryRegistry = new IndicesQueriesRegistry(Settings.EMPTY, singletonMap(
|
||||
MatchAllQueryBuilder.NAME, new Tuple<>(MatchAllQueryBuilder.QUERY_NAME_FIELD, termQueryParser)));
|
||||
parsers.put(SearchInput.TYPE, new SearchInputFactory(settings, client, queryRegistry, null, null));
|
||||
return new InputRegistry(parsers);
|
||||
IndicesQueriesRegistry queryRegistry = new IndicesQueriesRegistry();
|
||||
QueryParser<MatchAllQueryBuilder> queryParser = MatchAllQueryBuilder::fromXContent;
|
||||
queryRegistry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
|
||||
parsers.put(SearchInput.TYPE, new SearchInputFactory(settings, client, queryRegistry, null, null));
|
||||
return new InputRegistry(parsers);
|
||||
default:
|
||||
parsers.put(SimpleInput.TYPE, new SimpleInputFactory(settings));
|
||||
return new InputRegistry(parsers);
|
||||
|
@ -421,9 +421,9 @@ public class WatchTests extends ESTestCase {
|
|||
}
|
||||
|
||||
private TransformRegistry transformRegistry() {
|
||||
QueryParser<MatchAllQueryBuilder> termQueryParser = MatchAllQueryBuilder::fromXContent;
|
||||
IndicesQueriesRegistry queryRegistry = new IndicesQueriesRegistry(Settings.EMPTY,
|
||||
singletonMap(MatchAllQueryBuilder.NAME, new Tuple<>(MatchAllQueryBuilder.QUERY_NAME_FIELD, termQueryParser)));
|
||||
IndicesQueriesRegistry queryRegistry = new IndicesQueriesRegistry();
|
||||
QueryParser<MatchAllQueryBuilder> queryParser = MatchAllQueryBuilder::fromXContent;
|
||||
queryRegistry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
|
||||
Map<String, TransformFactory> factories = new HashMap<>();
|
||||
ChainTransformFactory parser = new ChainTransformFactory();
|
||||
factories.put(ChainTransform.TYPE, parser);
|
||||
|
|
Loading…
Reference in New Issue