/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.alibaba.nacos.test.config;

import com.alibaba.nacos.Nacos;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.client.config.http.HttpAgent;
import com.alibaba.nacos.client.config.http.MetricsHttpAgent;
import com.alibaba.nacos.client.config.http.ServerHttpAgent;
import com.alibaba.nacos.client.config.impl.ConfigHttpClientManager;
import com.alibaba.nacos.common.http.HttpRestResult;
import com.alibaba.nacos.common.http.client.NacosRestTemplate;
import com.alibaba.nacos.common.http.client.handler.AbstractResponseHandler;
import com.alibaba.nacos.common.http.client.response.HttpClientResponse;
import com.alibaba.nacos.common.http.param.Header;
import com.alibaba.nacos.common.http.param.Query;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.config.server.constant.Constants;
import com.alibaba.nacos.config.server.model.ConfigMetadata;
import com.alibaba.nacos.config.server.result.code.ResultCodeEnum;
import com.alibaba.nacos.config.server.utils.YamlParserUtil;
import com.alibaba.nacos.config.server.utils.ZipUtils;
import com.alibaba.nacos.test.base.ConfigCleanUtils;
import com.fasterxml.jackson.databind.JsonNode;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.mime.ByteArrayBody;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.io.IOException;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;

/**
 * CITCase for ConfigExportAndImportAPI.
 *
 * @author klw
 * @date 2019/5/23 15:26
 */
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Nacos.class, properties = {
        "server.servlet.context-path=/nacos"}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class ConfigExportAndImportAPIConfigITCase {
    
    private static final long TIME_OUT = 2000;
    
    private static final String CONFIG_CONTROLLER_PATH = "/v1/cs/configs";
    
    @LocalServerPort
    private int port;
    
    private String serverAddr = null;
    
    private HttpAgent agent = null;
    
    private NacosRestTemplate nacosRestTemplate;
    
    @BeforeAll
    @AfterAll
    static void cleanClientCache() throws Exception {
        ConfigCleanUtils.cleanClientCache();
        ConfigCleanUtils.changeToNewTestNacosHome(ConfigExportAndImportAPIConfigITCase.class.getSimpleName());
    }
    
    @BeforeEach
    void setUp() throws Exception {
        nacosRestTemplate = ConfigHttpClientManager.getInstance().getNacosRestTemplate();
        // register a handler to process byte[] result
        nacosRestTemplate.registerResponseHandler(byte[].class.getName(), new AbstractResponseHandler() {
            @Override
            public HttpRestResult<byte[]> convertResult(HttpClientResponse response, Type responseType)
                    throws Exception {
                return new HttpRestResult(response.getHeaders(), response.getStatusCode(),
                        IOUtils.toByteArray(response.getBody()), null);
            }
        });
        
        serverAddr = "http://127.0.0.1" + ":" + port + "/nacos";
        
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1" + ":" + port);
        agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
        agent.start();
        
        Map<String, String> prarm = new HashMap<>(7);
        prarm.put("dataId", "testNoAppname1.yml");
        prarm.put("group", "EXPORT_IMPORT_TEST_GROUP");
        prarm.put("content", "test: test");
        prarm.put("desc", "testNoAppname1");
        prarm.put("type", "yaml");
        assertEquals("true", httpPost(serverAddr + CONFIG_CONTROLLER_PATH, prarm));
        prarm.put("dataId", "testNoAppname2.txt");
        prarm.put("group", "TEST1_GROUP");
        prarm.put("content", "test: test");
        prarm.put("desc", "testNoAppname2");
        prarm.put("type", "text");
        assertEquals("true", httpPost(serverAddr + CONFIG_CONTROLLER_PATH, prarm));
        prarm.put("dataId", "testHasAppname1.properties");
        prarm.put("group", "EXPORT_IMPORT_TEST_GROUP");
        prarm.put("content", "test.test1.value=test");
        prarm.put("desc", "testHasAppname1");
        prarm.put("type", "properties");
        prarm.put("appName", "testApp1");
        assertEquals("true", httpPost(serverAddr + CONFIG_CONTROLLER_PATH, prarm));
    }
    
    @AfterEach
    void cleanup() throws Exception {
        Assertions.assertDoesNotThrow(() -> {
            HttpRestResult<String> testResult;
            Map<String, String> params = new HashMap<>();
            params.put("dataId", "testNoAppname1.yml");
            params.put("group", "EXPORT_IMPORT_TEST_GROUP");
            params.put("beta", "false");
            testResult = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
            assertEquals(HttpURLConnection.HTTP_OK, testResult.getCode());
            
            params.put("dataId", "testNoAppname2.txt");
            params.put("group", "TEST1_GROUP");
            params.put("beta", "false");
            testResult = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
            assertEquals(HttpURLConnection.HTTP_OK, testResult.getCode());
            
            params.put("dataId", "testHasAppname1.properties");
            params.put("group", "EXPORT_IMPORT_TEST_GROUP");
            params.put("beta", "false");
            testResult = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
            assertEquals(HttpURLConnection.HTTP_OK, testResult.getCode());
            
            params.put("dataId", "test1.yml");
            params.put("group", "TEST_IMPORT");
            params.put("beta", "false");
            testResult = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
            assertEquals(HttpURLConnection.HTTP_OK, testResult.getCode());
            
            params.put("dataId", "test2.txt");
            params.put("group", "TEST_IMPORT");
            params.put("beta", "false");
            testResult = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
            assertEquals(HttpURLConnection.HTTP_OK, testResult.getCode());
            
            params.put("dataId", "test3.properties");
            params.put("group", "TEST_IMPORT");
            params.put("beta", "false");
            testResult = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
            assertEquals(HttpURLConnection.HTTP_OK, testResult.getCode());
            
            params.put("dataId", "test1");
            params.put("group", "TEST_IMPORT2");
            params.put("beta", "false");
            testResult = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
            assertEquals(HttpURLConnection.HTTP_OK, testResult.getCode());
            
            params.put("dataId", "test3");
            params.put("group", "TEST_IMPORT2");
            params.put("beta", "false");
            testResult = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
            assertEquals(HttpURLConnection.HTTP_OK, testResult.getCode());
            
            params.put("dataId", "test4");
            params.put("group", "TEST_IMPORT2");
            params.put("beta", "false");
            testResult = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
            assertEquals(HttpURLConnection.HTTP_OK, testResult.getCode());
        });
        agent.shutdown();
    }
    
    @Test
    void testExportByIds() {
        String getDataUrl = "?search=accurate&dataId=&group=&appName=&config_tags=&pageNo=1&pageSize=10&tenant=&namespaceId=";
        String queryResult = httpGetString(serverAddr + CONFIG_CONTROLLER_PATH + getDataUrl, null);
        JsonNode resultObj = JacksonUtils.toObj(queryResult);
        JsonNode resultConfigs = resultObj.get("pageItems");
        JsonNode config1 = resultConfigs.get(0);
        JsonNode config2 = resultConfigs.get(1);
        String id1 = config1.get("id").asText();
        String id2 = config2.get("id").asText();
        String exportByIdsUrl = "?export=true&tenant=&group=&appName=&ids=" + id1 + "," + id2;
        System.out.println(exportByIdsUrl);
        byte[] zipData = httpGetBytes(serverAddr + CONFIG_CONTROLLER_PATH + exportByIdsUrl, null);
        ZipUtils.UnZipResult unZiped = ZipUtils.unzip(zipData);
        List<ZipUtils.ZipItem> zipItemList = unZiped.getZipItemList();
        assertEquals(2, zipItemList.size());
        String config1Name = config1.get("group").textValue() + "/" + config1.get("dataId").textValue();
        String config2Name = config2.get("group").textValue() + "/" + config2.get("dataId").textValue();
        for (ZipUtils.ZipItem zipItem : zipItemList) {
            if (!(config1Name.equals(zipItem.getItemName()) || config2Name.equals(zipItem.getItemName()))) {
                fail();
            }
        }
    }
    
    @Test
    @Timeout(value = 3 * TIME_OUT, unit = TimeUnit.MILLISECONDS)
    void testExportByGroup() {
        String getDataUrl = "?search=accurate&dataId=&group=EXPORT_IMPORT_TEST_GROUP&appName=&config_tags=&pageNo=1&pageSize=10&tenant=&namespaceId=";
        String queryResult = httpGetString(serverAddr + CONFIG_CONTROLLER_PATH + getDataUrl, null);
        JsonNode resultObj = JacksonUtils.toObj(queryResult);
        JsonNode resultConfigs = resultObj.get("pageItems");
        assertEquals(2, resultConfigs.size());
        JsonNode config1 = resultConfigs.get(0);
        JsonNode config2 = resultConfigs.get(1);
        String exportByIdsUrl = "?export=true&tenant=&group=EXPORT_IMPORT_TEST_GROUP&appName=&ids=";
        byte[] zipData = httpGetBytes(serverAddr + CONFIG_CONTROLLER_PATH + exportByIdsUrl, null);
        ZipUtils.UnZipResult unZiped = ZipUtils.unzip(zipData);
        List<ZipUtils.ZipItem> zipItemList = unZiped.getZipItemList();
        assertEquals(2, zipItemList.size());
        String config1Name = config1.get("group").textValue() + "/" + config1.get("dataId").textValue();
        String config2Name = config2.get("group").textValue() + "/" + config2.get("dataId").textValue();
        
        for (ZipUtils.ZipItem zipItem : zipItemList) {
            if (!(config1Name.equals(zipItem.getItemName()) || config2Name.equals(zipItem.getItemName()))) {
                fail();
            }
        }
        // verification metadata
        Map<String, String> metaData = processMetaData(unZiped.getMetaDataItem());
        String metaDataName = packageMetaName("EXPORT_IMPORT_TEST_GROUP", "testHasAppname1.properties");
        String appName = metaData.get(metaDataName);
        assertNotNull(appName);
        assertEquals("testApp1", appName);
    }
    
    @Test
    @Timeout(value = 3 * TIME_OUT, unit = TimeUnit.MILLISECONDS)
    void testExportByGroupAndApp() {
        String getDataUrl = "?search=accurate&dataId=&group=EXPORT_IMPORT_TEST_GROUP&appName=testApp1&config_tags="
                + "&pageNo=1&pageSize=10&tenant=&namespaceId=";
        String queryResult = httpGetString(serverAddr + CONFIG_CONTROLLER_PATH + getDataUrl, null);
        JsonNode resultObj = JacksonUtils.toObj(queryResult);
        JsonNode resultConfigs = resultObj.get("pageItems");
        assertEquals(1, resultConfigs.size());
        JsonNode config1 = resultConfigs.get(0);
        String exportByIdsUrl = "?export=true&tenant=&group=EXPORT_IMPORT_TEST_GROUP&appName=testApp1&ids=";
        byte[] zipData = httpGetBytes(serverAddr + CONFIG_CONTROLLER_PATH + exportByIdsUrl, null);
        ZipUtils.UnZipResult unZiped = ZipUtils.unzip(zipData);
        List<ZipUtils.ZipItem> zipItemList = unZiped.getZipItemList();
        assertEquals(1, zipItemList.size());
        String config1Name = config1.get("group").textValue() + "/" + config1.get("dataId").textValue();
        for (ZipUtils.ZipItem zipItem : zipItemList) {
            if (!config1Name.equals(zipItem.getItemName())) {
                fail();
            }
        }
        // verification metadata
        Map<String, String> metaData = processMetaData(unZiped.getMetaDataItem());
        String metaDataName = packageMetaName("EXPORT_IMPORT_TEST_GROUP", "testHasAppname1.properties");
        String appName = metaData.get(metaDataName);
        assertNotNull(appName);
        assertEquals("testApp1", appName);
    }
    
    @Test
    @Timeout(value = 3 * TIME_OUT, unit = TimeUnit.MILLISECONDS)
    void testExportAll() {
        String exportByIdsUrl = "?export=true&tenant=&group=&appName=&ids=";
        byte[] zipData = httpGetBytes(serverAddr + CONFIG_CONTROLLER_PATH + exportByIdsUrl, null);
        ZipUtils.UnZipResult unZiped = ZipUtils.unzip(zipData);
        List<ZipUtils.ZipItem> zipItemList = unZiped.getZipItemList();
        String config1Name = "EXPORT_IMPORT_TEST_GROUP/testNoAppname1.yml";
        String config2Name = "TEST1_GROUP/testNoAppname2.txt";
        String config3Name = "EXPORT_IMPORT_TEST_GROUP/testHasAppname1.properties";
        int successCount = 0;
        for (ZipUtils.ZipItem zipItem : zipItemList) {
            if (config1Name.equals(zipItem.getItemName()) || config2Name.equals(zipItem.getItemName())
                    || config3Name.equals(zipItem.getItemName())) {
                successCount++;
            }
        }
        assertEquals(3, successCount);
        // verification metadata
        Map<String, String> metaData = processMetaData(unZiped.getMetaDataItem());
        String metaDataName = packageMetaName("EXPORT_IMPORT_TEST_GROUP", "testHasAppname1.properties");
        String appName = metaData.get(metaDataName);
        assertNotNull(appName);
        assertEquals("testApp1", appName);
    }
    
    @Test
    @Timeout(value = 3 * TIME_OUT, unit = TimeUnit.MILLISECONDS)
    void testImport() {
        List<ZipUtils.ZipItem> zipItemList = new ArrayList<>(3);
        zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/test1.yml", "test: test1"));
        zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/test2.txt", "test: test1"));
        zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/test3.properties", "test.test1.value=test"));
        zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT_2/test4.properties", "test.test4.value=test"));
        zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/SUB_GROUP/test5.properties", "test.test5.value=test"));
        String metaDataStr = "TEST_IMPORT.test1~yml.app=testApp1\rTEST_IMPORT.test2~txt.app=testApp2\r\n"
                + "TEST_IMPORT.test3~properties.app=testApp3\nTEST_IMPORT_2.test4~properties.app=testApp4";
        zipItemList.add(new ZipUtils.ZipItem(".meta.yml", metaDataStr));
        final String importUrl = "?import=true&namespace=";
        Map<String, String> importPrarm = new HashMap<>(1);
        importPrarm.put("policy", "OVERWRITE");
        String importResult = uploadZipFile(serverAddr + CONFIG_CONTROLLER_PATH + importUrl, importPrarm,
                "testImport.zip", ZipUtils.zip(zipItemList));
        System.out.println("importResult: " + importResult);
        
        // test unrecognizedData
        JsonNode importResObj = JacksonUtils.toObj(importResult);
        int unrecognizedCount = importResObj.get("data").get("unrecognizedCount").intValue();
        assertEquals(1, unrecognizedCount);
        JsonNode unrecognizedData = importResObj.get("data").get("unrecognizedData").get(0);
        assertEquals("TEST_IMPORT/SUB_GROUP/test5.properties", unrecognizedData.get("itemName").textValue());
        
        String getDataUrl = "?search=accurate&dataId=&group=TEST_IMPORT&appName=&config_tags=&pageNo=1&pageSize=10&tenant=&namespaceId=";
        String queryResult = httpGetString(serverAddr + CONFIG_CONTROLLER_PATH + getDataUrl, null);
        JsonNode resultObj = JacksonUtils.toObj(queryResult);
        JsonNode resultConfigs = resultObj.get("pageItems");
        assertEquals(3, resultConfigs.size());
        for (int i = 0; i < resultConfigs.size(); i++) {
            JsonNode config = resultConfigs.get(i);
            if (!"TEST_IMPORT".equals(config.get("group").textValue())) {
                fail();
            }
            switch (config.get("dataId").textValue()) {
                case "test1.yml":
                    assertEquals("testApp1", config.get("appName").textValue());
                    break;
                case "test2.txt":
                    assertEquals("testApp2", config.get("appName").textValue());
                    break;
                case "test3.properties":
                    assertEquals("testApp3", config.get("appName").textValue());
                    break;
                default:
                    fail();
            }
        }
        
        getDataUrl = "?search=accurate&dataId=&group=TEST_IMPORT_2&appName=&config_tags=&pageNo=1&pageSize=10&tenant=&namespaceId=";
        queryResult = httpGetString(serverAddr + CONFIG_CONTROLLER_PATH + getDataUrl, null);
        resultObj = JacksonUtils.toObj(queryResult);
        resultConfigs = resultObj.get("pageItems");
        assertEquals(1, resultConfigs.size());
        JsonNode jsonNode = resultConfigs.get(0);
        assertEquals("testApp4", jsonNode.get("appName").textValue());
    }
    
    private Map<String, String> processMetaData(ZipUtils.ZipItem metaDataZipItem) {
        Map<String, String> metaDataMap = new HashMap<>(16);
        if (metaDataZipItem != null) {
            String metaDataStr = metaDataZipItem.getItemData();
            String[] metaDataArr = metaDataStr.split("\r\n");
            for (String metaDataItem : metaDataArr) {
                String[] metaDataItemArr = metaDataItem.split("=");
                assertEquals(2, metaDataItemArr.length);
                metaDataMap.put(metaDataItemArr[0], metaDataItemArr[1]);
            }
        }
        return metaDataMap;
    }
    
    private String packageMetaName(String group, String dataId) {
        String tempDataId = dataId;
        if (tempDataId.contains(".")) {
            tempDataId = tempDataId.substring(0, tempDataId.lastIndexOf(".")) + "~" + tempDataId.substring(
                    tempDataId.lastIndexOf(".") + 1);
        }
        return group + "." + tempDataId + ".app";
    }
    
    @Test
    void testExportV2() {
        String dataId = "testNoAppname2.txt";
        String getDataUrl =
                "?search=accurate&group=TEST1_GROUP&pageNo=1&pageSize=10&tenant=&namespaceId=&dataId=" + dataId;
        String queryResult = httpGetString(serverAddr + CONFIG_CONTROLLER_PATH + getDataUrl, null);
        JsonNode resultObj = JacksonUtils.toObj(queryResult);
        JsonNode resultConfigs = resultObj.get("pageItems");
        JsonNode config1 = resultConfigs.get(0);
        String configId = config1.get("id").asText();
        String exportByIdsUrl = "?exportV2=true&tenant=&group=&appName=&ids=" + configId;
        byte[] zipData = httpGetBytes(serverAddr + CONFIG_CONTROLLER_PATH + exportByIdsUrl, null);
        ZipUtils.UnZipResult unZiped = ZipUtils.unzip(zipData);
        List<ZipUtils.ZipItem> zipItemList = unZiped.getZipItemList();
        assertEquals(1, zipItemList.size());
        String config1Name = config1.get("group").textValue() + "/" + config1.get("dataId").textValue();
        
        for (ZipUtils.ZipItem zipItem : zipItemList) {
            if (!(config1Name.equals(zipItem.getItemName()))) {
                fail();
            }
        }
        assertEquals(dataId, config1.get("dataId").asText());
        String group = config1.get("group").asText();
        
        String queryConfigDetailResult = httpGetString(
                serverAddr + CONFIG_CONTROLLER_PATH + "?show=all&dataId=" + dataId + "&group=" + group, null);
        JsonNode configDetailResult = JacksonUtils.toObj(queryConfigDetailResult);
        assertNotNull(configDetailResult);
        // verification metadata
        ZipUtils.ZipItem metaDataItem = unZiped.getMetaDataItem();
        assertNotNull(metaDataItem);
        String metaDataItemItemData = metaDataItem.getItemData();
        ConfigMetadata configMetadata = YamlParserUtil.loadObject(metaDataItemItemData, ConfigMetadata.class);
        assertNotNull(configMetadata);
        assertEquals(1, configMetadata.getMetadata().size());
        
        ConfigMetadata.ConfigExportItem config1Metadata = new ConfigMetadata.ConfigExportItem();
        config1Metadata.setDataId(dataId);
        config1Metadata.setGroup(group);
        config1Metadata.setType(configDetailResult.get("type").asText());
        config1Metadata.setAppName(
                configDetailResult.get("appName") == null ? null : configDetailResult.get("appName").asText());
        config1Metadata.setDesc(
                configDetailResult.get("desc") == null ? null : configDetailResult.get("desc").asText());
        
        ConfigMetadata.ConfigExportItem configExportItem1 = configMetadata.getMetadata().get(0);
        assertEquals(configExportItem1, config1Metadata);
    }
    
    @Test
    void testImportV2() {
        List<ZipUtils.ZipItem> zipItemList = new ArrayList<>(3);
        zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT2/test1", "test: test1"));
        String metaDataStr = "metadata:\n" + "- appName: testAppName\n" + "  dataId: test1\n" + "  desc: testDesc\n"
                + "  group: TEST_IMPORT2\n" + "  type: yaml";
        
        zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, metaDataStr));
        final String importUrl = "?import=true&namespace=";
        Map<String, String> importPrarm = new HashMap<>(1);
        importPrarm.put("policy", "OVERWRITE");
        String importResult = uploadZipFile(serverAddr + CONFIG_CONTROLLER_PATH + importUrl, importPrarm,
                "testImport.zip", ZipUtils.zip(zipItemList));
        
        JsonNode importResObj = JacksonUtils.toObj(importResult);
        assertEquals(1, importResObj.get("data").get("succCount").asInt());
        
        String queryConfigDetailResult = httpGetString(
                serverAddr + CONFIG_CONTROLLER_PATH + "?show=all&dataId=test1&group=TEST_IMPORT2", null);
        JsonNode configDetailResult = JacksonUtils.toObj(queryConfigDetailResult);
        assertNotNull(configDetailResult);
        
        assertEquals("test1", configDetailResult.get("dataId").asText());
        assertEquals("TEST_IMPORT2", configDetailResult.get("group").asText());
        assertEquals("yaml", configDetailResult.get("type").asText());
        assertEquals("testAppName", configDetailResult.get("appName").asText());
        assertEquals("testDesc", configDetailResult.get("desc").asText());
    }
    
    @Test
    void testImportV2MetadataError() {
        List<ZipUtils.ZipItem> zipItemList = new ArrayList<>(3);
        zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT2/test2", "test: test2"));
        String metaDataStr =
                "metadata:\n" + "- appName: testAppName\n" + "  desc: test desc\n" + "  group: TEST_IMPORT\n"
                        + "  type: yaml";
        
        zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, metaDataStr));
        final String importUrl = "?import=true&namespace=";
        Map<String, String> importPrarm = new HashMap<>(1);
        importPrarm.put("policy", "OVERWRITE");
        String importResult = uploadZipFile(serverAddr + CONFIG_CONTROLLER_PATH + importUrl, importPrarm,
                "testImport.zip", ZipUtils.zip(zipItemList));
        
        JsonNode importResObj = JacksonUtils.toObj(importResult);
        assertEquals(importResObj.get("code").intValue(), ResultCodeEnum.METADATA_ILLEGAL.getCode());
        assertEquals(importResObj.get("message").textValue(), ResultCodeEnum.METADATA_ILLEGAL.getCodeMsg());
    }
    
    @Test
    void testImportV2MetadataNotFind() {
        List<ZipUtils.ZipItem> zipItemList = new ArrayList<>(3);
        zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT2/test3.yml", "test: test3"));
        String metaDataStr = "metadata:\n" + "- dataId: notExist\n" + "  group: TEST_IMPORT2\n" + "  type: yaml\n"
                + "- dataId: test3.yml\n" + "  group: TEST_IMPORT2\n" + "  type: yaml";
        
        zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, metaDataStr));
        final String importUrl = "?import=true&namespace=";
        Map<String, String> importPrarm = new HashMap<>(1);
        importPrarm.put("policy", "OVERWRITE");
        String importResult = uploadZipFile(serverAddr + CONFIG_CONTROLLER_PATH + importUrl, importPrarm,
                "testImport.zip", ZipUtils.zip(zipItemList));
        
        JsonNode importResObj = JacksonUtils.toObj(importResult);
        JsonNode data = importResObj.get("data");
        assertEquals(1, data.get("succCount").intValue());
        // test unrecognizedData
        int unrecognizedCount = data.get("unrecognizedCount").intValue();
        assertEquals(1, unrecognizedCount);
        JsonNode unrecognizedData = data.get("unrecognizedData").get(0);
        assertEquals("未在文件中找到: TEST_IMPORT2/notExist", unrecognizedData.get("itemName").textValue());
        
    }
    
    @Test
    void testImportV2ConfigIgnore() {
        List<ZipUtils.ZipItem> zipItemList = new ArrayList<>(3);
        zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT2/test4", "test: test4"));
        zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT2/ignore.yml", "test: test4"));
        String metaDataStr = "metadata:\n" + "- appName: testAppName\n" + "  dataId: test4\n" + "  desc: testDesc\n"
                + "  group: TEST_IMPORT2\n" + "  type: yaml";
        
        zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, metaDataStr));
        final String importUrl = "?import=true&namespace=";
        Map<String, String> importPrarm = new HashMap<>(1);
        importPrarm.put("policy", "OVERWRITE");
        String importResult = uploadZipFile(serverAddr + CONFIG_CONTROLLER_PATH + importUrl, importPrarm,
                "testImport.zip", ZipUtils.zip(zipItemList));
        
        JsonNode importResObj = JacksonUtils.toObj(importResult);
        JsonNode data = importResObj.get("data");
        assertEquals(1, data.get("succCount").intValue());
        // test unrecognizedData
        int unrecognizedCount = data.get("unrecognizedCount").intValue();
        assertEquals(1, unrecognizedCount);
        JsonNode unrecognizedData = data.get("unrecognizedData").get(0);
        assertEquals("未在元数据中找到: TEST_IMPORT2/ignore.yml", unrecognizedData.get("itemName").textValue());
    }
    
    private String httpGetString(String url, Map<String, String> param) {
        Query query = Query.newInstance().initParams(param);
        try {
            HttpRestResult<String> httpResult = nacosRestTemplate.get(url, Header.EMPTY, query, String.class);
            return httpResult.getData();
        } catch (Exception e) {
            throw new RuntimeException("", e);
        }
    }
    
    private byte[] httpGetBytes(String url, Map<String, String> param) {
        Query query = Query.newInstance().initParams(param);
        try {
            HttpRestResult<byte[]> httpResult = nacosRestTemplate.get(url, Header.EMPTY, query, byte[].class);
            return httpResult.getData();
        } catch (Exception e) {
            throw new RuntimeException("", e);
        }
    }
    
    private String httpPost(String url, Map<String, String> param) {
        return httpPost(url, param, null);
    }
    
    private String httpPost(String url, Map<String, String> param, Object payload) {
        Query query = Query.newInstance().initParams(param);
        try {
            HttpRestResult<String> httpResult = nacosRestTemplate.post(url, Header.EMPTY, query, payload, String.class);
            return httpResult.getData();
        } catch (Exception e) {
            throw new RuntimeException("", e);
        }
    }
    
    private String uploadZipFile(String url, Map<String, String> param, String filename, byte[] fileBytes) {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        try {
            int connectTimeout = 10000;
            int socketTimeout = 10000;
            HttpPost httpPost = new HttpPost(url);
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(org.apache.hc.core5.util.Timeout.ofMilliseconds(connectTimeout))
                    .setConnectionRequestTimeout(org.apache.hc.core5.util.Timeout.ofMicroseconds(socketTimeout))
                    .build();
            httpPost.setConfig(requestConfig);
            
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            if (MapUtils.isNotEmpty(param)) {
                param.entrySet().forEach(e -> {
                    builder.addTextBody(e.getKey(), e.getValue(), ContentType.APPLICATION_FORM_URLENCODED);
                });
            }
            
            ByteArrayBody byteArrayBody = new ByteArrayBody(fileBytes, ContentType.create("application/zip"), filename);
            builder.addPart("file", byteArrayBody);
            
            HttpEntity reqEntity = builder.setContentType(ContentType.MULTIPART_FORM_DATA).build();
            httpPost.setEntity(reqEntity);
            
            CloseableHttpResponse response = httpclient.execute(httpPost);
            try {
                HttpEntity resEntity = response.getEntity();
                String responseToStr = null;
                if (resEntity != null) {
                    responseToStr = EntityUtils.toString(response.getEntity());
                }
                EntityUtils.consume(resEntity);
                return responseToStr;
            } finally {
                response.close();
            }
        } catch (Throwable e) {
            throw new RuntimeException("httpPostZipFile error", e);
        } finally {
            try {
                httpclient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
