public static final Integer MAX_SERIES_IN_STUDY = VALUE_PARAM_MAX_LIMIT_FOR_SERIES * 3; public static final Integer MAX_STUDIES_IN_DICOM_STORE = VALUE_PARAM_MAX_LIMIT_FOR_STUDY * 3; public class TestUtils {

  /**
   * Generates mocked HttpClientFactory.
   *
   * @param closeableHttpClient mocked CloseableHttpClient
   * @return generated HttpClientFactory
   */
  public static HttpClientFactory prepareHttpClientFactory(
      CloseableHttpClient closeableHttpClient) {
    HttpClientFactory httpClientFactory = Mockito.mock(HttpClientFactory.class);
    Mockito.when(httpClientFactory.createHttpClient()).thenReturn(closeableHttpClient);
    return httpClientFactory;
  }

  /**
   * Prepares content for closeableHttpClient.
   *
   * @param closeableHttpClient mocked CloseableHttpClient
   * @param entityCount how many entities would be generated
   * @param entityType type of entity
   * @param statusCode http status code
   */
  public static void prepareHttpClient(CloseableHttpClient closeableHttpClient, int entityCount,
      EntityType entityType, int statusCode) throws IOException, DicomFuseException {
    prepareHttpClient(closeableHttpClient, entityCount, 0, entityType, statusCode, null, null);
  }

  /**
   * Prepares content for closeableHttpClient.
   *
   * @param closeableHttpClient mocked CloseableHttpClient
   * @param entityCount how many entities would be generated
   * @param offset start offset
   * @param entityType type of entity
   * @param statusCode http status code
   * @param path http path for request
   * @param query http query for request
   */
  public static void prepareHttpClient(CloseableHttpClient closeableHttpClient, int entityCount,
      int offset, EntityType entityType, int statusCode, String path, String query)
      throws IOException, DicomFuseException {
    List<BasicHttpEntity> basicHttpEntities = prepareHttpEntities(entityCount, offset, entityType);
    CloseableHttpResponse httpResponse = prepareHttpResponse(statusCode, basicHttpEntities);
    Mockito.doReturn(httpResponse)
        .when(closeableHttpClient)
        .execute(ArgumentMatchers.argThat((HttpUriRequest request) ->
            query == null || path == null ||
                request.getURI().getPath().equals(path) && query.equals(request.getURI().getQuery())
        ));
  }

  /**
   * Generates AuthAdc with a mocked token.
   *
   * @param token mocked token
   * @return generated AuthAdc
   */
  public static AuthAdc prepareAuthAdc(String token) {
    AccessToken accessToken = new AccessToken(token, null);
    GoogleCredentials googleCredentials = GoogleCredentials.create(accessToken);
    return new AuthAdc(googleCredentials);
  }

  /**
   * Generates http entities list.
   *
   * @param entityCount how many entities would be generated
   * @param offset start offset
   * @param entityType  type of entity
   */
  private static List<BasicHttpEntity> prepareHttpEntities(int entityCount, int offset, EntityType entityType)
      throws DicomFuseException {
    List<BasicHttpEntity> basicHttpEntities = new ArrayList<>();
    int entityLimit;
    int maxEntities;
    switch (entityType) {
      case DICOM_STORE:
        entityLimit = 100;
        maxEntities = 100;
        break;
      case STUDY:
        entityLimit = VALUE_PARAM_MAX_LIMIT_FOR_STUDY;
        maxEntities = MAX_STUDIES_IN_DICOM_STORE;
        break;
      case SERIES:
        entityLimit = VALUE_PARAM_MAX_LIMIT_FOR_SERIES;
        maxEntities = MAX_SERIES_IN_STUDY;
        break;
      case INSTANCE:
        entityLimit = VALUE_PARAM_MAX_LIMIT_FOR_INSTANCES;
        maxEntities = MAX_INSTANCES_IN_SERIES;
        break;
      default:
        throw new DicomFuseException("Error");
    }
    int iterationCount = (int) Math.ceil((double) maxEntities / entityLimit) + 1;

    int startOfEntityId = offset +1;
    if (entityCount < entityLimit) {
      BasicHttpEntity httpEntity = prepareHttpEntityWithContent(entityCount, entityType, startOfEntityId);
      basicHttpEntities.add(httpEntity);
      return basicHttpEntities;
    }

    if (entityCount == entityLimit) {
      BasicHttpEntity httpEntity = prepareHttpEntityWithContent(entityCount, entityType, startOfEntityId);
      basicHttpEntities.add(httpEntity);
      for (int i = 0; i < iterationCount; i++) {
        BasicHttpEntity emptyHttpEntity = prepareEmptyHttpEntity();
        basicHttpEntities.add(emptyHttpEntity);
      }
      return basicHttpEntities;
    }

    for (int i = 1, count = entityCount;
        i <= iterationCount;
        i++, count -= entityLimit, startOfEntityId += entityLimit) {

      if (count > 0 && count >= entityLimit) {
        BasicHttpEntity httpEntityCallable = prepareHttpEntityWithContent(entityLimit, entityType,
            startOfEntityId);
        basicHttpEntities.add(httpEntityCallable);
      }

      if (count > 0 && count < entityLimit) {
        BasicHttpEntity httpEntityCallable = prepareHttpEntityWithContent(count, entityType,
            startOfEntityId);
        basicHttpEntities.add(httpEntityCallable);
      }

      if (count <= 0) {
        BasicHttpEntity emptyHttpEntity = prepareEmptyHttpEntity();
        basicHttpEntities.add(emptyHttpEntity);
      }

    }

    return basicHttpEntities;
  }

  /**
   * Generates BasicHttpEntity with unique JSON entities content.
   *
   * @param entityCount size of array
   * @param entityType  type of entity
   * @param startOfEntityId starting ID from which the array of entities starts
   * @return entity object
   */
  private static BasicHttpEntity prepareHttpEntityWithContent(int entityCount,
      EntityType entityType, int startOfEntityId) throws DicomFuseException {
    BasicHttpEntity basicHttpEntity = new BasicHttpEntity();
    StringBuilder stringBuilder = new StringBuilder();
    if (entityType == DICOM_STORE) {
      stringBuilder.append("{\"dicomStores\":[");
    } else {
      stringBuilder.append("[");
    }
    int count = startOfEntityId + entityCount;
    for (int i = startOfEntityId; i < count; i++) {
      switch (entityType) {
        case DICOM_STORE:
          stringBuilder
              .append("{\"name\":\"projects/test/locations/test/datasets/test/dicomStores/test");
          stringBuilder.append(i);
          stringBuilder.append("\"}");
          break;
        case STUDY:
          stringBuilder.append("{\"0020000D\":{\"vr\":\"UI\",\"Value\":[\"");
          stringBuilder.append(i);
          stringBuilder.append("\"]}}");
          break;
        case SERIES:
          stringBuilder.append("{\"0020000E\":{\"vr\":\"UI\",\"Value\":[\"");
          stringBuilder.append(i);
          stringBuilder.append("\"]}}");
          break;
        case INSTANCE:
          stringBuilder.append("{\"00080018\":{\"vr\":\"UI\",\"Value\":[\"");
          stringBuilder.append(i);
          stringBuilder.append("\"]}}");
          break;
        default:
          throw new DicomFuseException("Error");
      }
      if (i != count - 1) {
        stringBuilder.append(",");
      }
    }
    if (entityType == DICOM_STORE) {
      stringBuilder.append("]}");
    } else {
      stringBuilder.append("]");
    }
    basicHttpEntity.setContent(new ByteArrayInputStream(stringBuilder.toString().getBytes(UTF_8)));
    return basicHttpEntity;
  }

  /**
   * Generates BasicHttpEntity with with empty content.
   *
   * @return entity object
   */
  private static BasicHttpEntity prepareEmptyHttpEntity() {
    BasicHttpEntity emptyHttpEntity = new BasicHttpEntity();
    emptyHttpEntity.setContent(new ByteArrayInputStream("[]".getBytes(UTF_8)));
    return emptyHttpEntity;
  }

  /**
   * Generates CloseableHttpResponse.
   *
   * @param statusCode Http response code
   * @return response object
   */
  public static CloseableHttpResponse prepareHttpResponse(int statusCode) {
    return prepareHttpResponse(statusCode, Collections.emptyList());
  }

  /**
   * Generates CloseableHttpResponse with multiple entities.
   *
   * @param statusCode Http response code
   * @param httpEntities list of entities
   * @return response object
   */
  private static CloseableHttpResponse prepareHttpResponse(int statusCode,
      List<BasicHttpEntity> httpEntities) {
    ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
    BasicStatusLine basicStatusLine = new BasicStatusLine(protocolVersion, statusCode, "");
    CloseableHttpResponse closeableHttpResponse = Mockito.mock(CloseableHttpResponse.class);
    Mockito.when(closeableHttpResponse.getStatusLine()).thenReturn(basicStatusLine);

    if (httpEntities.size() == 1) {
      Mockito.when(closeableHttpResponse.getEntity()).thenReturn(httpEntities.get(0));
    }
    if (httpEntities.size() > 1) {
      BasicHttpEntity[] array = new BasicHttpEntity[httpEntities.size() - 1];
      array = httpEntities.toArray(array);
      BasicHttpEntity[] entities = Arrays.copyOfRange(array, 1, array.length);
      Mockito.when(closeableHttpResponse.getEntity()).thenReturn(httpEntities.get(0), entities);
    }
    return closeableHttpResponse;
  }
} public class AuthAdc {

  private static final List<String> SCOPES = Collections.singletonList(
      "https://www.googleapis.com/auth/cloud-healthcare"
  );
  private GoogleCredentials googleCredentials;

  public AuthAdc(GoogleCredentials googleCredentials) {
    this.googleCredentials = googleCredentials;
  }

  public AuthAdc() {
  }

  public void createCredentials(Path keyPath) throws IOException {
    InputStream inputStream = new FileInputStream(keyPath.toFile());
    googleCredentials = GoogleCredentials.fromStream(inputStream).createScoped(SCOPES);
  }

  public void createCredentials() throws IOException {
    googleCredentials = GoogleCredentials.getApplicationDefault().createScoped(SCOPES);
  }

  public GoogleCredentials getCredentials() throws IOException {
    googleCredentials.getRequestMetadata(); // Update token if it necessary.
    return googleCredentials;
  }
} public interface FuseDao {

  List<DicomStore> getAllDicomStores(QueryBuilder queryBuilder) throws DicomFuseException;
  DicomStore getSingleDicomStore(QueryBuilder queryBuilder) throws DicomFuseException;
  List<Study> getStudies(QueryBuilder queryBuilder) throws DicomFuseException;
  Study getSingleStudy(QueryBuilder queryBuilder) throws DicomFuseException;
  List<Series> getSeries(QueryBuilder queryBuilder) throws DicomFuseException;
  Series getSingleSeries(QueryBuilder queryBuilder) throws DicomFuseException;
  List<Instance> getInstances(QueryBuilder queryBuilder) throws DicomFuseException;
  Instance getSingleInstance(QueryBuilder queryBuilder) throws DicomFuseException;

  void downloadInstance(QueryBuilder queryBuilder) throws DicomFuseException;
  void uploadInstance(QueryBuilder queryBuilder) throws DicomFuseException;
  void deleteInstance(QueryBuilder queryBuilder) throws DicomFuseException;
  void createDicomStore(QueryBuilder queryBuilder) throws DicomFuseException;
  void deleteDicomStore(QueryBuilder queryBuilder) throws DicomFuseException;
} public class FuseDaoImpl implements FuseDao {

  private AuthAdc authAdc;
  private ObjectMapper objectMapper;
  private HttpClientFactory httpClientFactory;

  public FuseDaoImpl(AuthAdc authAdc, HttpClientFactory httpClientFactory) {
    this.authAdc = authAdc;
    this.httpClientFactory = httpClientFactory;
    objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  }

  @Override
  public List<DicomStore> getAllDicomStores(QueryBuilder queryBuilder)
      throws DicomFuseException {
    DicomStoresPathBuilder dicomStoresPathBuilder = new DicomStoresPathBuilder(queryBuilder);
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .setPath(dicomStoresPathBuilder.toPath());
    DicomStores dicomStoresOnPage = createRequestForObjectList(uriBuilder,
        new TypeReference<DicomStores>() {});
    List<DicomStore> dicomStoresList = new ArrayList<>();
    if (dicomStoresOnPage.getDicomStores() != null) {
      dicomStoresList.addAll(dicomStoresOnPage.getDicomStores());
    }
    String nextPageToken = dicomStoresOnPage.getNextPageToken();
    while (nextPageToken != null) {
      uriBuilder.setParameter(PARAM_PAGE_TOKEN, nextPageToken);
      dicomStoresOnPage = createRequestForObjectList(uriBuilder,
          new TypeReference<DicomStores>() {});
      dicomStoresList.addAll(dicomStoresOnPage.getDicomStores());
      nextPageToken = dicomStoresOnPage.getNextPageToken();
    }
    return dicomStoresList;
  }

  @Override
  public DicomStore getSingleDicomStore(QueryBuilder queryBuilder) throws DicomFuseException {
    SingleDicomStorePathBuilder singleDicomStorePathBuilder =
        new SingleDicomStorePathBuilder(queryBuilder);
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .setPath(singleDicomStorePathBuilder.toPath());
    return createRequestForObjectList(uriBuilder, new TypeReference<DicomStore>() {});
  }

  @Override
  public List<Study> getStudies(QueryBuilder queryBuilder) throws DicomFuseException {
    StudiesPathBuilder studiesPathBuilder = new StudiesPathBuilder(queryBuilder);
    String path = studiesPathBuilder.toPath();
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .addParameter(PARAM_LIMIT, VALUE_PARAM_MAX_LIMIT_FOR_STUDY.toString())
        .addParameter(PARAM_OFFSET, queryBuilder.getOffset().toString())
        .setPath(path);
    return createRequestForObjectsList(uriBuilder, new TypeReference<List<Study>>() {});
  }

  @Override
  public Study getSingleStudy(QueryBuilder queryBuilder) throws DicomFuseException {
    StudiesPathBuilder studiesPathBuilder = new StudiesPathBuilder(queryBuilder);
    String path = studiesPathBuilder.toPath();
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .addParameter(PARAM_STUDY_ID, queryBuilder.getStudyId())
        .setPath(path);
    List<Study> studies =
        createRequestForObjectsList(uriBuilder, new TypeReference<List<Study>>() {});
    if (studies.size() == 0) {
      throw new DicomFuseException("Study not found");
    }
    return studies.get(0);
  }

  @Override
  public List<Series> getSeries(QueryBuilder queryBuilder) throws DicomFuseException {
    SeriesPathBuilder seriesPathBuilder = new SeriesPathBuilder(queryBuilder);
    String path = seriesPathBuilder.toPath();
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .addParameter(PARAM_INCLUDE_FIELD, VALUE_PARAM_STUDY_INSTANCE_UID)
        .addParameter(PARAM_LIMIT, VALUE_PARAM_MAX_LIMIT_FOR_SERIES.toString())
        .addParameter(PARAM_OFFSET, queryBuilder.getOffset().toString())
        .setPath(path);
    return createRequestForObjectsList(uriBuilder, new TypeReference<List<Series>>() {});
  }


  @Override
  public Series getSingleSeries(QueryBuilder queryBuilder) throws DicomFuseException {
    SeriesPathBuilder studiesPathBuilder = new SeriesPathBuilder(queryBuilder);
    String path = studiesPathBuilder.toPath();
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .addParameter(PARAM_INCLUDE_FIELD, VALUE_PARAM_STUDY_INSTANCE_UID)
        .addParameter(PARAM_SERIES_ID, queryBuilder.getSeriesId())
        .setPath(path);
    List<Series> series =
        createRequestForObjectsList(uriBuilder, new TypeReference<List<Series>>() {});
    if (series.size() == 0) {
      throw new DicomFuseException("Series not found");
    }
    return series.get(0);
  }

  @Override
  public List<Instance> getInstances(QueryBuilder queryBuilder) throws DicomFuseException {
    InstancesPathBuilder instancesPathBuilder = new InstancesPathBuilder(queryBuilder);
    String path = instancesPathBuilder.toPath();
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .addParameter(PARAM_INCLUDE_FIELD, VALUE_PARAM_STUDY_INSTANCE_UID)
        .addParameter(PARAM_INCLUDE_FIELD, VALUE_PARAM_SERIES_INSTANCE_UID)
        .addParameter(PARAM_LIMIT, VALUE_PARAM_MAX_LIMIT_FOR_INSTANCES.toString())
        .addParameter(PARAM_OFFSET, queryBuilder.getOffset().toString())
        .setPath(path);
    return createRequestForObjectsList(uriBuilder, new TypeReference<List<Instance>>() {});
  }

  @Override
  public Instance getSingleInstance(QueryBuilder queryBuilder) throws DicomFuseException {
    InstancesPathBuilder instancesPathBuilder = new InstancesPathBuilder(queryBuilder);
    String path = instancesPathBuilder.toPath();
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .addParameter(PARAM_INCLUDE_FIELD, VALUE_PARAM_STUDY_INSTANCE_UID)
        .addParameter(PARAM_INCLUDE_FIELD, VALUE_PARAM_SERIES_INSTANCE_UID)
        .addParameter(PARAM_INSTANCE_ID, queryBuilder.getInstanceId())
        .setPath(path);
    List<Instance> instances =
        createRequestForObjectsList(uriBuilder, new TypeReference<List<Instance>>() {});
    if (instances.size() == 0) {
      throw new DicomFuseException("Instance not found");
    }
    return instances.get(0);
  }

  @Override
  public void downloadInstance(QueryBuilder queryBuilder) throws DicomFuseException {
    InstancePathBuilder instancePathBuilder = new InstancePathBuilder(queryBuilder);
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .setPath(instancePathBuilder.toPath());
    createRequestToDownloadInstance(uriBuilder, queryBuilder.getInstanceDataPath());
  }

  @Override
  public void uploadInstance(QueryBuilder queryBuilder) throws DicomFuseException {
    StudiesPathBuilder studiesPathBuilder = new StudiesPathBuilder(queryBuilder);
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .setPath(studiesPathBuilder.toPath());
    createRequestToUploadInstance(uriBuilder, queryBuilder.getInstanceDataPath(),
        queryBuilder.getDicomPath());
  }

  @Override
  public void deleteInstance(QueryBuilder queryBuilder) throws DicomFuseException {
    InstancePathBuilder instancePathBuilder = new InstancePathBuilder(queryBuilder);
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .setPath(instancePathBuilder.toPath());
    createRequestToDeleteInstance(uriBuilder);
  }

  @Override
  public void createDicomStore(QueryBuilder queryBuilder) throws DicomFuseException {
    DicomStoresPathBuilder dicomStoresPathBuilder = new DicomStoresPathBuilder(queryBuilder);
    try (CloseableHttpClient httpclient = httpClientFactory.createHttpClient()) {
      URI uri = new URIBuilder()
          .setScheme(SCHEME)
          .setHost(HEALTHCARE_HOST)
          .setPath(dicomStoresPathBuilder.toPath())
          .setParameter(PARAM_DICOM_STORE_ID, queryBuilder.getDicomStoreId())
          .build();
      HttpPost request = new HttpPost(uri);
      request.addHeader(CONTENT_TYPE, APPLICATION_JSON_CHARSET_UTF8);
      GoogleCredentials credentials = authAdc.getCredentials();
      String tokenValue = credentials.getAccessToken().getTokenValue();
      request.addHeader(AUTHORIZATION, BEARER + tokenValue);
      try (CloseableHttpResponse response = httpclient.execute(request)) {
        checkStatusCode(response, uri);
      }
    } catch (IOException | URISyntaxException e) {
      throw new DicomFuseException(e);
    }
  }

  @Override
  public void deleteDicomStore(QueryBuilder queryBuilder) throws DicomFuseException {
    DicomStorePathBuilder dicomStorePathBuilder = new DicomStorePathBuilder(queryBuilder);
    try (CloseableHttpClient httpclient = httpClientFactory.createHttpClient()) {
      URI uri = new URIBuilder()
          .setScheme(SCHEME)
          .setHost(HEALTHCARE_HOST)
          .setPath(dicomStorePathBuilder.toPath())
          .build();
      HttpDelete request = new HttpDelete(uri);
      GoogleCredentials credentials = authAdc.getCredentials();
      String tokenValue = credentials.getAccessToken().getTokenValue();
      request.addHeader(AUTHORIZATION, BEARER + tokenValue);
      try (CloseableHttpResponse response = httpclient.execute(request)) {
        checkStatusCode(response, uri);
      }
    } catch (IOException | URISyntaxException e) {
      throw new DicomFuseException(e);
    }
  }

  private <T> T createRequestForObjectList(URIBuilder uriBuilder, TypeReference<T> typeReference)
      throws DicomFuseException {
    T result;
    try (CloseableHttpClient httpclient = httpClientFactory.createHttpClient()) {
      URI uri = uriBuilder.build();
      HttpGet request = new HttpGet(uri);
      request.addHeader(CONTENT_TYPE, APPLICATION_JSON_CHARSET_UTF8);
      GoogleCredentials credentials = authAdc.getCredentials();
      String tokenValue = credentials.getAccessToken().getTokenValue();
      request.addHeader(AUTHORIZATION, BEARER + tokenValue);
      try (CloseableHttpResponse response = httpclient.execute(request)) {
        checkStatusCode(response, uri);
        try (InputStream inputStream = response.getEntity().getContent()) {
          result = objectMapper.readValue(inputStream, typeReference);
        } catch (JsonParseException | JsonMappingException e) {
          throw new DicomFuseException(e);
        }
      }
    } catch (IOException | URISyntaxException e) {
      throw new DicomFuseException(e);
    }
    return result;
  }

  private <T> List<T> createRequestForObjectsList(URIBuilder uriBuilder, TypeReference<List<T>> typeReference)
      throws DicomFuseException {
    List<T> result;
    try (CloseableHttpClient httpclient = httpClientFactory.createHttpClient()) {
      URI uri = uriBuilder.build();
      HttpGet request = new HttpGet(uri);
      request.addHeader(CONTENT_TYPE, APPLICATION_JSON_CHARSET_UTF8);
      GoogleCredentials credentials = authAdc.getCredentials();
      String tokenValue = credentials.getAccessToken().getTokenValue();
      request.addHeader(AUTHORIZATION, BEARER + tokenValue);
      try (CloseableHttpResponse response = httpclient.execute(request)) {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == HttpStatusCodes.STATUS_CODE_OK) {
          try (InputStream inputStream = response.getEntity().getContent()) {
            result = objectMapper.readValue(inputStream, typeReference);
          }
        } else if (statusCode == HttpStatusCodes.STATUS_CODE_NO_CONTENT) {
          result = new ArrayList<T>();
        } else {
          throw new DicomFuseException("Failed HTTP " + response.getStatusLine() + " " + uri,
              statusCode);
        }
      }
    } catch (IOException | URISyntaxException e) {
      throw new DicomFuseException(e);
    }
    return result;
  }

  private void createRequestToDownloadInstance(URIBuilder uriBuilder, Path instanceDataPath)
      throws DicomFuseException {
    try (CloseableHttpClient httpclient = httpClientFactory.createHttpClient()) {
      URI uri = uriBuilder.build();
      HttpGet request = new HttpGet(uri);
      request.addHeader(ACCEPT, APPLICATION_DICOM_TRANSFER_SYNTAX);
      request.addHeader(CONTENT_TYPE, APPLICATION_DICOM_JSON_CHARSET_UTF8);
      GoogleCredentials credentials = authAdc.getCredentials();
      String tokenValue = credentials.getAccessToken().getTokenValue();
      request.addHeader(AUTHORIZATION, BEARER + tokenValue);
      try (CloseableHttpResponse response = httpclient.execute(request)) {
        checkStatusCode(response, uri);
        HttpEntity entity = response.getEntity();
        try (InputStream is = entity.getContent()) {
          Files.copy(is, instanceDataPath, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
          throw new DicomFuseException(e);
        }
      }
    } catch (IOException | URISyntaxException e) {
      throw new DicomFuseException(e);
    }
  }

  private void createRequestToUploadInstance(URIBuilder uriBuilder, Path instanceDataPath,
      DicomPath dicomPath) throws DicomFuseException {
    try (CloseableHttpClient httpclient = httpClientFactory.createHttpClient()) {
      URI uri = uriBuilder.build();
      HttpPost request = new HttpPost(uri);

      ContentType contentType = ContentType.create("application/dicom");
      String boundary = UUID.randomUUID().toString();

      HttpEntity httpEntity = MultipartEntityBuilder.create()
          .setBoundary(boundary)
          .addBinaryBody("DICOMFile", instanceDataPath.toFile(), contentType, "")
          .build();
      request.setEntity(httpEntity);

      request.addHeader(CONTENT_TYPE, MULTIPART_RELATED_TYPE_APPLICATION_DICOM_BOUNDARY + boundary);
      GoogleCredentials credentials = authAdc.getCredentials();
      String tokenValue = credentials.getAccessToken().getTokenValue();
      request.addHeader(AUTHORIZATION, BEARER + tokenValue);
      try (CloseableHttpResponse response = httpclient.execute(request)) {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatusCodes.STATUS_CODE_OK) {
          HttpEntity entity = response.getEntity();
          String responseBody = EntityUtils.toString(entity);
          String mimeType = ContentType.get(entity).getMimeType();
          throw new DicomFuseException(
              "Failed to upload - " + dicomPath + "\n" + response.getStatusLine() +
                  "\n" + StowErrorFormatter.formatByMimeType(responseBody, mimeType), statusCode);
        }
      }
    } catch (IOException | URISyntaxException e) {
      throw new DicomFuseException(e);
    }
  }

  private void createRequestToDeleteInstance(URIBuilder uriBuilder) throws DicomFuseException {
    try (CloseableHttpClient httpclient = httpClientFactory.createHttpClient()) {
      URI uri = uriBuilder.build();
      HttpDelete request = new HttpDelete(uri);
      request.addHeader(CONTENT_TYPE, APPLICATION_JSON_CHARSET_UTF8);
      GoogleCredentials credentials = authAdc.getCredentials();
      String tokenValue = credentials.getAccessToken().getTokenValue();
      request.addHeader(AUTHORIZATION, BEARER + tokenValue);
      try (CloseableHttpResponse response = httpclient.execute(request)) {
        checkStatusCode(response, uri);
      }
    } catch (IOException | URISyntaxException e) {
      throw new DicomFuseException(e);
    }
  }

  private void checkStatusCode(CloseableHttpResponse response, URI uri) throws DicomFuseException {
    int statusCode = response.getStatusLine().getStatusCode();
    if (statusCode != HttpStatusCodes.STATUS_CODE_OK && statusCode != HttpStatusCodes.STATUS_CODE_NO_CONTENT) {
      throw new DicomFuseException("Failed HTTP " + response.getStatusLine() + " " + uri,
          statusCode);
    }
  }
} public interface HttpClientFactory {

  CloseableHttpClient createHttpClient();
} public class CloudConf {

  private String stage;
  private String project;
  private String location;
  private String dataSet;

  public CloudConf(String stage, String project, String location, String dataSet) {
    this.stage = stage;
    this.project = project;
    this.location = location;
    this.dataSet = dataSet;
  }

  public String getStage() {
    return stage;
  }

  public String getProject() {
    return project;
  }

  public String getLocation() {
    return location;
  }

  public String getDataSet() {
    return dataSet;
  }
} public class DicomPath {

  private static final String DCM_EXTENSION = ".dcm";
  private final DicomPathLevel dicomPathLevel;
  private final String dicomStoreId;
  private final String studyInstanceUID;
  private final String seriesInstanceUID;
  private final String sopInstanceUID;
  private final String fileName;


  public static class Builder {

    private final DicomPathLevel dicomPathLevel;
    private String dicomStoreId = "";
    private String studyInstanceUID = "";
    private String seriesInstanceUID = "";
    private String sopInstanceUID = "";
    private String fileName = "";

    public Builder(DicomPathLevel dicomPathLevel) {
      this.dicomPathLevel = dicomPathLevel;
    }

    public Builder dicomStoreId(String dicomStoreId) {
      this.dicomStoreId = dicomStoreId;
      return this;
    }

    public Builder studyInstanceUID(String studyInstanceUID) {
      this.studyInstanceUID = studyInstanceUID;
      return this;
    }

    public Builder seriesInstanceUID(String seriesInstanceUID) {
      this.seriesInstanceUID = seriesInstanceUID;
      return this;
    }

    public Builder sopInstanceUID(String sopInstanceUID) {
      this.sopInstanceUID = sopInstanceUID;
      return this;
    }

    public Builder fileName(String fileName) {
      this.fileName = fileName;
      return this;
    }

    public DicomPath build() {
      return new DicomPath(this);
    }
  }

  private DicomPath(Builder builder) {
    dicomPathLevel = builder.dicomPathLevel;
    dicomStoreId = builder.dicomStoreId;
    studyInstanceUID = builder.studyInstanceUID;
    seriesInstanceUID = builder.seriesInstanceUID;
    sopInstanceUID = builder.sopInstanceUID;
    fileName = builder.fileName;
  }

  public DicomPathLevel getDicomPathLevel() {
    return dicomPathLevel;
  }

  public String getDicomStoreId() {
    return dicomStoreId;
  }

  public String getStudyInstanceUID() {
    return studyInstanceUID;
  }

  public String getSeriesInstanceUID() {
    return seriesInstanceUID;
  }

  public String getSopInstanceUID() {
    return sopInstanceUID;
  }

  public String getFileName() {
    return fileName;
  }

  @Override
  public String toString() {
    StringBuilder stringBuilder = new StringBuilder();
    switch (dicomPathLevel) {
      case DATASET:
        break;
      case DICOM_STORE:
        stringBuilder.append(dicomStoreId);
        break;
      case STUDY:
        stringBuilder.append(dicomStoreId);
        stringBuilder.append("/");
        stringBuilder.append(studyInstanceUID);
        break;
      case SERIES:
        stringBuilder.append(dicomStoreId);
        stringBuilder.append("/");
        stringBuilder.append(studyInstanceUID);
        stringBuilder.append("/");
        stringBuilder.append(seriesInstanceUID);
        break;
      case INSTANCE:
        stringBuilder.append(dicomStoreId);
        stringBuilder.append("/");
        stringBuilder.append(studyInstanceUID);
        stringBuilder.append("/");
        stringBuilder.append(seriesInstanceUID);
        stringBuilder.append("/");
        stringBuilder.append(sopInstanceUID);
        stringBuilder.append(DCM_EXTENSION);
        break;
      case TEMP_FILE_IN_DICOM_STORE:
        stringBuilder.append(dicomStoreId);
        stringBuilder.append("/");
        stringBuilder.append(fileName);
        break;
      case TEMP_FILE_IN_SERIES:
        stringBuilder.append(dicomStoreId);
        stringBuilder.append("/");
        stringBuilder.append(studyInstanceUID);
        stringBuilder.append("/");
        stringBuilder.append(seriesInstanceUID);
        stringBuilder.append("/");
        stringBuilder.append(fileName);
        break;
      default:
    }
    return stringBuilder.toString();
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    DicomPath dicomPath = (DicomPath) o;
    return dicomPathLevel == dicomPath.dicomPathLevel &&
        Objects.equals(dicomStoreId, dicomPath.dicomStoreId) &&
        Objects.equals(studyInstanceUID, dicomPath.studyInstanceUID) &&
        Objects.equals(seriesInstanceUID, dicomPath.seriesInstanceUID) &&
        Objects.equals(sopInstanceUID, dicomPath.sopInstanceUID) &&
        Objects.equals(fileName, dicomPath.fileName);
  }

  @Override
  public int hashCode() {
    return Objects
        .hash(dicomPathLevel, dicomStoreId, studyInstanceUID, seriesInstanceUID, sopInstanceUID,
            fileName);
  }
} public class Cache {

  private CachedDataset cachedDataset = new CachedDataset();

  public CachedDicomStore getCachedDicomStore(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = cachedDataset.getCachedDicomStores()
        .get(dicomPath.getDicomStoreId());
    if (cachedDicomStore == null) {
      throw new DicomFuseException("null cached DICOM Store - " + dicomPath);
    }
    return cachedDicomStore;
  }

  private CachedStudy getCachedStudy(CachedDicomStore cachedDicomStore, DicomPath dicomPath)
      throws DicomFuseException {
    CachedStudy cachedStudy = cachedDicomStore.getCachedStudies()
        .get(dicomPath.getStudyInstanceUID());
    if (cachedStudy == null) {
      throw new DicomFuseException("null cached Study - " + dicomPath);
    }
    return cachedStudy;
  }

  private CachedSeries getCachedSeries(CachedStudy cachedStudy, DicomPath dicomPath)
      throws DicomFuseException {
    CachedSeries cachedSeries = cachedStudy.getCachedSeries().get(dicomPath.getSeriesInstanceUID());
    if (cachedSeries == null) {
      throw new DicomFuseException("null cached Series - " + dicomPath);
    }
    return cachedSeries;
  }

  private InstanceContent getInstanceContent(CachedSeries cachedSeries, DicomPath dicomPath)
      throws DicomFuseException {
    InstanceContent instanceContent = cachedSeries.getCachedInstances()
        .get(dicomPath.getSopInstanceUID());
    if (instanceContent == null) {
      throw new DicomFuseException("null instance content - " + dicomPath);
    }
    return instanceContent;
  }

  private InstanceContent getTempInstanceContent(CachedDicomStore cachedDicomStore,
      DicomPath dicomPath) throws DicomFuseException {
    InstanceContent instanceContent = cachedDicomStore.getCachedTempInstances()
        .get(dicomPath.getFileName());
    if (instanceContent == null) {
      throw new DicomFuseException("null instance content - " + dicomPath);
    }
    return instanceContent;
  }

  public ConcurrentHashMap<String, CachedDicomStore> getCachedDicomStores() {
    return cachedDataset.getCachedDicomStores();
  }

  public ConcurrentHashMap<String, CachedStudy> getCachedStudies(DicomPath dicomPath)
      throws DicomFuseException {
    return getCachedDicomStore(dicomPath).getCachedStudies();
  }

  public ConcurrentHashMap<String, CachedSeries> getCachedSeries(DicomPath dicomPath)
      throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    return getCachedStudy(cachedDicomStore, dicomPath).getCachedSeries();
  }

  public ConcurrentHashMap<String, InstanceContent> getCachedInstances(DicomPath dicomPath)
      throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
    return getCachedSeries(cachedStudy, dicomPath).getCachedInstances();
  }

  public ConcurrentHashMap<String, InstanceContent> getCachedTempInstances(DicomPath dicomPath)
      throws DicomFuseException {
    return getCachedDicomStore(dicomPath).getCachedTempInstances();
  }

  public List<DicomStore> getCachedDicomStoreList() {
    List<DicomStore> cachedDicomStoreList = new ArrayList<>();
    for (CachedDicomStore cachedDicomStore : cachedDataset.getCachedDicomStores().values()) {
      cachedDicomStoreList.add(cachedDicomStore.getDicomStore());
    }
    return cachedDicomStoreList;
  }

  public List<Study> getCachedStudyList(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    List<Study> cachedStudyList = new ArrayList<>();
    for (CachedStudy cachedStudy : cachedDicomStore.getCachedStudies().values()) {
      cachedStudyList.add(cachedStudy.getStudy());
    }
    return cachedStudyList;
  }

  public List<Series> getCachedSeriesList(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
    List<Series> cachedSeriesList = new ArrayList<>();
    for (CachedSeries cachedSeries : cachedStudy.getCachedSeries().values()) {
      cachedSeriesList.add(cachedSeries.getSeries());
    }
    return cachedSeriesList;
  }

  public List<Instance> getCachedInstanceList(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
    CachedSeries cachedSeries = getCachedSeries(cachedStudy, dicomPath);
    List<Instance> cachedInstancesList = new ArrayList<>();
    for (InstanceContent instanceContent : cachedSeries.getCachedInstances().values()) {
      cachedInstancesList.add(instanceContent.getInstance());
    }
    return cachedInstancesList;
  }

  public InstanceContent getInstanceContent(DicomPath dicomPath) throws DicomFuseException {
    DicomPathLevel dicomPathLevel = dicomPath.getDicomPathLevel();
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    switch (dicomPathLevel) {
      case INSTANCE:
        CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
        CachedSeries cachedSeries = getCachedSeries(cachedStudy, dicomPath);
        return getInstanceContent(cachedSeries, dicomPath);
      case TEMP_FILE_IN_DICOM_STORE:
      case TEMP_FILE_IN_SERIES:
        return getTempInstanceContent(cachedDicomStore, dicomPath);
      default:
        throw new DicomFuseException("Invalid dicom path level - " + dicomPath);
    }
  }

  public long getInstanceSize(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
    CachedSeries cachedSeries = getCachedSeries(cachedStudy, dicomPath);
    return getInstanceContent(cachedSeries, dicomPath).getInstanceSize();
  }

  public Command getInstanceCommand(DicomPath dicomPath) throws DicomFuseException {
    DicomPathLevel dicomPathLevel = dicomPath.getDicomPathLevel();
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    InstanceContent instanceContent;
    switch (dicomPathLevel) {
      case INSTANCE:
        CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
        CachedSeries cachedSeries = getCachedSeries(cachedStudy, dicomPath);
        instanceContent = getInstanceContent(cachedSeries, dicomPath);
        return instanceContent.getCommand();
      case TEMP_FILE_IN_DICOM_STORE:
      case TEMP_FILE_IN_SERIES:
        instanceContent = getTempInstanceContent(cachedDicomStore, dicomPath);
        return instanceContent.getCommand();
      default:
        throw new DicomFuseException("Invalid dicom path level - " + dicomPath);
    }
  }

  public void setInstanceCommand(DicomPath dicomPath, Command command) throws DicomFuseException {
    DicomPathLevel dicomPathLevel = dicomPath.getDicomPathLevel();
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    InstanceContent instanceContent;
    switch (dicomPathLevel) {
      case INSTANCE:
        CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
        CachedSeries cachedSeries = getCachedSeries(cachedStudy, dicomPath);
        instanceContent = getInstanceContent(cachedSeries, dicomPath);
        instanceContent.setCommand(command);
        break;
      case TEMP_FILE_IN_DICOM_STORE:
      case TEMP_FILE_IN_SERIES:
        instanceContent = getTempInstanceContent(cachedDicomStore, dicomPath);
        instanceContent.setCommand(command);
        break;
      default:
        throw new DicomFuseException("Invalid dicom path level - " + dicomPath);
    }
  }

  /**
   * Checks that Dataset outdated or not.
   *
   * @return true if Dataset outdated, false if not
   */
  public boolean isDatasetOutdated() {
    // Instant.now() - Returns a value in microseconds in Java 9 and later, but returns
    // milliseconds in Java 8. That's why, to avoid incorrect isBefore(instantNow) results in
    // cases where less than a millisecond has passed equals(instantNow) were included.
    Instant instantNow = Instant.now();
    return cachedDataset.getDatasetCacheTime().isBefore(instantNow) ||
        cachedDataset.getDatasetCacheTime().equals(instantNow);
  }

  /**
   * Checks that DICOM Store outdated or not.
   *
   * @param dicomPath current DICOM path to the DICOM Store
   * @return true if DICOM Store outdated, false if not
   */
  public boolean isDicomStoreOutdated(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    // Instant.now() - Returns a value in microseconds in Java 9 and later, but returns
    // milliseconds in Java 8. That's why, to avoid incorrect isBefore(instantNow) results in
    // cases where less than a millisecond has passed equals(instantNow) were included.
    Instant instantNow = Instant.now();
    return cachedDicomStore.getDicomStoreCacheTime().isBefore(instantNow) ||
        cachedDicomStore.getDicomStoreCacheTime().equals(instantNow);
  }

  /**
   * Checks that Study outdated or not.
   *
   * @param dicomPath current DICOM path to the Study
   * @return true if Study outdated, false if not
   */
  public boolean isStudyOutdated(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
    // Instant.now() - Returns a value in microseconds in Java 9 and later, but returns
    // milliseconds in Java 8. That's why, to avoid incorrect isBefore(instantNow) results in
    // cases where less than a millisecond has passed equals(instantNow) were included.
    Instant instantNow = Instant.now();
    return cachedStudy.getStudyCacheTime().isBefore(instantNow) ||
        cachedStudy.getStudyCacheTime().equals(instantNow);
  }

  /**
   * Checks that Series outdated or not.
   *
   * @param dicomPath current DICOM path to the Series
   * @return true if Series outdated, false if not
   */
  public boolean isSeriesOutdated(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
    CachedSeries cachedSeries = getCachedSeries(cachedStudy, dicomPath);
    // Instant.now() - Returns a value in microseconds in Java 9 and later, but returns
    // milliseconds in Java 8. That's why, to avoid incorrect isBefore(instantNow) results in
    // cases where less than a millisecond has passed equals(instantNow) were included.
    Instant instantNow = Instant.now();
    return cachedSeries.getSeriesCacheTime().isBefore(instantNow) ||
        cachedSeries.getSeriesCacheTime().equals(instantNow);
  }

  public boolean isDicomStoreNotExist(DicomPath dicomPath) {
    return !cachedDataset.getCachedDicomStores().containsKey(dicomPath.getDicomStoreId());
  }

  public boolean isStudyNotExist(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    return !cachedDicomStore.getCachedStudies().containsKey(dicomPath.getStudyInstanceUID());
  }

  public boolean isSeriesNotExist(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
    return !cachedStudy.getCachedSeries().containsKey(dicomPath.getSeriesInstanceUID());
  }

  public boolean isInstanceNotExist(DicomPath dicomPath) throws DicomFuseException {
    DicomPathLevel dicomPathLevel = dicomPath.getDicomPathLevel();
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    switch (dicomPathLevel) {
      case INSTANCE:
        CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
        CachedSeries cachedSeries = getCachedSeries(cachedStudy, dicomPath);
        return !cachedSeries.getCachedInstances().containsKey(dicomPath.getSopInstanceUID());
      case TEMP_FILE_IN_DICOM_STORE:
      case TEMP_FILE_IN_SERIES:
        return !cachedDicomStore.getCachedTempInstances()
            .containsKey(dicomPath.getFileName());
      default:
        throw new DicomFuseException("Invalid dicom path level - " + dicomPath);
    }
  }

  public void setDatasetCacheTime(Instant instant) {
    cachedDataset.setDatasetCacheTime(instant);
  }

  public void setDicomStoreCacheTime(DicomPath dicomPath, Instant instant)
      throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    cachedDicomStore.setDicomStoreCacheTime(instant);
  }

  public void setStudyCacheTime(DicomPath dicomPath, Instant instant) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
    cachedStudy.setStudyCacheTime(instant);
  }

  public void setSeriesCacheTime(DicomPath dicomPath, Instant instant) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    CachedStudy cachedStudy = getCachedStudy(cachedDicomStore, dicomPath);
    CachedSeries cachedSeries = getCachedSeries(cachedStudy, dicomPath);
    cachedSeries.setSeriesCacheTime(instant);
  }

  public AtomicLong getOffset(DicomPath dicomPath) throws DicomFuseException {
    CachedDicomStore cachedDicomStore = getCachedDicomStore(dicomPath);
    return getTempInstanceContent(cachedDicomStore, dicomPath).getOffset();
  }
} public class DicomFuseException extends Exception {

  private int statusCode;

  public DicomFuseException(String message) {
    super(message);
  }

  public DicomFuseException(String message, Exception e) {
    super(message, e);
  }

  public DicomFuseException(Exception e) {
    super(e);
  }

  public DicomFuseException(String message, int statusCode) {
    super(message);
    this.statusCode = statusCode;
  }

  public int getStatusCode() {
    return statusCode;
  }
} public class DicomPathCacher {

  private final ConcurrentHashMap<String, DicomPath> temporaryDicomPaths = new ConcurrentHashMap<>();

  public void putDicomPath(String path, DicomPath dicomPath) {
    temporaryDicomPaths.put(path, dicomPath);
  }

  public DicomPath getDicomPath(String path) {
    return temporaryDicomPaths.get(path);
  }

  public void removeDicomPath(String fileName) {
    temporaryDicomPaths.remove(fileName);
  }
} public class Arguments {

  @Parameter(
      names = {"--datasetAddr", "-a"},
      descriptionKey = "option.datasetAddr",
      required = true,
      order = 0,
      converter = CloudConfigurationConverter.class
  )
  public CloudConf cloudConf;

  @Parameter(
      names = {"--mountPath", "-p"},
      descriptionKey = "option.mountPath",
      required = true,
      order = 1,
      converter = PathConverter.class
  )
  public Path mountPath;

  @Parameter(
      names = {"--cacheTime", "-t"},
      descriptionKey = "option.cacheTime",
      converter = CacheTimeConverter.class,
      order = 2,
      validateWith = CacheTimePositiveValidator.class
  )
  public CacheTime cacheTime = new CacheTime(60, 300);

  @Parameter(
      names = {"--cacheSize", "-s"},
      descriptionKey = "option.cacheSize",
      converter = LongConverter.class,
      order = 3,
      validateWith = CacheSizePositiveValidator.class
  )
  public long cacheSize = 10000;

  @Parameter(
      names = {"--enableDeletion", "-d"},
      descriptionKey = "option.enableDeletion",
      order = 4,
      converter = BooleanConverter.class
  )
  public boolean enableDeletion = true;

  @Parameter(
      names = {"--keyFile", "-k"},
      descriptionKey = "option.keyFile",
      order = 5,
      converter = PathConverter.class
  )
  public Path keyPath;

  @Parameter(
      names = {"--extraMountOptions"},
      descriptionKey = "option.extraMountOptions",
      order = 6
  )
  public List<String> extraMountOptions = new ArrayList<>();

  @Parameter(
      names = {"--help", "-h"},
      help = true,
      descriptionKey = "option.help",
      order = 7
  )
  public boolean help = false;
}
import static com.google.dicomwebfuse.EntityType.DICOM_STORE;
import static com.google.dicomwebfuse.EntityType.INSTANCE;
import static com.google.dicomwebfuse.EntityType.SERIES;
import static com.google.dicomwebfuse.EntityType.STUDY;
import static com.google.dicomwebfuse.dao.Constants.MAX_INSTANCES_IN_SERIES;
import static com.google.dicomwebfuse.dao.Constants.MAX_SERIES_IN_STUDY;
import static com.google.dicomwebfuse.dao.Constants.MAX_STUDIES_IN_DICOM_STORE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import com.google.api.client.http.HttpStatusCodes;
import com.google.dicomwebfuse.TestUtils;
import com.google.dicomwebfuse.auth.AuthAdc;
import com.google.dicomwebfuse.dao.FuseDao;
import com.google.dicomwebfuse.dao.FuseDaoImpl;
import com.google.dicomwebfuse.dao.http.HttpClientFactory;
import com.google.dicomwebfuse.entities.CloudConf;
import com.google.dicomwebfuse.entities.DicomPath;
import com.google.dicomwebfuse.entities.cache.Cache;
import com.google.dicomwebfuse.exception.DicomFuseException;
import com.google.dicomwebfuse.fuse.cacher.DicomPathCacher;
import com.google.dicomwebfuse.parser.Arguments;
import java.io.IOException;
import jnr.ffi.Platform;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
// Copyright 2019 Google LLC
//
// 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.google.dicomwebfuse.fuse;



class DicomFuseHelperTest {

  @Test
  void testShouldSuccessfullyOpenUnlistedStudy() throws IOException, DicomFuseException {