From 0df427cb4218d2343439bbd49b802fee29e4baaa Mon Sep 17 00:00:00 2001 From: aratikakadiya Date: Thu, 4 Jun 2026 11:55:28 -0700 Subject: [PATCH 1/4] Categorized exception to have a better message on table creation failure. --- cadc-tap-schema/build.gradle | 2 +- .../nrc/cadc/tap/db/BasicDataTypeMapper.java | 5 +- .../java/ca/nrc/cadc/tap/db/TableCreator.java | 42 ++++++--- .../ca/nrc/cadc/vosi/actions/PutAction.java | 40 +++++--- cadc-tap/build.gradle | 2 +- .../ca/nrc/cadc/tap/schema/TapSchemaUtil.java | 10 ++ .../tap/schema/validator/ValidatorConfig.java | 3 +- .../tap/schema/validator/ViolationType.java | 1 + .../org/opencadc/youcat/CreateTableTest.java | 94 ++++++++++++++++++- 9 files changed, 162 insertions(+), 37 deletions(-) diff --git a/cadc-tap-schema/build.gradle b/cadc-tap-schema/build.gradle index 056fc79e..f9aa138a 100644 --- a/cadc-tap-schema/build.gradle +++ b/cadc-tap-schema/build.gradle @@ -15,7 +15,7 @@ sourceCompatibility = 11 group = 'org.opencadc' -version = '1.2.18' +version = '1.2.19' 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/db/BasicDataTypeMapper.java b/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/BasicDataTypeMapper.java index bafec40e..5e586607 100644 --- a/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/BasicDataTypeMapper.java +++ b/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/BasicDataTypeMapper.java @@ -179,9 +179,6 @@ public BasicDataTypeMapper() { public String getDataType(ColumnDesc columnDesc) { TapDataType tt = columnDesc.getDatatype(); TypePair dbt = findTypePair(tt); - if (dbt == null) { - throw new UnsupportedOperationException("unsupported database column type: " + tt); - } String ret = dbt.str; if (ret.equals("CHAR")) { @@ -277,7 +274,7 @@ protected TypePair findTypePair(TapDataType tt) { dbt = dataTypes.get(tmp); } if (dbt == null) { - throw new UnsupportedOperationException("unexpected datatype: " + tt); + throw new UnsupportedOperationException("Unexpected/Unsupported datatype: " + tt.getDatatype()); } log.debug("findTypePair: " + tt + " -> " + dbt); diff --git a/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java b/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java index e82e22bb..98c0c209 100644 --- a/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java +++ b/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java @@ -120,32 +120,44 @@ public void createTable(TableDesc table) { tm.commitTransaction(); prof.checkpoint("commit-transaction"); + } catch (UnsupportedOperationException | IllegalArgumentException rethrow) { + log.error("create table failed - rollback", rethrow); + if (rollback(tm, prof, false)) { + log.error("create table failed - rollback: OK"); + } + throw rethrow; } catch (Exception ex) { - try { - log.error("create table failed - rollback", ex); - tm.rollbackTransaction(); - prof.checkpoint("rollback-transaction"); + log.error("create table failed - rollback", ex); + if (rollback(tm, prof, false)) { log.error("create table failed - rollback: OK"); - } catch (Exception oops) { - log.error("create table failed - rollback : FAIL", oops); } - // TODO: categorise failures better - throw new RuntimeException("failed to create table " + table.getTableName(), ex); - } finally { + throw new RuntimeException("failed to create table " + table.getTableName() + " with cause: " + ex.getMessage(), ex); + } finally { if (tm.isOpen()) { log.error("BUG: open transaction in finally - trying to rollback"); - try { - tm.rollbackTransaction(); - prof.checkpoint("rollback-transaction"); + if (rollback(tm, prof, true)) { log.error("BUG: rollback in finally: OK"); - } catch (Exception oops) { - log.error("BUG: rollback in finally: FAIL", oops); } throw new RuntimeException("BUG: open transaction in finally"); } } } - + + private static boolean rollback(DatabaseTransactionManager tm, Profiler prof, boolean isFinally) { + try { + tm.rollbackTransaction(); + prof.checkpoint("rollback-transaction"); + } catch (Exception oops) { + if (isFinally) { + log.error("BUG: rollback in finally: FAIL", oops); + } else { + log.error("create table failed - rollback : FAIL", oops); + } + return false; + } + return true; + } + /** * Change a table name. This can also optionally change the schema name. * diff --git a/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/PutAction.java b/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/PutAction.java index 6681fdde..da48ab39 100644 --- a/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/PutAction.java +++ b/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/PutAction.java @@ -310,26 +310,23 @@ public TableDesc acceptTargetTableDesc(TableDesc td) { tm.commitTransaction(); prof.checkpoint("commit-transaction"); + } catch (UnsupportedOperationException | IllegalArgumentException | IOException rethrow) { + log.error("PUT failed - rollback", rethrow); + if (rollback(tm, prof, false)) { + log.error("PUT failed - rollback: OK"); + } + throw rethrow; } catch (Exception ex) { - try { - log.error("PUT failed - rollback", ex); - tm.rollbackTransaction(); - prof.checkpoint("rollback-transaction"); + log.error("PUT failed - rollback", ex); + if (rollback(tm, prof, false)) { log.error("PUT failed - rollback: OK"); - } catch (Exception oops) { - log.error("PUT failed - rollback : FAIL", oops); } - // TODO: categorise failures better - throw new RuntimeException("failed to create/add " + tableName, ex); - } finally { + throw new RuntimeException("failed to create/add " + tableName + " with cause: " + ex.getMessage(), ex); + } finally { if (tm.isOpen()) { log.error("BUG: open transaction in finally - trying to rollback"); - try { - tm.rollbackTransaction(); - prof.checkpoint("rollback-transaction"); + if (rollback(tm, prof, true)) { log.error("BUG: rollback in finally: OK"); - } catch (Exception oops) { - log.error("BUG: rollback in finally: FAIL", oops); } throw new RuntimeException("BUG: open transaction in finally"); } @@ -337,6 +334,21 @@ public TableDesc acceptTargetTableDesc(TableDesc td) { syncOutput.setCode(200); } + private static boolean rollback(DatabaseTransactionManager tm, Profiler prof, boolean isFinally) { + try { + tm.rollbackTransaction(); + prof.checkpoint("rollback-transaction"); + } catch (Exception oops) { + if (isFinally) { + log.error("BUG: rollback in finally: FAIL", oops); + } else { + log.error("PUT failed - rollback : FAIL", oops); + } + return false; + } + return true; + } + @Override protected InlineContentHandler getInlineContentHandler() { return new TablesInputHandler(INPUT_TAG); diff --git a/cadc-tap/build.gradle b/cadc-tap/build.gradle index 4c3ea2f5..3495c821 100644 --- a/cadc-tap/build.gradle +++ b/cadc-tap/build.gradle @@ -14,7 +14,7 @@ apply from: '../opencadc.gradle' sourceCompatibility = 11 group = 'org.opencadc' -version = '1.1.26' +version = '1.1.27' description = 'OpenCADC TAP-1.1 tap client library' def git_url = 'https://github.com/opencadc/tap' diff --git a/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/TapSchemaUtil.java b/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/TapSchemaUtil.java index ff283edb..cdee2d85 100644 --- a/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/TapSchemaUtil.java +++ b/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/TapSchemaUtil.java @@ -75,11 +75,15 @@ import ca.nrc.cadc.tap.schema.validator.IdentifierValidator; import ca.nrc.cadc.tap.schema.validator.ValidatorConfig; import ca.nrc.cadc.tap.schema.validator.Violation; +import ca.nrc.cadc.tap.schema.validator.ViolationType; import ca.nrc.cadc.tap.schema.validator.adql.ReservedKeyword; import ca.nrc.cadc.tap.schema.validator.ucd.UCDValidator; import ca.nrc.cadc.tap.schema.validator.unit.VOUnitValidator; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; + import org.apache.log4j.Logger; /** @@ -267,6 +271,7 @@ public static VOTableDocument createVOTable(TableDesc tableDesc, ValidatorConfig public static String validateTableDesc(TableDesc td, ValidatorConfig config) { List errors = new ArrayList<>(); List warnings = new ArrayList<>(); + Set names = new HashSet<>(); IdentifierValidator identifierValidator = new IdentifierValidator(config); String contextPrefix = td.getSchemaName() + ": "; @@ -287,6 +292,11 @@ public static String validateTableDesc(TableDesc td, ValidatorConfig config) { identifierValidator.checkValidIdentifier(cd.getColumnName(), IdentifierValidator.IdentifierType.COLUMN_NAME).getViolations(), config, errors, warnings); + if (!names.add(cd.getColumnName())) { + collectViolations(contextPrefix, "column name", cd.getColumnName(), + List.of(new Violation[]{new Violation(ViolationType.DUPLICATE_IDENTIFIER, "Duplicate column name: " + cd.getColumnName())}), + config, errors, warnings); + } // Ignore UCD and Unit validation for views and non-strict mode(e.g. none). if (td.tableType.equals(TableDesc.TableType.VIEW) || config.getConfigType().equals(ValidatorConfig.ConfigType.NONE)) { continue; diff --git a/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/validator/ValidatorConfig.java b/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/validator/ValidatorConfig.java index bb7f18b4..bf7c0bda 100644 --- a/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/validator/ValidatorConfig.java +++ b/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/validator/ValidatorConfig.java @@ -92,7 +92,8 @@ public enum ConfigType { ViolationType.NULL_OR_BLANK, ViolationType.STRUCTURAL, ViolationType.IDENTIFIER_INVALID_CHAR, - ViolationType.IDENTIFIER_RESERVED_KEYWORD // allowing quoted identifiers if config is not "strict" + ViolationType.IDENTIFIER_RESERVED_KEYWORD, // allowing quoted identifiers if config is not "strict" + ViolationType.DUPLICATE_IDENTIFIER )); private final Set errorTypes; diff --git a/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/validator/ViolationType.java b/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/validator/ViolationType.java index e114e3b6..312f7bd8 100644 --- a/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/validator/ViolationType.java +++ b/cadc-tap/src/main/java/ca/nrc/cadc/tap/schema/validator/ViolationType.java @@ -72,6 +72,7 @@ public enum ViolationType { // --- structural (always errors, cannot be downgraded) --- NULL_OR_BLANK, STRUCTURAL, + DUPLICATE_IDENTIFIER, // --- IDENTIFIER --- IDENTIFIER_INVALID_CHAR, diff --git a/youcat/src/intTest/java/org/opencadc/youcat/CreateTableTest.java b/youcat/src/intTest/java/org/opencadc/youcat/CreateTableTest.java index 8c88c5af..3115b4ea 100644 --- a/youcat/src/intTest/java/org/opencadc/youcat/CreateTableTest.java +++ b/youcat/src/intTest/java/org/opencadc/youcat/CreateTableTest.java @@ -467,7 +467,7 @@ public void write(OutputStream out) throws IOException { } }; HttpUpload put = new HttpUpload(src, tableURL); - put.setContentType(TablesInputHandler.VOTABLE_TYPE); + put.setRequestProperty(HttpConstants.HDR_CONTENT_TYPE, TablesInputHandler.VOTABLE_TYPE); Subject.doAs(schemaOwner, new RunnableAction(put)); Assert.assertNull("throwable", put.getThrowable()); Assert.assertEquals("response code", 200, put.getResponseCode()); @@ -618,4 +618,96 @@ public void write(OutputStream out) throws IOException { } } + // Test failures with meaningful messages + @Test + public void testCreateTableFailures() { + try { + clearSchemaPerms(); + TapPermissions tp = new TapPermissions(null, true, null, null); + super.setPerms(schemaOwner, testSchemaName, tp, 200); + + String testTable = testSchemaName + ".testCreateTableFailure"; + + // cleanup just in case + doDelete(schemaOwner, testTable, true); + + + VOTableTable vtab = new VOTableTable(); + // Test 1: faulty datatype - expected 400 Bad Request + vtab.getFields().add(new VOTableField("c0", TapDataType.STRING.getDatatype(), TapDataType.STRING.arraysize)); + vtab.getFields().add(new VOTableField("c1", TapDataType.SHORT.getDatatype())); + vtab.getFields().add(new VOTableField("c2", TapDataType.INTEGER.getDatatype())); + vtab.getFields().add(new VOTableField("c3", "unicodeChar")); + + VOTableResource vres = new VOTableResource("results"); + vres.setTable(vtab); + final VOTableDocument doc = new VOTableDocument(); + doc.getResources().add(vres); + + // create + URL tableURL = new URL(certTablesURL.toExternalForm() + "/" + testTable); + OutputStreamWrapper src = new OutputStreamWrapper() { + @Override + public void write(OutputStream out) throws IOException { + VOTableWriter w = new VOTableWriter(VOTableWriter.SerializationType.TABLEDATA); + w.write(doc, out); + } + }; + HttpUpload put = new HttpUpload(src, tableURL); + put.setRequestProperty(HttpConstants.HDR_CONTENT_TYPE, TablesInputHandler.VOTABLE_TYPE); + Subject.doAs(schemaOwner, new RunnableAction(put)); + Assert.assertEquals("response code", 400, put.getResponseCode()); + Assert.assertNotNull("throwable", put.getThrowable()); + + // TAP query check (Table is not created) + String adql = "SELECT * from " + testTable; + Map params = new TreeMap(); + params.put("LANG", "ADQL"); + params.put("QUERY", adql); + log.info("doQueryCheck: " + testTable + " " + anonQueryURL); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + HttpPost doit = new HttpPost(anonQueryURL, params, out); + doit.run(); + Assert.assertEquals("response code", 400, doit.getResponseCode()); + Assert.assertNotNull("throwable", doit.getThrowable()); + + // Test 2: duplicate names + vtab = new VOTableTable(); + vtab.getFields().add(new VOTableField("c0", TapDataType.STRING.getDatatype(), TapDataType.STRING.arraysize)); + vtab.getFields().add(new VOTableField("c1", TapDataType.SHORT.getDatatype())); + vtab.getFields().add(new VOTableField("c2", TapDataType.INTEGER.getDatatype())); + vtab.getFields().add(new VOTableField("c2", TapDataType.STRING.getDatatype(), TapDataType.STRING.arraysize)); + + vres = new VOTableResource("results"); + vres.setTable(vtab); + final VOTableDocument doc1 = new VOTableDocument(); + doc1.getResources().add(vres); + + // create + src = new OutputStreamWrapper() { + @Override + public void write(OutputStream out) throws IOException { + VOTableWriter w = new VOTableWriter(VOTableWriter.SerializationType.TABLEDATA); + w.write(doc1, out); + } + }; + put = new HttpUpload(src, tableURL); + put.setRequestProperty(HttpConstants.HDR_CONTENT_TYPE, TablesInputHandler.VOTABLE_TYPE); + Subject.doAs(schemaOwner, new RunnableAction(put)); + Assert.assertEquals("response code", 400, put.getResponseCode()); + Assert.assertNotNull("throwable", put.getThrowable()); + + // TAP query check (Table is not created) + out = new ByteArrayOutputStream(); + doit = new HttpPost(anonQueryURL, params, out); + doit.run(); + Assert.assertEquals("response code", 400, doit.getResponseCode()); + Assert.assertNotNull("throwable", doit.getThrowable()); + + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + } From 03b04f3d61e4a5d7f7df4eacbb573572511f1a3e Mon Sep 17 00:00:00 2001 From: aratikakadiya Date: Thu, 18 Jun 2026 13:55:30 -0700 Subject: [PATCH 2/4] Rework. --- .../java/ca/nrc/cadc/tap/db/TableCreator.java | 41 ++++++++---------- .../ca/nrc/cadc/vosi/actions/PutAction.java | 43 +++++++++---------- 2 files changed, 39 insertions(+), 45 deletions(-) diff --git a/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java b/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java index 98c0c209..69304caf 100644 --- a/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java +++ b/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java @@ -121,43 +121,40 @@ public void createTable(TableDesc table) { tm.commitTransaction(); prof.checkpoint("commit-transaction"); } catch (UnsupportedOperationException | IllegalArgumentException rethrow) { - log.error("create table failed - rollback", rethrow); - if (rollback(tm, prof, false)) { - log.error("create table failed - rollback: OK"); + try { + log.debug("create table failed - rollback", rethrow); + tm.rollbackTransaction(); + prof.checkpoint("rollback-transaction"); + log.debug("create table failed - rollback: OK"); + } catch (Exception oops) { + log.debug("create table failed - rollback : FAIL", oops); } throw rethrow; } catch (Exception ex) { - log.error("create table failed - rollback", ex); - if (rollback(tm, prof, false)) { - log.error("create table failed - rollback: OK"); + try { + log.debug("create table failed - rollback", ex); + tm.rollbackTransaction(); + prof.checkpoint("rollback-transaction"); + log.debug("create table failed - rollback: OK"); + } catch (Exception oops) { + log.error("create table failed - rollback : FAIL", oops); } throw new RuntimeException("failed to create table " + table.getTableName() + " with cause: " + ex.getMessage(), ex); } finally { if (tm.isOpen()) { log.error("BUG: open transaction in finally - trying to rollback"); - if (rollback(tm, prof, true)) { + try { + tm.rollbackTransaction(); + prof.checkpoint("rollback-transaction"); log.error("BUG: rollback in finally: OK"); + } catch (Exception oops) { + log.error("BUG: rollback in finally: FAIL", oops); } throw new RuntimeException("BUG: open transaction in finally"); } } } - private static boolean rollback(DatabaseTransactionManager tm, Profiler prof, boolean isFinally) { - try { - tm.rollbackTransaction(); - prof.checkpoint("rollback-transaction"); - } catch (Exception oops) { - if (isFinally) { - log.error("BUG: rollback in finally: FAIL", oops); - } else { - log.error("create table failed - rollback : FAIL", oops); - } - return false; - } - return true; - } - /** * Change a table name. This can also optionally change the schema name. * diff --git a/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/PutAction.java b/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/PutAction.java index da48ab39..f94bb0fd 100644 --- a/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/PutAction.java +++ b/cadc-tap-schema/src/main/java/ca/nrc/cadc/vosi/actions/PutAction.java @@ -311,22 +311,34 @@ public TableDesc acceptTargetTableDesc(TableDesc td) { tm.commitTransaction(); prof.checkpoint("commit-transaction"); } catch (UnsupportedOperationException | IllegalArgumentException | IOException rethrow) { - log.error("PUT failed - rollback", rethrow); - if (rollback(tm, prof, false)) { - log.error("PUT failed - rollback: OK"); + try { + log.debug("PUT failed - rollback", rethrow); + tm.rollbackTransaction(); + prof.checkpoint("rollback-transaction"); + log.debug("PUT failed - rollback: OK"); + } catch (Exception oops) { + log.error("PUT failed - rollback: FAIL", oops); } throw rethrow; } catch (Exception ex) { - log.error("PUT failed - rollback", ex); - if (rollback(tm, prof, false)) { - log.error("PUT failed - rollback: OK"); + try { + log.debug("PUT failed - rollback", ex); + tm.rollbackTransaction(); + prof.checkpoint("rollback-transaction"); + log.debug("PUT failed - rollback: OK"); + } catch (Exception oops) { + log.error("PUT failed - rollback: FAIL", oops); } throw new RuntimeException("failed to create/add " + tableName + " with cause: " + ex.getMessage(), ex); } finally { if (tm.isOpen()) { - log.error("BUG: open transaction in finally - trying to rollback"); - if (rollback(tm, prof, true)) { + try { + log.error("BUG: open transaction in finally - trying to rollback"); + tm.rollbackTransaction(); + prof.checkpoint("rollback-transaction"); log.error("BUG: rollback in finally: OK"); + } catch (Exception oops) { + log.error("BUG: rollback in finally: FAIL", oops); } throw new RuntimeException("BUG: open transaction in finally"); } @@ -334,21 +346,6 @@ public TableDesc acceptTargetTableDesc(TableDesc td) { syncOutput.setCode(200); } - private static boolean rollback(DatabaseTransactionManager tm, Profiler prof, boolean isFinally) { - try { - tm.rollbackTransaction(); - prof.checkpoint("rollback-transaction"); - } catch (Exception oops) { - if (isFinally) { - log.error("BUG: rollback in finally: FAIL", oops); - } else { - log.error("PUT failed - rollback : FAIL", oops); - } - return false; - } - return true; - } - @Override protected InlineContentHandler getInlineContentHandler() { return new TablesInputHandler(INPUT_TAG); From 63fe67e70d1225ada9e7ec6a41663ca35caea8b6 Mon Sep 17 00:00:00 2001 From: aratikakadiya Date: Thu, 18 Jun 2026 14:13:45 -0700 Subject: [PATCH 3/4] Rework. --- .../src/main/java/ca/nrc/cadc/tap/db/TableCreator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java b/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java index 69304caf..4c8f53d3 100644 --- a/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java +++ b/cadc-tap-schema/src/main/java/ca/nrc/cadc/tap/db/TableCreator.java @@ -127,7 +127,7 @@ public void createTable(TableDesc table) { prof.checkpoint("rollback-transaction"); log.debug("create table failed - rollback: OK"); } catch (Exception oops) { - log.debug("create table failed - rollback : FAIL", oops); + log.error("create table failed - rollback : FAIL", oops); } throw rethrow; } catch (Exception ex) { From 8aefdd5ea6c161036f52431718deca0f5dd44a46 Mon Sep 17 00:00:00 2001 From: aratikakadiya Date: Thu, 18 Jun 2026 14:20:19 -0700 Subject: [PATCH 4/4] Rework. --- cadc-tap/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cadc-tap/build.gradle b/cadc-tap/build.gradle index 3495c821..c70fdfd3 100644 --- a/cadc-tap/build.gradle +++ b/cadc-tap/build.gradle @@ -14,7 +14,7 @@ apply from: '../opencadc.gradle' sourceCompatibility = 11 group = 'org.opencadc' -version = '1.1.27' +version = '1.1.28' description = 'OpenCADC TAP-1.1 tap client library' def git_url = 'https://github.com/opencadc/tap'