/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you 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.
 */

/*
 * Modifications Copyright OpenSearch Contributors. See
 * GitHub history for details.
 */

package org.opensearch.client.documentation;

import org.opensearch.action.LatchedActionListener;
import org.opensearch.action.admin.cluster.health.ClusterHealthRequest;
import org.opensearch.action.admin.cluster.health.ClusterHealthResponse;
import org.opensearch.action.admin.cluster.settings.ClusterGetSettingsRequest;
import org.opensearch.action.admin.cluster.settings.ClusterGetSettingsResponse;
import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse;
import org.opensearch.action.support.ActiveShardCount;
import org.opensearch.action.support.clustermanager.AcknowledgedResponse;
import org.opensearch.client.OpenSearchRestHighLevelClientTestCase;
import org.opensearch.client.RequestOptions;
import org.opensearch.client.RestHighLevelClient;
import org.opensearch.client.cluster.RemoteConnectionInfo;
import org.opensearch.client.cluster.RemoteInfoRequest;
import org.opensearch.client.cluster.RemoteInfoResponse;
import org.opensearch.client.indices.CreateIndexRequest;
import org.opensearch.client.indices.DeleteComponentTemplateRequest;
import org.opensearch.client.indices.GetComponentTemplatesRequest;
import org.opensearch.client.indices.GetComponentTemplatesResponse;
import org.opensearch.client.indices.PutComponentTemplateRequest;
import org.opensearch.cluster.health.ClusterHealthStatus;
import org.opensearch.cluster.health.ClusterIndexHealth;
import org.opensearch.cluster.health.ClusterShardHealth;
import org.opensearch.cluster.metadata.AliasMetadata;
import org.opensearch.cluster.metadata.ComponentTemplate;
import org.opensearch.cluster.metadata.Template;
import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider;
import org.opensearch.common.Priority;
import org.opensearch.common.compress.CompressedXContent;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.unit.TimeValue;
import org.opensearch.core.action.ActionListener;
import org.opensearch.core.common.unit.ByteSizeUnit;
import org.opensearch.core.rest.RestStatus;
import org.opensearch.core.xcontent.MediaTypeRegistry;
import org.opensearch.indices.recovery.RecoverySettings;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;

/**
 * Documentation for Cluster APIs in the high level java client.
 * Code wrapped in {@code tag} and {@code end} tags is included in the docs.
 */
public class ClusterClientDocumentationIT extends OpenSearchRestHighLevelClientTestCase {

    public void testClusterPutSettings() throws IOException {
        RestHighLevelClient client = highLevelClient();

        // tag::put-settings-request
        ClusterUpdateSettingsRequest request = new ClusterUpdateSettingsRequest();
        // end::put-settings-request

        // tag::put-settings-create-settings
        String transientSettingKey =
                RecoverySettings.INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey();
        int transientSettingValue = 10;
        Settings transientSettings =
                Settings.builder()
                .put(transientSettingKey, transientSettingValue, ByteSizeUnit.BYTES)
                .build(); // <1>

        String persistentSettingKey =
                EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE_SETTING.getKey();
        String persistentSettingValue =
                EnableAllocationDecider.Allocation.NONE.name();
        Settings persistentSettings =
                Settings.builder()
                .put(persistentSettingKey, persistentSettingValue)
                .build(); // <2>
        // end::put-settings-create-settings

        // tag::put-settings-request-cluster-settings
        request.transientSettings(transientSettings); // <1>
        request.persistentSettings(persistentSettings); // <2>
        // end::put-settings-request-cluster-settings

        {
            // tag::put-settings-settings-builder
            Settings.Builder transientSettingsBuilder =
                    Settings.builder()
                    .put(transientSettingKey, transientSettingValue, ByteSizeUnit.BYTES);
            request.transientSettings(transientSettingsBuilder); // <1>
            // end::put-settings-settings-builder
        }
        {
            // tag::put-settings-settings-map
            Map<String, Object> map = new HashMap<>();
            map.put(transientSettingKey
                    , transientSettingValue + ByteSizeUnit.BYTES.getSuffix());
            request.transientSettings(map); // <1>
            // end::put-settings-settings-map
        }
        {
            // tag::put-settings-settings-source
            request.transientSettings(
                    "{\"indices.recovery.max_bytes_per_sec\": \"10b\"}"
                    , MediaTypeRegistry.JSON); // <1>
            // end::put-settings-settings-source
        }

        // tag::put-settings-request-timeout
        request.timeout(TimeValue.timeValueMinutes(2)); // <1>
        request.timeout("2m"); // <2>
        // end::put-settings-request-timeout
        // tag::put-settings-request-masterTimeout
        request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
        request.clusterManagerNodeTimeout("1m"); // <2>
        // end::put-settings-request-masterTimeout

        // tag::put-settings-execute
        ClusterUpdateSettingsResponse response = client.cluster().putSettings(request, RequestOptions.DEFAULT);
        // end::put-settings-execute

        // tag::put-settings-response
        boolean acknowledged = response.isAcknowledged(); // <1>
        Settings transientSettingsResponse = response.getTransientSettings(); // <2>
        Settings persistentSettingsResponse = response.getPersistentSettings(); // <3>
        // end::put-settings-response
        assertTrue(acknowledged);
        assertThat(transientSettingsResponse.get(transientSettingKey), equalTo(transientSettingValue + ByteSizeUnit.BYTES.getSuffix()));
        assertThat(persistentSettingsResponse.get(persistentSettingKey), equalTo(persistentSettingValue));

        // tag::put-settings-request-reset-transient
        request.transientSettings(Settings.builder().putNull(transientSettingKey).build()); // <1>
        // tag::put-settings-request-reset-transient
        request.persistentSettings(Settings.builder().putNull(persistentSettingKey));
        ClusterUpdateSettingsResponse resetResponse = client.cluster().putSettings(request, RequestOptions.DEFAULT);

        assertTrue(resetResponse.isAcknowledged());
    }

    public void testClusterUpdateSettingsAsync() throws Exception {
        RestHighLevelClient client = highLevelClient();
        {
            ClusterUpdateSettingsRequest request = new ClusterUpdateSettingsRequest();

            // tag::put-settings-execute-listener
            ActionListener<ClusterUpdateSettingsResponse> listener =
                    new ActionListener<ClusterUpdateSettingsResponse>() {
                @Override
                public void onResponse(ClusterUpdateSettingsResponse response) {
                    // <1>
                }

                @Override
                public void onFailure(Exception e) {
                    // <2>
                }
            };
            // end::put-settings-execute-listener

            // Replace the empty listener by a blocking listener in test
            final CountDownLatch latch = new CountDownLatch(1);
            listener = new LatchedActionListener<>(listener, latch);

            // tag::put-settings-execute-async
            client.cluster().putSettingsAsync(request, RequestOptions.DEFAULT, listener); // <1>
            // end::put-settings-execute-async

            assertTrue(latch.await(30L, TimeUnit.SECONDS));
        }
    }

    @SuppressWarnings("unused")
    public void testClusterGetSettings() throws IOException {
        RestHighLevelClient client = highLevelClient();

        // tag::get-settings-request
        ClusterGetSettingsRequest request = new ClusterGetSettingsRequest();
        // end::get-settings-request

        // tag::get-settings-request-includeDefaults
        request.includeDefaults(true); // <1>
        // end::get-settings-request-includeDefaults

        // tag::get-settings-request-local
        request.local(true); // <1>
        // end::get-settings-request-local

        // tag::get-settings-request-masterTimeout
        request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
        request.clusterManagerNodeTimeout("1m"); // <2>
        // end::get-settings-request-masterTimeout

        // tag::get-settings-execute
        ClusterGetSettingsResponse response = client.cluster().getSettings(request, RequestOptions.DEFAULT); // <1>
        // end::get-settings-execute

        // tag::get-settings-response
        Settings persistentSettings = response.getPersistentSettings(); // <1>
        Settings transientSettings = response.getTransientSettings(); // <2>
        Settings defaultSettings = response.getDefaultSettings(); // <3>
        String settingValue = response.getSetting("cluster.routing.allocation.enable"); // <4>
        // end::get-settings-response

        assertThat(defaultSettings.size(), greaterThan(0));
    }

    public void testClusterGetSettingsAsync() throws InterruptedException {
        RestHighLevelClient client = highLevelClient();

        ClusterGetSettingsRequest request = new ClusterGetSettingsRequest();

        // tag::get-settings-execute-listener
        ActionListener<ClusterGetSettingsResponse> listener =
            new ActionListener<ClusterGetSettingsResponse>() {
                @Override
                public void onResponse(ClusterGetSettingsResponse response) {
                    // <1>
                }

                @Override
                public void onFailure(Exception e) {
                    // <2>
                }
            };
        // end::get-settings-execute-listener

        // Replace the empty listener by a blocking listener in test
        final CountDownLatch latch = new CountDownLatch(1);
        listener = new LatchedActionListener<>(listener, latch);

        // tag::get-settings-execute-async
        client.cluster().getSettingsAsync(request, RequestOptions.DEFAULT, listener); // <1>
        // end::get-settings-execute-async

        assertTrue(latch.await(30L, TimeUnit.SECONDS));
    }

    @SuppressWarnings("unused")
    public void testClusterHealth() throws IOException {
        RestHighLevelClient client = highLevelClient();
        client.indices().create(new CreateIndexRequest("index"), RequestOptions.DEFAULT);
        {
            // tag::health-request
            ClusterHealthRequest request = new ClusterHealthRequest();
            // end::health-request
        }
        {
            // tag::health-request-indices-ctr
            ClusterHealthRequest request = new ClusterHealthRequest("index1", "index2");
            // end::health-request-indices-ctr
        }
        {
            // tag::health-request-indices-setter
            ClusterHealthRequest request = new ClusterHealthRequest();
            request.indices("index1", "index2");
            // end::health-request-indices-setter
        }
        ClusterHealthRequest request = new ClusterHealthRequest();

        // tag::health-request-timeout
        request.timeout(TimeValue.timeValueSeconds(50)); // <1>
        request.timeout("50s"); // <2>
        // end::health-request-timeout

        // tag::health-request-master-timeout
        request.clusterManagerNodeTimeout(TimeValue.timeValueSeconds(20)); // <1>
        request.clusterManagerNodeTimeout("20s"); // <2>
        // end::health-request-master-timeout

        // tag::health-request-wait-status
        request.waitForStatus(ClusterHealthStatus.YELLOW); // <1>
        request.waitForYellowStatus(); // <2>
        // end::health-request-wait-status

        // tag::health-request-wait-events
        request.waitForEvents(Priority.NORMAL); // <1>
        // end::health-request-wait-events

        // tag::health-request-level
        request.level(ClusterHealthRequest.Level.SHARDS); // <1>
        // end::health-request-level

        // tag::health-request-wait-relocation
        request.waitForNoRelocatingShards(true); // <1>
        // end::health-request-wait-relocation

        // tag::health-request-wait-initializing
        request.waitForNoInitializingShards(true); // <1>
        // end::health-request-wait-initializing

        // tag::health-request-wait-nodes
        request.waitForNodes("2"); // <1>
        request.waitForNodes(">=2"); // <2>
        request.waitForNodes("le(2)"); // <3>
        // end::health-request-wait-nodes

        // tag::health-request-wait-active
        request.waitForActiveShards(ActiveShardCount.ALL); // <1>
        request.waitForActiveShards(1); // <2>
        // end::health-request-wait-active

        // tag::health-request-local
        request.local(true); // <1>
        // end::health-request-local

        // tag::health-execute
        ClusterHealthResponse response = client.cluster().health(request, RequestOptions.DEFAULT);
        // end::health-execute

        assertThat(response.isTimedOut(), equalTo(false));
        assertThat(response.status(), equalTo(RestStatus.OK));
        assertThat(response.getStatus(), equalTo(ClusterHealthStatus.YELLOW));
        assertThat(response, notNullValue());
        // tag::health-response-general
        String clusterName = response.getClusterName(); // <1>
        ClusterHealthStatus status = response.getStatus(); // <2>
        // end::health-response-general

        // tag::health-response-request-status
        boolean timedOut = response.isTimedOut(); // <1>
        RestStatus restStatus = response.status(); // <2>
        // end::health-response-request-status

        // tag::health-response-nodes
        int numberOfNodes = response.getNumberOfNodes(); // <1>
        int numberOfDataNodes = response.getNumberOfDataNodes(); // <2>
        // end::health-response-nodes

        {
            // tag::health-response-shards
            int activeShards = response.getActiveShards(); // <1>
            int activePrimaryShards = response.getActivePrimaryShards(); // <2>
            int relocatingShards = response.getRelocatingShards(); // <3>
            int initializingShards = response.getInitializingShards(); // <4>
            int unassignedShards = response.getUnassignedShards(); // <5>
            int delayedUnassignedShards = response.getDelayedUnassignedShards(); // <6>
            double activeShardsPercent = response.getActiveShardsPercent(); // <7>
            // end::health-response-shards
        }

        // tag::health-response-task
        TimeValue taskMaxWaitingTime = response.getTaskMaxWaitingTime(); // <1>
        int numberOfPendingTasks = response.getNumberOfPendingTasks(); // <2>
        int numberOfInFlightFetch = response.getNumberOfInFlightFetch(); // <3>
        // end::health-response-task

        // tag::health-response-indices
        Map<String, ClusterIndexHealth> indices = response.getIndices(); // <1>
        // end::health-response-indices

        {
            // tag::health-response-index
            ClusterIndexHealth index = indices.get("index"); // <1>
            ClusterHealthStatus indexStatus = index.getStatus();
            int numberOfShards = index.getNumberOfShards();
            int numberOfReplicas = index.getNumberOfReplicas();
            int activeShards = index.getActiveShards();
            int activePrimaryShards = index.getActivePrimaryShards();
            int initializingShards = index.getInitializingShards();
            int relocatingShards = index.getRelocatingShards();
            int unassignedShards = index.getUnassignedShards();
            // end::health-response-index

            // tag::health-response-shard-details
            Map<Integer, ClusterShardHealth> shards = index.getShards(); // <1>
            ClusterShardHealth shardHealth = shards.get(0);
            int shardId = shardHealth.getShardId();
            ClusterHealthStatus shardStatus = shardHealth.getStatus();
            int active = shardHealth.getActiveShards();
            int initializing = shardHealth.getInitializingShards();
            int unassigned = shardHealth.getUnassignedShards();
            int relocating = shardHealth.getRelocatingShards();
            boolean primaryActive = shardHealth.isPrimaryActive();
            // end::health-response-shard-details
        }
    }

    public void testClusterHealthAsync() throws Exception {
        RestHighLevelClient client = highLevelClient();
        {
            ClusterHealthRequest request = new ClusterHealthRequest();

            // tag::health-execute-listener
            ActionListener<ClusterHealthResponse> listener =
                new ActionListener<ClusterHealthResponse>() {
                    @Override
                    public void onResponse(ClusterHealthResponse response) {
                        // <1>
                    }

                    @Override
                    public void onFailure(Exception e) {
                        // <2>
                    }
                };
            // end::health-execute-listener

            // Replace the empty listener by a blocking listener in test
            final CountDownLatch latch = new CountDownLatch(1);
            listener = new LatchedActionListener<>(listener, latch);

            // tag::health-execute-async
            client.cluster().healthAsync(request, RequestOptions.DEFAULT, listener); // <1>
            // end::health-execute-async

            assertTrue(latch.await(30L, TimeUnit.SECONDS));
        }
    }

    public void testRemoteInfo() throws Exception {
        setupRemoteClusterConfig("local_cluster");

        RestHighLevelClient client = highLevelClient();

        // tag::remote-info-request
        RemoteInfoRequest request = new RemoteInfoRequest();
        // end::remote-info-request

        // tag::remote-info-execute
        RemoteInfoResponse response = client.cluster().remoteInfo(request, RequestOptions.DEFAULT); // <1>
        // end::remote-info-execute

        // tag::remote-info-response
        List<RemoteConnectionInfo> infos = response.getInfos();
        // end::remote-info-response

        assertThat(infos.size(), greaterThan(0));
    }

    public void testRemoteInfoAsync() throws Exception {
        setupRemoteClusterConfig("local_cluster");

        RestHighLevelClient client = highLevelClient();

        // tag::remote-info-request
        RemoteInfoRequest request = new RemoteInfoRequest();
        // end::remote-info-request

        // tag::remote-info-execute-listener
            ActionListener<RemoteInfoResponse> listener =
                new ActionListener<RemoteInfoResponse>() {
                    @Override
                    public void onResponse(RemoteInfoResponse response) {
                        // <1>
                    }

                    @Override
                    public void onFailure(Exception e) {
                        // <2>
                    }
                };
            // end::remote-info-execute-listener

        // Replace the empty listener by a blocking listener in test
        final CountDownLatch latch = new CountDownLatch(1);
        listener = new LatchedActionListener<>(listener, latch);

        // tag::health-execute-async
            client.cluster().remoteInfoAsync(request, RequestOptions.DEFAULT, listener); // <1>
        // end::health-execute-async

        assertTrue(latch.await(30L, TimeUnit.SECONDS));
    }

    public void testGetComponentTemplates() throws Exception {
        RestHighLevelClient client = highLevelClient();
        {
            Template template = new Template(Settings.builder().put("index.number_of_replicas", 3).build(), null, null);
            ComponentTemplate componentTemplate = new ComponentTemplate(template, null, null);
            PutComponentTemplateRequest putComponentTemplateRequest = new PutComponentTemplateRequest().name("ct1")
                .componentTemplate(componentTemplate);
            client.cluster().putComponentTemplate(putComponentTemplateRequest, RequestOptions.DEFAULT);

            assertTrue(client.cluster().putComponentTemplate(putComponentTemplateRequest, RequestOptions.DEFAULT).isAcknowledged());
        }

        // tag::get-component-templates-request
        GetComponentTemplatesRequest request = new GetComponentTemplatesRequest("ct1"); // <1>
        // end::get-component-templates-request

        // tag::get-component-templates-request-masterTimeout
        request.setClusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
        request.setClusterManagerNodeTimeout("1m"); // <2>
        // end::get-component-templates-request-masterTimeout

        // tag::get-component-templates-execute
        GetComponentTemplatesResponse getTemplatesResponse = client.cluster().getComponentTemplate(request, RequestOptions.DEFAULT);
        // end::get-component-templates-execute

        // tag::get-component-templates-response
        Map<String, ComponentTemplate> templates = getTemplatesResponse.getComponentTemplates(); // <1>
        // end::get-component-templates-response

        assertThat(templates.size(), is(1));
        assertThat(templates.get("ct1"), is(notNullValue()));

        // tag::get-component-templates-execute-listener
        ActionListener<GetComponentTemplatesResponse> listener =
            new ActionListener<GetComponentTemplatesResponse>() {
                @Override
                public void onResponse(GetComponentTemplatesResponse response) {
                    // <1>
                }

                @Override
                public void onFailure(Exception e) {
                    // <2>
                }
            };
        // end::get-component-templates-execute-listener

        // Replace the empty listener by a blocking listener in test
        final CountDownLatch latch = new CountDownLatch(1);
        listener = new LatchedActionListener<>(listener, latch);

        // tag::get-component-templates-execute-async
        client.cluster().getComponentTemplateAsync(request, RequestOptions.DEFAULT, listener); // <1>
        // end::get-component-templates-execute-async

        assertTrue(latch.await(30L, TimeUnit.SECONDS));
    }

    public void testPutComponentTemplate() throws Exception {
        RestHighLevelClient client = highLevelClient();

        {
            // tag::put-component-template-request
            PutComponentTemplateRequest request = new PutComponentTemplateRequest()
                .name("ct1"); // <1>

            Settings settings = Settings.builder()
                .put("index.number_of_shards", 3)
                .put("index.number_of_replicas", 1)
                .build();
            String mappingJson = "{\n" +
                "  \"properties\": {\n" +
                "    \"message\": {\n" +
                "      \"type\": \"text\"\n" +
                "    }\n" +
                "  }\n" +
                "}";
            AliasMetadata twitterAlias = AliasMetadata.builder("twitter_alias").build();
            Map<String, AliasMetadata> aliases = new HashMap<>();
            aliases.put("twitter_alias", twitterAlias);
            Template template = new Template(settings, new CompressedXContent(mappingJson), aliases); // <2>

            request.componentTemplate(new ComponentTemplate(template, null, null));
            assertTrue(client.cluster().putComponentTemplate(request, RequestOptions.DEFAULT).isAcknowledged());
            // end::put-component-template-request
        }

        {
            // tag::put-component-template-request-version
            PutComponentTemplateRequest request = new PutComponentTemplateRequest()
                .name("ct1");
            Settings settings = Settings.builder()
                .put("index.number_of_replicas", 3)
                .build();
            Template template = new Template(settings, null, null);

            request.componentTemplate(new ComponentTemplate(template, 3L, null)); // <1>
            assertTrue(client.cluster().putComponentTemplate(request, RequestOptions.DEFAULT).isAcknowledged());
            // end::put-component-template-request-version

            // tag::put-component-template-request-create
            request.create(true);  // <1>
            // end::put-component-template-request-create

            // tag::put-component-template-request-masterTimeout
            request.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1>
            // end::put-component-template-request-masterTimeout

            request.create(false); // make test happy

            // tag::put-component-template-request-execute
            AcknowledgedResponse putComponentTemplateResponse = client.cluster().putComponentTemplate(request, RequestOptions.DEFAULT);
            // end::put-component-template-request-execute

            // tag::put-component-template-response
            boolean acknowledged = putComponentTemplateResponse.isAcknowledged(); // <1>
            // end::put-component-template-response
            assertTrue(acknowledged);

            // tag::put-component-template-execute-listener
            ActionListener<AcknowledgedResponse> listener =
                new ActionListener<AcknowledgedResponse>() {
                    @Override
                    public void onResponse(AcknowledgedResponse putComponentTemplateResponse) {
                        // <1>
                    }

                    @Override
                    public void onFailure(Exception e) {
                        // <2>
                    }
                };
            // end::put-component-template-execute-listener

            final CountDownLatch latch = new CountDownLatch(1);
            listener = new LatchedActionListener<>(listener, latch);

            // tag::put-component-template-execute-async
            client.cluster().putComponentTemplateAsync(request, RequestOptions.DEFAULT, listener); // <1>
            // end::put-component-template-execute-async

            assertTrue(latch.await(30L, TimeUnit.SECONDS));
        }
    }

    public void testDeleteComponentTemplate() throws Exception {
        RestHighLevelClient client = highLevelClient();
        {
            PutComponentTemplateRequest request = new PutComponentTemplateRequest().name("ct1");

            Settings settings = Settings.builder().put("index.number_of_shards", 3).put("index.number_of_replicas", 1).build();
            String mappingJson = "{\n"
                + "  \"properties\": {\n"
                + "    \"message\": {\n"
                + "      \"type\": \"text\"\n"
                + "    }\n"
                + "  }\n"
                + "}";
            AliasMetadata twitterAlias = AliasMetadata.builder("twitter_alias").build();
            Map<String, AliasMetadata> aliases = new HashMap<>();
            aliases.put("twitter_alias", twitterAlias);
            Template template = new Template(settings, new CompressedXContent(mappingJson), aliases);

            request.componentTemplate(new ComponentTemplate(template, null, null));
            assertTrue(client.cluster().putComponentTemplate(request, RequestOptions.DEFAULT).isAcknowledged());
        }

        // tag::delete-component-template-request
        DeleteComponentTemplateRequest deleteRequest = new DeleteComponentTemplateRequest("ct1"); // <1>
        // end::delete-component-template-request

        // tag::delete-component-template-request-masterTimeout
        deleteRequest.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1>
        // end::delete-component-template-request-masterTimeout

        // tag::delete-component-template-execute
        AcknowledgedResponse deleteTemplateAcknowledge = client.cluster().deleteComponentTemplate(deleteRequest, RequestOptions.DEFAULT);
        // end::delete-component-template-execute

        // tag::delete-component-template-response
        boolean acknowledged = deleteTemplateAcknowledge.isAcknowledged(); // <1>
        // end::delete-component-template-response
        assertThat(acknowledged, equalTo(true));

        {
            PutComponentTemplateRequest request = new PutComponentTemplateRequest().name("ct1");

            Settings settings = Settings.builder().put("index.number_of_shards", 3).put("index.number_of_replicas", 1).build();
            Template template = new Template(settings, null, null);
            request.componentTemplate(new ComponentTemplate(template, null, null));
            assertTrue(client.cluster().putComponentTemplate(request, RequestOptions.DEFAULT).isAcknowledged());
        }

        // tag::delete-component-template-execute-listener
        ActionListener<AcknowledgedResponse> listener =
            new ActionListener<AcknowledgedResponse>() {
                @Override
                public void onResponse(AcknowledgedResponse response) {
                    // <1>
                }

                @Override
                public void onFailure(Exception e) {
                    // <2>
                }
            };
        // end::delete-component-template-execute-listener

        final CountDownLatch latch = new CountDownLatch(1);
        listener = new LatchedActionListener<>(listener, latch);

        // tag::delete-component-template-execute-async
        client.cluster().deleteComponentTemplateAsync(deleteRequest, RequestOptions.DEFAULT, listener); // <1>
        // end::delete-component-template-execute-async

        assertTrue(latch.await(30L, TimeUnit.SECONDS));
    }
}
