diff --git a/cadc-tap-schema/build.gradle b/cadc-tap-schema/build.gradle index 313b9bd3..bbfb33e3 100644 --- a/cadc-tap-schema/build.gradle +++ b/cadc-tap-schema/build.gradle @@ -16,7 +16,7 @@ sourceCompatibility = 1.8 group = 'org.opencadc' -version = '1.2.3' +version = '1.2.4' description = 'OpenCADC TAP-1.1 tap schema server library' def git_url = 'https://github.com/opencadc/tap' diff --git a/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/schema/TapSchemaUtil.java b/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/schema/TapSchemaUtil.java index 5cc854f7..8374d998 100644 --- a/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/schema/TapSchemaUtil.java +++ b/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/schema/TapSchemaUtil.java @@ -3,7 +3,7 @@ ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** * -* (c) 2017. (c) 2017. +* (c) 2024. (c) 2024. * Government of Canada Gouvernement du Canada * National Research Council Conseil national de recherches * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 @@ -67,7 +67,9 @@ package ca.nrc.cadc.tap.schema; +import ca.nrc.cadc.dali.tables.votable.VOTableDocument; import ca.nrc.cadc.dali.tables.votable.VOTableField; +import ca.nrc.cadc.dali.tables.votable.VOTableResource; import ca.nrc.cadc.dali.tables.votable.VOTableTable; import org.apache.log4j.Logger; @@ -101,12 +103,11 @@ public static TableDesc createTableDesc(String schemaName, String tableName, VOT } catch (ADQLIdentifierException ex) { throw new IllegalArgumentException("invalid ADQL identifier (table name): " + tableName, ex); } - - TableDesc ret = new TableDesc(schemaName, tableName); if (votable == null) { throw new IllegalArgumentException("invalid input: no VOTable with column metadata"); } + TableDesc ret = new TableDesc(schemaName, tableName); for (VOTableField f : votable.getFields()) { try { checkValidIdentifier(f.getName()); @@ -127,10 +128,14 @@ public static TableDesc createTableDesc(String schemaName, String tableName, VOT * @return The associated VOTableField */ public static VOTableField convert(ColumnDesc column) { - VOTableField vtf = new VOTableField( - column.getColumnName(), column.getDatatype().getDatatype(), - column.getDatatype().arraysize); + VOTableField vtf = new VOTableField(column.getColumnName(), + column.getDatatype().getDatatype(), column.getDatatype().arraysize); vtf.xtype = column.getDatatype().xtype; + vtf.description = column.description; + vtf.id = column.id; + vtf.ucd = column.ucd; + vtf.unit = column.unit; + vtf.utype = column.utype; return vtf; } @@ -231,4 +236,36 @@ public static boolean isValidIdentifierCharacter(char c) { } return false; } + + /** + * Create VOTable description of a TableDesc. + * @param tableDesc + * @return + */ + public static VOTableDocument createVOTable(TableDesc tableDesc) { + try { + checkValidTableName(tableDesc.getTableName()); + } catch (ADQLIdentifierException ex) { + throw new IllegalArgumentException("invalid ADQL identifier (table name): " + + tableDesc.getTableName(), ex); + } + + VOTableDocument document = new VOTableDocument(); + VOTableResource resource = new VOTableResource("results"); + document.getResources().add(resource); + VOTableTable table = new VOTableTable(); + resource.setTable(table); + + for (ColumnDesc column : tableDesc.getColumnDescs()) { + try { + checkValidIdentifier(column.getColumnName()); + table.getFields().add(convert(column)); + } catch (ADQLIdentifierException ex) { + throw new IllegalArgumentException("invalid ADQL identifier (column name): " + + column.getColumnName(), ex); + } + } + return document; + } + } diff --git a/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/GetAction.java b/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/GetAction.java index b7323fd0..2d794173 100644 --- a/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/GetAction.java +++ b/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/GetAction.java @@ -67,17 +67,19 @@ package ca.nrc.cadc.vosi.actions; +import ca.nrc.cadc.dali.tables.votable.VOTableDocument; +import ca.nrc.cadc.dali.tables.votable.VOTableWriter; import ca.nrc.cadc.net.ResourceNotFoundException; -import ca.nrc.cadc.rest.RestAction; import ca.nrc.cadc.tap.schema.SchemaDesc; import ca.nrc.cadc.tap.schema.TableDesc; import ca.nrc.cadc.tap.schema.TapSchema; import ca.nrc.cadc.tap.schema.TapSchemaDAO; import ca.nrc.cadc.tap.schema.TapSchemaLoader; +import ca.nrc.cadc.tap.schema.TapSchemaUtil; import ca.nrc.cadc.vosi.TableSetWriter; import ca.nrc.cadc.vosi.TableWriter; +import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; -import java.security.AccessControlException; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; @@ -131,10 +133,22 @@ public void doAction() throws Exception { // currently, permission check already threw this throw new ResourceNotFoundException("table not found: " + tableName); } - TableWriter tw = new TableWriter(); - syncOutput.setCode(HttpServletResponse.SC_OK); - syncOutput.setHeader("Content-Type", "text/xml"); - tw.write(td, new OutputStreamWriter(syncOutput.getOutputStream())); + + // If the Accept header = application/x-votable+xml, + // output the TableDesc as a VOTable + String accept = syncInput.getHeader("Accept"); + if (VOTableWriter.CONTENT_TYPE.equals(accept)) { + VOTableDocument vot = TapSchemaUtil.createVOTable(td); + VOTableWriter tw = new VOTableWriter(); + syncOutput.setCode(HttpServletResponse.SC_OK); + syncOutput.setHeader("Content-Type", VOTableWriter.CONTENT_TYPE); + tw.write(vot, new OutputStreamWriter(syncOutput.getOutputStream())); + } else { + TableWriter tw = new TableWriter(); + syncOutput.setCode(HttpServletResponse.SC_OK); + syncOutput.setHeader("Content-Type", "text/xml"); + tw.write(td, new OutputStreamWriter(syncOutput.getOutputStream())); + } } else if (schemaName != null) { checkViewSchemaPermissions(dao, schemaName, logInfo); // TODO: TapSchemaDAO only supports schema only, ok for detail=min diff --git a/youcat/build.gradle b/youcat/build.gradle index 733cf1f1..0e811a6c 100644 --- a/youcat/build.gradle +++ b/youcat/build.gradle @@ -35,7 +35,7 @@ dependencies { compile 'org.opencadc:cadc-uws:[1.0.2,)' compile 'org.opencadc:cadc-uws-server:[1.2.22,)' compile 'org.opencadc:cadc-tap:[1.1.17,)' - compile 'org.opencadc:cadc-tap-schema:[1.2.3,)' + compile 'org.opencadc:cadc-tap-schema:[1.2.4,)' compile 'org.opencadc:cadc-tap-server:[1.1.26,)' compile 'org.opencadc:cadc-tap-server-pg:[1.1.1,)' compile 'org.opencadc:cadc-adql:[1.1.4,)' diff --git a/youcat/src/intTest/java/org/opencadc/youcat/VosiTablesTest.java b/youcat/src/intTest/java/org/opencadc/youcat/VosiTablesTest.java index 238b08d2..a9598a58 100644 --- a/youcat/src/intTest/java/org/opencadc/youcat/VosiTablesTest.java +++ b/youcat/src/intTest/java/org/opencadc/youcat/VosiTablesTest.java @@ -2,8 +2,12 @@ package org.opencadc.youcat; import ca.nrc.cadc.auth.AuthMethod; +import ca.nrc.cadc.dali.tables.votable.VOTableDocument; +import ca.nrc.cadc.dali.tables.votable.VOTableReader; +import ca.nrc.cadc.dali.tables.votable.VOTableWriter; import ca.nrc.cadc.net.ContentType; import ca.nrc.cadc.net.HttpDownload; +import ca.nrc.cadc.net.HttpGet; import ca.nrc.cadc.reg.Standards; import ca.nrc.cadc.reg.client.RegistryClient; import ca.nrc.cadc.tap.schema.SchemaDesc; @@ -14,8 +18,8 @@ import ca.nrc.cadc.vosi.TableSetReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.net.URI; import java.net.URL; +import java.nio.charset.StandardCharsets; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Assert; @@ -31,7 +35,7 @@ public class VosiTablesTest static { - Log4jInit.setLevel("ca.nrc.cadc.cat", Level.INFO); + Log4jInit.setLevel("org.opencadc.youcat", Level.INFO); } URL tablesURL; @@ -137,10 +141,38 @@ public void testDetailMin() Assert.assertTrue("no columns:" + td.getTableName(), td.getColumnDescs().isEmpty()); } } - catch(Exception unexpected) + catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); } } + + @Test + public void testValidateVOTableDoc() { + try { + String s = tablesURL.toExternalForm() + "/tap_schema.tables"; + log.info("testValidateVOTableDoc: " + s); + + URL url = new URL(s); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + HttpGet get = new HttpGet(url, bos); + get.setRequestProperty("Accept", VOTableWriter.CONTENT_TYPE); + get.run(); + Assert.assertNull(get.getThrowable()); + Assert.assertEquals(200, get.getResponseCode()); + Assert.assertEquals(VOTableWriter.CONTENT_TYPE, get.getContentType()); + + log.debug("VOTable XML: " + bos.toString(StandardCharsets.UTF_8)); + + VOTableReader tr = new VOTableReader(true); + VOTableDocument td = tr.read(new ByteArrayInputStream(bos.toByteArray())); + Assert.assertNotNull(td); + Assert.assertFalse("tap_schema.tables", td.getResources().isEmpty()); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + }