content: package com.airbnb.airpal.resources;
  
  import com.amazonaws.services.s3.model.ObjectMetadata;
  import com.opencsv.CSVReader;
  import com.airbnb.airpal.core.store.files.ExpiringFileStore;
  import com.fasterxml.jackson.annotation.JsonProperty;
  import com.google.inject.Inject;
  import lombok.Data;
  import lombok.extern.slf4j.Slf4j;
  import lombok.val;
  import com.amazonaws.services.s3.model.GetObjectRequest;
  import com.amazonaws.services.s3.AmazonS3;
  
  import javax.inject.Named;
  import javax.ws.rs.DefaultValue;
  import javax.ws.rs.GET;
  import javax.ws.rs.Path;
  import javax.ws.rs.Produces;
  import javax.ws.rs.QueryParam;
  import javax.ws.rs.core.MediaType;
  import javax.ws.rs.core.Response;
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.FileNotFoundException;
  import java.io.FileReader;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.net.URI;
  import java.util.zip.GZIPInputStream;
  
  @Slf4j
  @Path("/api/preview")
  public class ResultsPreviewResource
  {
      private final ExpiringFileStore fileStore;
      private final AmazonS3 s3Client;
      private final String outputBucket;
  
      @Inject
      public ResultsPreviewResource(
              ExpiringFileStore fileStore,
              AmazonS3 s3Client,
              @Named("s3Bucket") String outputBucket)
      {
          this.fileStore = fileStore;
          this.s3Client = s3Client;
          this.outputBucket = outputBucket;
      }
  
      @GET
      @Path("/")
      @Produces(MediaType.APPLICATION_JSON)
      public Response getFile(@QueryParam("fileURI") URI fileURI,
                              @DefaultValue("100") @QueryParam("lines") int numLines)
      {
          if (fileURI.getPath().startsWith("/api/s3")) {
              return getS3Preview(fileURI, numLines);
          } else {
              return getFilePreview(fileURI, numLines);
          }
      }
  
      private String getOutputKey(String fileBaseName)
      {
          return "airpal/" + fileBaseName;
      }
  
      private String getFilename(URI fileURI)
      {
          return fileURI.getPath().substring(fileURI.getPath().lastIndexOf('/') + 1);
      }
  
      private Response getS3Preview(URI fileURI, int numLines) {
          val filename = getFilename(fileURI);
          val outputKey = getOutputKey(filename);
          // download ~100 kb (depending on your definition) of the file
          val request = new GetObjectRequest(
                  outputBucket,
                  outputKey
          ).withRange(0, 100 * 1024);
          val object = s3Client.getObject(request);
          ObjectMetadata objectMetadata = object.getObjectMetadata();
          boolean gzip = "gzip".equalsIgnoreCase(objectMetadata.getContentEncoding());
          try (InputStream input = object.getObjectContent()) {
              InputStreamReader reader;
              if (gzip) {
                  reader = new InputStreamReader(new GZIPInputStream(input));
              }
              else {
                  reader = new InputStreamReader(input);
              }
              return getPreviewFromCSV(new CSVReader(reader), numLines);
          }
          catch (IOException e) {
              return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
          }
      }
  
      private Response getPreviewFromCSV(CSVReader reader, final int numLines) {
          List<Map<String, String>> columns = new ArrayList<>();
          List<List<String>> rows = new ArrayList<>();
          try {
              for (final String columnName: reader.readNext()) {
                  columns.add(new HashMap<String, String>(){{
                      put("name", columnName);
                  }});
              }
              int counter = 0;
              for (String[] line : reader) {
                  counter++;
                  rows.add(Arrays.asList(line));
                  if (counter >= numLines) {
                    break;
                  }
              }
          } catch (IOException e) {
              log.error(e.getMessage());
              return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
          }
          return Response.ok(new PreviewResponse(columns, rows)).build();
      }
  
      private Response getFilePreview(URI fileURI, int numLines) {
          String fileName = getFilename(fileURI);
          final File file = fileStore.get(fileName);
          try {
              if (file == null) {
                  throw new FileNotFoundException(fileName + " could not be found");
              }
              try (final CSVReader reader = new CSVReader(new FileReader(file))) {
                return getPreviewFromCSV(reader, numLines);
              } catch (IOException e) {
                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
              }
          } catch (FileNotFoundException e) {
              log.warn(e.getMessage());
              return Response.status(Response.Status.NOT_FOUND).build();
          }
      }
  
  
      @Data
      private static class PreviewResponse
      {
          @JsonProperty
          private final List<Map<String, String>> columns;
  
          @JsonProperty
          private final List<List<String>> data;
      }
  }
  