/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.translator.couchbase; import static org.junit.Assert.*; import static org.teiid.translator.TypeFacility.RUNTIME_NAMES.STRING; import static org.teiid.translator.couchbase.CouchbaseProperties.DOCUMENTID; import static org.teiid.translator.couchbase.CouchbaseProperties.FALSE_VALUE; import static org.teiid.translator.couchbase.CouchbaseProperties.UNDERSCORE; import static org.teiid.translator.couchbase.CouchbaseProperties.WAVE; import static org.teiid.translator.couchbase.CouchbaseMetadataProcessor.IS_ARRAY_TABLE; import static org.teiid.translator.couchbase.CouchbaseMetadataProcessor.NAMED_TYPE_PAIR; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.resource.ResourceException; import org.junit.Ignore; import org.junit.Test; import org.teiid.core.util.UnitTestUtil; import org.teiid.language.SQLConstants.Tokens; import org.teiid.metadata.MetadataFactory; import org.teiid.metadata.Table; import org.teiid.query.metadata.DDLStringVisitor; import org.teiid.query.metadata.SystemMetadata; import org.teiid.translator.couchbase.CouchbaseMetadataProcessor.Dimension; import com.couchbase.client.java.document.json.JsonArray; import com.couchbase.client.java.document.json.JsonObject; import com.couchbase.client.java.document.json.JsonValue; @SuppressWarnings({"nls",}) public class TestCouchbaseMetadataProcessor { static final String KEYSPACE = "test"; static final String KEYSPACE_SOURCE = "`test`"; @Test public void testCustomerOrder() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); Table table = createTable(mf, KEYSPACE, KEYSPACE); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, formCustomer(), mf, table, KEYSPACE, false, new Dimension()); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, formOder(), mf, table, KEYSPACE, false, new Dimension()); helpTest("customerOrder.expected", mf); } @Test public void testCustomerOrderMultiple() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); Table table = createTable(mf, KEYSPACE, KEYSPACE); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, formCustomer(), mf, table, KEYSPACE, false, new Dimension()); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, formOder(), mf, table, KEYSPACE, false, new Dimension()); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, formCustomer(), mf, table, KEYSPACE, false, new Dimension()); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, formOder(), mf, table, KEYSPACE, false, new Dimension()); helpTest("customerOrder.expected", mf); } @Test public void testCustomerOrderWithTypedName() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); mp.setTypeNameList("`test`:`type`,`beer-sample`:`type`,` travel-sample`:`type`"); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); Table customer = createTable(mf, KEYSPACE, "Customer"); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, formCustomer(), mf, customer, customer.getName(), false, new Dimension()); Table order = createTable(mf, KEYSPACE, "Oder"); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, formOder(), mf, order, order.getName(), false, new Dimension()); helpTest("customerOrderTypedName.expected", mf); } @Test public void testCustomerWithDuplicatedTypedName() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); mp.setTypeNameList("`test`:`type`,`test2`:`type`"); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); String typedName = "Customer"; String keyspace1 = "test"; String keyspace2 = "test2"; Table table1 = createTable(mf, keyspace1, typedName); mp.scanRow("test", KEYSPACE_SOURCE, formCustomer(), mf, table1, table1.getName(), false, new Dimension()); Table table2 = createTable(mf, keyspace2, typedName); mp.scanRow("test2", "`test2`", formCustomer(), mf, table2, table2.getName(), false, new Dimension()); helpTest("customerDuplicatedTypedName.expected", mf); } @Test public void testNullValue() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); Table table = createTable(mf, KEYSPACE, KEYSPACE); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, formNullValueJson(), mf, table, KEYSPACE, false, new Dimension()); helpTest("nullValue.expected", mf); } @Test public void testDataType() throws ResourceException { /* 10 potential types: String, Integer, Long, Double, Boolean, BigInteger, BigDecimal, JsonObject, JsonArray, null */ CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); Table table = createTable(mf, KEYSPACE, KEYSPACE); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, formDataTypeJson(), mf, table, KEYSPACE, false, new Dimension()); helpTest("dataTypeJson.expected", mf); } @Test public void testNestedJson() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); Table table = createTable(mf, KEYSPACE, KEYSPACE); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, nestedJson(), mf, table, KEYSPACE, false, new Dimension()); helpTest("nestedJson.expected", mf); } @Test public void testNestedJsonWithTypedName() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); Table table = createTable(mf, KEYSPACE, "Sample"); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, nestedJson(), mf, table, "Sample", false, new Dimension()); helpTest("nestedJsonTypedName.expected", mf); } @Test public void testNestedArray() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); Table table = createTable(mf, KEYSPACE, KEYSPACE); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, nestedArray(), mf, table, KEYSPACE, false, new Dimension()); helpTest("nestedArray.expected", mf); } @Test public void testComplexJson() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); Table table = createTable(mf, KEYSPACE, KEYSPACE); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, complexJson(), mf, table, KEYSPACE, false, new Dimension()); helpTest("complexJson.expected", mf); } @Test public void testJsonNestedArray() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); Table table = createTable(mf, KEYSPACE, KEYSPACE); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, complexJsonNestedArray(), mf, table, KEYSPACE, false, new Dimension()); helpTest("complexJsonNestedArray.expected", mf); } @Ignore("not resolved so far") @Test public void testMetadataCaseSensitive() throws ResourceException { CouchbaseMetadataProcessor mp = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); JsonObject json = JsonObject.create() .put("name", "value") .put("Name", "value") .put("nAmE", "value"); Table table = createTable(mf, KEYSPACE, KEYSPACE); mp.scanRow(KEYSPACE, KEYSPACE_SOURCE, json, mf, table, KEYSPACE, false, new Dimension()); helpTest("TODO.expected", mf); } @Test public void testProcedures() throws ResourceException { CouchbaseMetadataProcessor metadataProcessor = new CouchbaseMetadataProcessor(); MetadataFactory mf = new MetadataFactory("vdb", 1, "couchbase", SystemMetadata.getInstance().getRuntimeTypeMap(), new Properties(), null); metadataProcessor.addProcedures(mf, null); helpTest("procedures.expected", mf); } static Table createTable(MetadataFactory mf, String keyspace, String tableName) { if (mf.getSchema().getTable(tableName) != null && !tableName.equals(keyspace)) { tableName = keyspace + UNDERSCORE + tableName; } Table table = mf.addTable(tableName); table.setNameInSource(WAVE + keyspace + WAVE); table.setSupportsUpdate(true); table.setProperty(IS_ARRAY_TABLE, FALSE_VALUE); if(!tableName.equals(keyspace)){ table.setProperty(NAMED_TYPE_PAIR, buildNamedTypePair("`type`", tableName)); } mf.addColumn(DOCUMENTID, STRING, table); mf.addPrimaryKey("PK0", Arrays.asList(DOCUMENTID), table); //$NON-NLS-1$ return table; } private static String buildNamedTypePair(String columnIdentifierName, String typedValue) { StringBuilder sb = new StringBuilder(); sb.append(columnIdentifierName).append(Tokens.COLON).append(Tokens.QUOTE).append(typedValue).append(Tokens.QUOTE); return sb.toString(); } static JsonObject formCustomer() { return JsonObject.create() .put("Name", "John Doe") .put("ID", "Customer_101") .put("type", "Customer") .put("SavedAddresses", JsonArray.from("123 Main St.", "456 1st Ave")); } static JsonObject formOder() { return JsonObject.create() .put("Name", "Air Ticket") .put("type", "Oder") .put("CustomerID", "Customer_101") .put("CreditCard", JsonObject.create().put("Type", "Visa").put("CardNumber", "4111 1111 1111 111").put("Expiry", "12/12").put("CVN", 123)) .put("Items", JsonArray.from(JsonObject.create().put("ItemID", 89123).put("Quantity", 1), JsonObject.create().put("ItemID", 92312).put("Quantity", 5))); } static JsonValue formNullValueJson() { return JsonObject.create() .put("Name", "null value test") .putNull("attr_null") .put("attr_obj", JsonObject.create().putNull("attr_null")) .put("attr_array", JsonArray.create().addNull()); } static JsonObject formDataTypeJson() { return JsonObject.create() .put("Name", "data type test") .put("attr_string", "This is String value") .put("attr_integer", Integer.MAX_VALUE) .put("attr_long", Long.MAX_VALUE) .put("attr_double", Double.MAX_VALUE) .put("attr_boolean", Boolean.TRUE) .put("attr_bigInteger", new BigInteger("fffffffffffff", 16)) .put("attr_bigDecimal", new BigDecimal("1115.37")) .put("attr_jsonObject", JsonObject.create().put("key", "value")) .put("attr_jsonArray", formDataTypeArray()) .putNull("attr_null"); } static JsonArray formDataTypeArray() { return JsonArray.create() .add("This is String value") .add(Integer.MAX_VALUE) .add(Long.MAX_VALUE) .add(Double.MAX_VALUE) .add(Boolean.TRUE) .add(new BigInteger("fffffffffffff", 16)) .add(new BigDecimal("1115.37")) .add(JsonObject.create().put("key", "value")) .add(JsonArray.create().add("array")) .addNull(); } static JsonObject nestedJson() { return JsonObject.create() .put("Name", "Nested Json") .put("nestedJson", JsonObject.create() .put("Dimension", 1) .put("nestedJson", JsonObject.create() .put("Dimension", 2) .put("nestedJson", JsonObject.create() .put("Dimension", 3) .put("nestedJson", "value")))); } static JsonObject nestedArray() { return JsonObject.create() .put("Name", "Nested Array") .put("nestedArray", JsonArray.create() .add("dimension 1") .add(JsonArray.create() .add("dimension 2") .add(JsonArray.create() .add("dimension 3") .add(JsonArray.create() .add("dimension 4"))))); } static JsonObject complexJson() { return JsonObject.create() .put("Name", "Complex Json") .put("attr_jsonObject", JsonObject.create() .put("Name", "Nested Json") .put("attr_jsonArray", JsonArray.create() .add("Nested array") .add(JsonObject.create().put("Name", "Nested Json")))) .put("attr_jsonArray", JsonArray.create() .add("Nested array") .add(JsonObject.create().put("Name", "Nested Json")) .add(JsonObject.create().put("Name", "Nested Json").put("Dimension", 1))); } static JsonValue complexJsonNestedArray() { return JsonObject.create() .put("Name", "Complex Json") .put("attr_jsonObject", JsonObject.create() .put("Name", "Nested Json") .put("attr_jsonArray", JsonArray.create() .add("Nested array") .add(JsonObject.create().put("Name", "Nested Json")))); } private static final boolean PRINT_TO_CONSOLE = false; private static final boolean REPLACE_EXPECTED = false; private void helpTest(String name, MetadataFactory mf) throws ResourceException { try { String metadataDDL = DDLStringVisitor.getDDLString(mf.getSchema(), null, null); if(PRINT_TO_CONSOLE) { System.out.println(metadataDDL); } Path path = Paths.get(UnitTestUtil.getTestDataPath(), name); if(REPLACE_EXPECTED) { Files.write(path, metadataDDL.getBytes("UTF-8"), StandardOpenOption.TRUNCATE_EXISTING); } String expected = new String(Files.readAllBytes(path)); assertEquals(expected, metadataDDL); } catch (IOException e) { throw new ResourceException(e); } } @Test public void testTypeListParse() { Map<String, String> typeNameMap = new HashMap<>(); String typeNameList = "`product`:`type`,`store`:`type`,`customer`:`jsonType`,`sales`:`type`"; Pattern typeNamePattern = Pattern.compile(CouchbaseProperties.TPYENAME_MATCHER_PATTERN); Matcher typeGroupMatch = typeNamePattern.matcher(typeNameList); while (typeGroupMatch.find()) { typeNameMap.put(typeGroupMatch.group(1), typeGroupMatch.group(2)); } assertTrue(typeNameMap.values().contains("`type`")); assertEquals("`type`", typeNameMap.get("`product`")); } }