@GetMapping("/by-namespace-and-releases-not-in")
  public List<InstanceDTO> getByReleasesNotIn(@RequestParam("appId") String appId,
                                              @RequestParam("clusterName") String clusterName,
                                              @RequestParam("namespaceName") String namespaceName,
                                              @RequestParam("releaseIds") String releaseIds) {
    Set<Long> releaseIdSet = RELEASES_SPLITTER.splitToList(releaseIds).stream().map(Long::parseLong)
        .collect(Collectors.toSet());

    List<Release> releases = releaseService.findByReleaseIds(releaseIdSet);

    if (CollectionUtils.isEmpty(releases)) {
      throw NotFoundException.releaseNotFound(releaseIds);
    }

    Set<String> releaseKeys = releases.stream().map(Release::getReleaseKey).collect(Collectors
        .toSet());

    List<InstanceConfig> instanceConfigs = instanceService
        .findInstanceConfigsByNamespaceWithReleaseKeysNotIn(appId, clusterName, namespaceName,
            releaseKeys);

    Multimap<Long, InstanceConfig> instanceConfigMap = HashMultimap.create();
    Set<String> otherReleaseKeys = Sets.newHashSet();

    for (InstanceConfig instanceConfig : instanceConfigs) {
      instanceConfigMap.put(instanceConfig.getInstanceId(), instanceConfig);
      otherReleaseKeys.add(instanceConfig.getReleaseKey());
    }

    List<Instance> instances = instanceService.findInstancesByIds(instanceConfigMap.keySet());

    if (CollectionUtils.isEmpty(instances)) {
      return Collections.emptyList();
    }

    List<InstanceDTO> instanceDTOs = BeanUtils.batchTransform(InstanceDTO.class, instances);

    List<Release> otherReleases = releaseService.findByReleaseKeys(otherReleaseKeys);
    Map<String, ReleaseDTO> releaseMap = Maps.newHashMap();

    for (Release release : otherReleases) {
      //unset configurations to save space
      release.setConfigurations(null);
      ReleaseDTO releaseDTO = BeanUtils.transform(ReleaseDTO.class, release);
      releaseMap.put(release.getReleaseKey(), releaseDTO);
    }

    for (InstanceDTO instanceDTO : instanceDTOs) {
      Collection<InstanceConfig> configs = instanceConfigMap.get(instanceDTO.getId());
      List<InstanceConfigDTO> configDTOs = configs.stream().map(instanceConfig -> {
        InstanceConfigDTO instanceConfigDTO = new InstanceConfigDTO();
        instanceConfigDTO.setRelease(releaseMap.get(instanceConfig.getReleaseKey()));
        instanceConfigDTO.setReleaseDeliveryTime(instanceConfig.getReleaseDeliveryTime());
        instanceConfigDTO.setDataChangeLastModifiedTime(instanceConfig
            .getDataChangeLastModifiedTime());
        return instanceConfigDTO;
      }).collect(Collectors.toList());
      instanceDTO.setConfigs(configDTOs);
    }

    return instanceDTOs;
  }
@Test
  public void testGetByReleasesNotIn() throws Exception {
    String someConfigAppId = "someConfigAppId";
    String someConfigClusterName = "someConfigClusterName";
    String someConfigNamespaceName = "someConfigNamespaceName";
    long someReleaseId = 1;
    long anotherReleaseId = 2;
    String releaseIds = Joiner.on(",").join(someReleaseId, anotherReleaseId);

    Date someReleaseDeliveryTime = new Date();
    Date anotherReleaseDeliveryTime = new Date();

    Release someRelease = mock(Release.class);
    Release anotherRelease = mock(Release.class);
    String someReleaseKey = "someReleaseKey";
    String anotherReleaseKey = "anotherReleaseKey";
    when(someRelease.getReleaseKey()).thenReturn(someReleaseKey);
    when(anotherRelease.getReleaseKey()).thenReturn(anotherReleaseKey);

    when(releaseService.findByReleaseIds(Sets.newHashSet(someReleaseId, anotherReleaseId)))
        .thenReturn(Lists.newArrayList(someRelease, anotherRelease));

    long someInstanceId = 1;
    long anotherInstanceId = 2;
    String someInstanceConfigReleaseKey = "someInstanceConfigReleaseKey";
    String anotherInstanceConfigReleaseKey = "anotherInstanceConfigReleaseKey";
    InstanceConfig someInstanceConfig = mock(InstanceConfig.class);
    InstanceConfig anotherInstanceConfig = mock(InstanceConfig.class);
    when(someInstanceConfig.getInstanceId()).thenReturn(someInstanceId);
    when(anotherInstanceConfig.getInstanceId()).thenReturn(anotherInstanceId);
    when(someInstanceConfig.getReleaseKey()).thenReturn(someInstanceConfigReleaseKey);
    when(anotherInstanceConfig.getReleaseKey()).thenReturn(anotherInstanceConfigReleaseKey);
    when(someInstanceConfig.getReleaseDeliveryTime()).thenReturn(someReleaseDeliveryTime);
    when(anotherInstanceConfig.getReleaseDeliveryTime()).thenReturn(anotherReleaseDeliveryTime);
    when(instanceService.findInstanceConfigsByNamespaceWithReleaseKeysNotIn(someConfigAppId,
        someConfigClusterName, someConfigNamespaceName, Sets.newHashSet(someReleaseKey,
            anotherReleaseKey))).thenReturn(Lists.newArrayList(someInstanceConfig,
        anotherInstanceConfig));

    String someInstanceAppId = "someInstanceAppId";
    String someInstanceClusterName = "someInstanceClusterName";
    String someInstanceNamespaceName = "someInstanceNamespaceName";
    String someIp = "someIp";
    String anotherIp = "anotherIp";
    Instance someInstance = assembleInstance(someInstanceId, someInstanceAppId,
        someInstanceClusterName,
        someInstanceNamespaceName, someIp);
    Instance anotherInstance = assembleInstance(anotherInstanceId, someInstanceAppId,
        someInstanceClusterName,
        someInstanceNamespaceName, anotherIp);
    when(instanceService.findInstancesByIds(Sets.newHashSet(someInstanceId, anotherInstanceId)))
        .thenReturn(Lists.newArrayList(someInstance, anotherInstance));

    Release someInstanceConfigRelease = new Release();
    someInstanceConfigRelease.setReleaseKey(someInstanceConfigReleaseKey);
    Release anotherInstanceConfigRelease = new Release();
    anotherInstanceConfigRelease.setReleaseKey(anotherInstanceConfigReleaseKey);
    when(releaseService.findByReleaseKeys(Sets.newHashSet(someInstanceConfigReleaseKey,
        anotherInstanceConfigReleaseKey))).thenReturn(Lists.newArrayList(someInstanceConfigRelease,
        anotherInstanceConfigRelease));

    List<InstanceDTO> result = instanceConfigController.getByReleasesNotIn(someConfigAppId,
        someConfigClusterName, someConfigNamespaceName, releaseIds);

    assertEquals(2, result.size());
    InstanceDTO someInstanceDto = null;
    InstanceDTO anotherInstanceDto = null;

    for (InstanceDTO instanceDTO : result) {
      if (instanceDTO.getId() == someInstanceId) {
        someInstanceDto = instanceDTO;
      } else if (instanceDTO.getId() == anotherInstanceId) {
        anotherInstanceDto = instanceDTO;
      }
    }

    verifyInstance(someInstance, someInstanceDto);
    verifyInstance(anotherInstance, anotherInstanceDto);

    assertEquals(someInstanceConfigReleaseKey, someInstanceDto.getConfigs().get(0).getRelease()
        .getReleaseKey());
    assertEquals(anotherInstanceConfigReleaseKey, anotherInstanceDto.getConfigs().get(0)
        .getRelease()
        .getReleaseKey());

    assertEquals(someReleaseDeliveryTime, someInstanceDto.getConfigs().get(0).getReleaseDeliveryTime());
    assertEquals(anotherReleaseDeliveryTime, anotherInstanceDto.getConfigs().get(0)
        .getReleaseDeliveryTime());
  }