From c1766213d6d56520a304461cfbd50a3064c37064 Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Fri, 12 Apr 2024 13:43:03 -0700 Subject: [PATCH] minoc: use filenameOverride feature in cutout logic this makes sure that vault cutouts have the real fits file name and not the opaque vault storage id --- .../opencadc/minoc/FitsOperationsTest.java | 34 +++++++++++----- .../java/org/opencadc/minoc/GetAction.java | 39 ++++++++++++------- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/minoc/src/intTest/java/org/opencadc/minoc/FitsOperationsTest.java b/minoc/src/intTest/java/org/opencadc/minoc/FitsOperationsTest.java index 48f874a3..6b060b83 100644 --- a/minoc/src/intTest/java/org/opencadc/minoc/FitsOperationsTest.java +++ b/minoc/src/intTest/java/org/opencadc/minoc/FitsOperationsTest.java @@ -118,6 +118,7 @@ public class FitsOperationsTest extends MinocTest { protected URL filesVaultURL; private String putContentType = "application/fits"; + private String filenameOverridePrefix = null; static { Log4jInit.setLevel("org.opencadc.minoc", Level.INFO); @@ -217,6 +218,13 @@ public void testSimple() throws Exception { final URL noclArtifactURL = new URL(filesURL + "/" + noclArtifactURI.toString()); LOGGER.info("no content-length: " + noclArtifactURL); + try { + filenameOverridePrefix = "something-else"; + uploadAndCompareCutout(artifactURI, SodaParamValidator.SUB, cutoutSpecs, testFilePrefix); + } finally { + filenameOverridePrefix = null; + } + try { putContentType = null; uploadAndCompareCutout(noclArtifactURI, SodaParamValidator.SUB, cutoutSpecs, testFilePrefix); @@ -403,14 +411,18 @@ public void testJCMTPolarizationCutout() throws Exception { private File doCutout(final URI artifactURI, final String queryString, final String testFilePrefix, final String expectedContentType) throws Exception { - final URL artifactSUBURL = new URL(filesURL + "/" + artifactURI + String auri = artifactURI.toASCIIString(); + if (filenameOverridePrefix != null) { + auri += ":fo/" + filenameOverridePrefix + ".fits"; + } + final URL artifactSUBURL = new URL(filesURL + "/" + auri + (queryString == null ? "" : "?" + queryString)); final File outputFile = Files.createTempFile(testFilePrefix + "-", ".fits").toFile(); LOGGER.debug("Writing cutout to " + outputFile); // Perform the cutout. Subject.doAs(userSubject, (PrivilegedExceptionAction) () -> { - LOGGER.debug("Testing cutout with " + artifactSUBURL); + LOGGER.info("Testing cutout with " + artifactSUBURL); try (final FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) { final HttpGet cutoutClient = new HttpGet(artifactSUBURL, true); cutoutClient.setFollowRedirects(true); @@ -418,16 +430,20 @@ private File doCutout(final URI artifactURI, final String queryString, final Str Assert.assertEquals("Wrong content type.", expectedContentType, cutoutClient.getResponseHeader(HttpTransfer.CONTENT_TYPE)); - Assert.assertNotNull("Should include Content-Disposition (" - + cutoutClient.getResponseHeader("Content-Disposition") + ")", - cutoutClient.getResponseHeader("Content-Disposition")); + String cdisp = cutoutClient.getResponseHeader("Content-Disposition"); + LOGGER.info("content-disposition: " + cdisp); + Assert.assertNotNull("Should include Content-Disposition (" + cdisp + ")", cdisp); + if (filenameOverridePrefix != null) { + Assert.assertTrue(cdisp.contains(filenameOverridePrefix)); + } else { + Assert.assertTrue(cdisp.contains(testFilePrefix)); + } Assert.assertEquals("Should NOT contain " + HttpTransfer.CONTENT_LENGTH, -1L, cutoutClient.getContentLength()); - Assert.assertFalse("Should NOT contain " + HttpTransfer.CONTENT_MD5, - StringUtil.hasText(cutoutClient.getContentMD5())); - Assert.assertFalse("Should NOT contain " + HttpTransfer.CONTENT_ENCODING, - StringUtil.hasText(cutoutClient.getContentEncoding())); + Assert.assertNull("Should NOT contain " + HttpTransfer.CONTENT_MD5, cutoutClient.getContentMD5()); + Assert.assertNull("Should NOT contain " + HttpTransfer.DIGEST, cutoutClient.getDigest()); + Assert.assertNull("Should NOT contain " + HttpTransfer.CONTENT_ENCODING, cutoutClient.getContentEncoding()); final byte[] buffer = new byte[64 * 1024]; int bytesRead; diff --git a/minoc/src/main/java/org/opencadc/minoc/GetAction.java b/minoc/src/main/java/org/opencadc/minoc/GetAction.java index e774e779..cf74fb3e 100644 --- a/minoc/src/main/java/org/opencadc/minoc/GetAction.java +++ b/minoc/src/main/java/org/opencadc/minoc/GetAction.java @@ -185,8 +185,12 @@ public void doAction() throws Exception { log.debug("Range (" + range + ") ignored in GET with operations"); } - if (!isFITS(artifact)) { - throw new IllegalArgumentException("not a fits file: " + artifactURI); + if (!isFITS(artifact, filenameOverride)) { + String filename = artifactURI.toASCIIString(); + if (filenameOverride != null) { + filename += " aka " + filenameOverride; + } + throw new IllegalArgumentException("not a fits file: " + filename); } final List conflicts = sodaCutout.getConflicts(); @@ -270,9 +274,15 @@ private ByteCountOutputStream doByteRangeRequest(Artifact artifact, ByteRange by private ByteCountOutputStream doOperation(FitsOperations fitsOperations, SodaCutout sodaCutout) throws NoOverlapException, ReadException, IOException { + String filename = InventoryUtil.computeArtifactFilename(artifactURI); + if (filenameOverride != null) { + filename = filenameOverride; + } + if (sodaCutout.isMETA()) { log.debug("META supplied"); - final String filename = InventoryUtil.computeArtifactFilename(artifactURI) + ".txt"; + filename += ".txt"; + syncOutput.setHeader(CONTENT_DISPOSITION, "inline; filename=\"" + filename + "\""); syncOutput.setHeader(HttpTransfer.CONTENT_TYPE, "text/plain"); @@ -289,9 +299,7 @@ private ByteCountOutputStream doOperation(FitsOperations fitsOperations, SodaCut final Cutout cutout = new Cutout(); cutout.pixelCutouts = slices; - final String schemePath = artifactURI.getSchemeSpecificPart(); - final String fileName = schemePath.substring(schemePath.lastIndexOf("/") + 1); - final CutoutFileNameFormat cutoutFileNameFormat = new CutoutFileNameFormat(fileName); + final CutoutFileNameFormat cutoutFileNameFormat = new CutoutFileNameFormat(filename); syncOutput.setHeader(CONTENT_DISPOSITION, "inline; filename=\"" + cutoutFileNameFormat.format(cutout) + "\""); syncOutput.setHeader(HttpTransfer.CONTENT_TYPE, "application/fits"); @@ -368,9 +376,7 @@ private ByteCountOutputStream doOperation(FitsOperations fitsOperations, SodaCut } } - final String schemePath = artifactURI.getSchemeSpecificPart(); - final String fileName = schemePath.substring(schemePath.lastIndexOf("/") + 1); - final CutoutFileNameFormat cutoutFileNameFormat = new CutoutFileNameFormat(fileName); + final CutoutFileNameFormat cutoutFileNameFormat = new CutoutFileNameFormat(filename); syncOutput.setHeader(CONTENT_DISPOSITION, "inline; filename=\"" + cutoutFileNameFormat.format(cutout) + "\""); syncOutput.setHeader(HttpTransfer.CONTENT_TYPE, "application/fits"); @@ -394,12 +400,17 @@ private T assertSingleWCS(final String key, final List wcsValues) { } } - private boolean isFITS(final Artifact artifact) { + private boolean isFITS(final Artifact artifact, String filenameOverride) { String effectiveType = getEffectiveContentType(artifact.contentType); - final String contentType = (effectiveType != null - ? effectiveType : getContentTypeFromFilename(artifact.getURI().getSchemeSpecificPart())); - return StringUtil.hasText(contentType) - && Arrays.stream(FITS_CONTENT_TYPES).anyMatch(s -> s.equals(contentType)); + if (effectiveType == null) { + if (filenameOverride != null) { + effectiveType = getContentTypeFromFilename(filenameOverride); + } else { + effectiveType = getContentTypeFromFilename(artifact.getURI().getSchemeSpecificPart()); + } + } + final String contentType = effectiveType; + return contentType != null && Arrays.stream(FITS_CONTENT_TYPES).anyMatch(s -> s.equals(contentType)); } private String getEffectiveContentType(String s) {