@GetMapping("/list.json")
    @AuthAction(AuthService.PrivilegeType.READ_RULE)
    public Result<List<ApiDefinitionEntity>> queryApis(String app, String ip, Integer port) {

        if (StringUtil.isEmpty(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        if (StringUtil.isEmpty(ip)) {
            return Result.ofFail(-1, "ip can't be null or empty");
        }
        if (port == null) {
            return Result.ofFail(-1, "port can't be null");
        }

        try {
            List<ApiDefinitionEntity> apis = sentinelApiClient.fetchApis(app, ip, port).get();
            repository.saveAll(apis);
            return Result.ofSuccess(apis);
        } catch (Throwable throwable) {
            logger.error("queryApis error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }
    }
@Test
    public void testQueryApis() throws Exception {
        String path = "/gateway/api/list.json";

        List<ApiDefinitionEntity> entities = new ArrayList<>();

        // Mock two entities
        ApiDefinitionEntity entity = new ApiDefinitionEntity();
        entity.setId(1L);
        entity.setApp(TEST_APP);
        entity.setIp(TEST_IP);
        entity.setPort(TEST_PORT);
        entity.setApiName("foo");
        Date date = new Date();
        entity.setGmtCreate(date);
        entity.setGmtModified(date);

        Set<ApiPredicateItemEntity> itemEntities = new LinkedHashSet<>();
        entity.setPredicateItems(itemEntities);
        ApiPredicateItemEntity itemEntity = new ApiPredicateItemEntity();
        itemEntity.setPattern("/aaa");
        itemEntity.setMatchStrategy(URL_MATCH_STRATEGY_EXACT);

        itemEntities.add(itemEntity);
        entities.add(entity);

        ApiDefinitionEntity entity2 = new ApiDefinitionEntity();
        entity2.setId(2L);
        entity2.setApp(TEST_APP);
        entity2.setIp(TEST_IP);
        entity2.setPort(TEST_PORT);
        entity2.setApiName("biz");
        entity.setGmtCreate(date);
        entity.setGmtModified(date);

        Set<ApiPredicateItemEntity> itemEntities2 = new LinkedHashSet<>();
        entity2.setPredicateItems(itemEntities2);
        ApiPredicateItemEntity itemEntity2 = new ApiPredicateItemEntity();
        itemEntity2.setPattern("/bbb");
        itemEntity2.setMatchStrategy(URL_MATCH_STRATEGY_PREFIX);

        itemEntities2.add(itemEntity2);
        entities.add(entity2);

        CompletableFuture<List<ApiDefinitionEntity>> completableFuture = mock(CompletableFuture.class);
        given(completableFuture.get()).willReturn(entities);
        given(sentinelApiClient.fetchApis(TEST_APP, TEST_IP, TEST_PORT)).willReturn(completableFuture);

        MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(path);
        requestBuilder.param("app", TEST_APP);
        requestBuilder.param("ip", TEST_IP);
        requestBuilder.param("port", String.valueOf(TEST_PORT));

        // Do controller logic
        MvcResult mvcResult = mockMvc.perform(requestBuilder)
                .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();

        // Verify the fetchApis method has been called
        verify(sentinelApiClient).fetchApis(TEST_APP, TEST_IP, TEST_PORT);

        // Verify if two same entities are got
        Result<List<ApiDefinitionEntity>> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference<Result<List<ApiDefinitionEntity>>>(){});
        assertTrue(result.isSuccess());

        List<ApiDefinitionEntity> data = result.getData();
        assertEquals(2, data.size());
        assertEquals(entities, data);

        // Verify the entities are add into memory repository
        List<ApiDefinitionEntity> entitiesInMem = repository.findAllByApp(TEST_APP);
        assertEquals(2, entitiesInMem.size());
        assertEquals(entities, entitiesInMem);
    }