From 817b5e0207b6ef5923eb9639add246b3bb87b942 Mon Sep 17 00:00:00 2001 From: Ramesh Reddy Date: Wed, 7 Oct 2015 13:10:25 -0500 Subject: [PATCH] TEIID-3370: adding the insert/update/delete support --- .../translator/odata4/BaseQueryExecution.java | 17 ++- .../odata4/ODataExecutionFactory.java | 24 +++- .../translator/odata4/ODataFilterVisitor.java | 15 +-- .../odata4/ODataMetadataProcessor.java | 23 +++- .../teiid/translator/odata4/ODataPlugin.java | 1 + .../translator/odata4/ODataSQLVisitor.java | 33 ++++-- .../odata4/ODataUpdateExecution.java | 109 ++++++++++-------- .../translator/odata4/ODataUpdateQuery.java | 92 +++++++++------ .../translator/odata4/ODataUpdateVisitor.java | 81 ++++++++++--- .../teiid/translator/odata/i18n.properties | 5 +- .../odata4/TestODataMetadataProcessor.java | 6 +- .../odata4/TestODataQueryExecution.java | 24 +++- .../translator/odata4/TestODataSQLVistor.java | 2 +- .../odata4/TestODataUpdateExecution.java | 13 ++- 14 files changed, 302 insertions(+), 143 deletions(-) diff --git a/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/BaseQueryExecution.java b/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/BaseQueryExecution.java index 0650acb692..a8c52f0bd2 100644 --- a/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/BaseQueryExecution.java +++ b/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/BaseQueryExecution.java @@ -118,11 +118,16 @@ protected TranslatorException buildError(BinaryWSProcedureExecution execution) { // do some error handling try { Blob blob = (Blob)execution.getOutputParameterValues().get(0); - JsonDeserializer parser = new JsonDeserializer(false); - ODataError error = parser.toError(blob.getBinaryStream()); - return new TranslatorException(ODataPlugin.Util.gs( - ODataPlugin.Event.TEIID17013, execution.getResponseCode(), - error.getCode(), error.getMessage(), error.getInnerError())); + if (blob != null) { + JsonDeserializer parser = new JsonDeserializer(false); + ODataError error = parser.toError(blob.getBinaryStream()); + return new TranslatorException(ODataPlugin.Util.gs( + ODataPlugin.Event.TEIID17013, execution.getResponseCode(), + error.getCode(), error.getMessage(), error.getInnerError())); + } else { + return new TranslatorException(ODataPlugin.Util.gs( + ODataPlugin.Event.TEIID17031)); + } } catch (Throwable t) { return new TranslatorException(t); @@ -168,7 +173,7 @@ protected BinaryWSProcedureExecution invokeHTTP(String method, protected Map> getDefaultHeaders() { Map> headers = new HashMap>(); - headers.put("Accept", Arrays.asList(ContentType.JSON_NO_METADATA.toContentTypeString())); //$NON-NLS-1$ + headers.put("Accept", Arrays.asList(ContentType.JSON.toContentTypeString())); //$NON-NLS-1$ headers.put("Content-Type", Arrays.asList( ContentType.APPLICATION_JSON.toContentTypeString())); //$NON-NLS-1$ //$NON-NLS-2$ if (this.executionContext != null) { diff --git a/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataExecutionFactory.java b/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataExecutionFactory.java index 9a8a545d55..a067347c1b 100644 --- a/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataExecutionFactory.java +++ b/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataExecutionFactory.java @@ -79,6 +79,7 @@ public class ODataExecutionFactory extends ExecutionFactory row = execution.next(); - if (row == null) { - break; + while (uri != null) { + BinaryWSProcedureExecution execution = invokeHTTP("GET",uri,null, getDefaultHeaders()); + if (execution.getResponseCode() == HttpStatusCode.OK.getStatusCode()) { + EntityCollection entities = null; + Blob blob = (Blob)execution.getOutputParameterValues().get(0); + try { + InputStream response = blob.getBinaryStream(); + if (response != null){ + JsonDeserializer serializer = new JsonDeserializer(false); + entities = serializer.toEntitySet(response).getPayload(); + URI nextUri = entities.getNext(); + if (nextUri != null) { + uri = nextUri.toString(); + } else { + uri = null; + } + } + } catch (ODataDeserializerException e) { + throw new TranslatorException(e); + } catch (SQLException e) { + throw new TranslatorException(e); } - String updateUri = odataQuery.buildUpdateURL("", row); - executeQuery(odataQuery.getMethod(), updateUri, - odataQuery.getPayload(), null, - new HttpStatusCode[] { HttpStatusCode.NO_CONTENT }); - this.updateCount.incrementAndGet(); - } - } else { - throw buildError(execution); - } - } - - private void handleDelete(ODataUpdateQuery odataQuery) throws TranslatorException { - String uri = odataQuery.buildUpdateSelectionURL(""); - BinaryWSProcedureExecution execution = invokeHTTP("GET",uri,null,getDefaultHeaders()); - if (execution.getResponseCode() == HttpStatusCode.OK.getStatusCode()) { - // TODO: This loop needs to be executed in batch mode, to be transactionally safe - while (true) { - List row = execution.next(); - if (row == null) { - break; + + if (entities != null && !entities.getEntities().isEmpty()) { + for (Entity entity:entities.getEntities()) { + Link editLink = entity.getEditLink(); + Map> headers = getDefaultUpdateHeaders(); + if (entity.getETag() != null) { + headers.put("If-Match", Arrays.asList(entity.getETag())); //$NON-NLS-1$ + } + BinaryWSProcedureExecution update = invokeHTTP( + method, + editLink.getHref(), + method.equals("DELETE")?null:odataQuery.getPayload(entity), + headers); + if (HttpStatusCode.NO_CONTENT.getStatusCode() == update.getResponseCode()) { + this.updateCount.incrementAndGet(); + } else { + throw buildError(update); + } + } } - String updateUri = odataQuery.buildUpdateURL("", row); - executeQuery("DELETE", updateUri, - odataQuery.getPayload(), null, - new HttpStatusCode[] { HttpStatusCode.NO_CONTENT }); - this.updateCount.incrementAndGet(); + } else { + throw buildError(execution); } - } else { - throw buildError(execution); } - } + } + private Map> getDefaultUpdateHeaders() { + Map> headers = new HashMap>(); + headers.put("Accept", Arrays.asList(ContentType.APPLICATION_JSON.toContentTypeString())); //$NON-NLS-1$ + headers.put("Content-Type", Arrays.asList(ContentType.APPLICATION_JSON.toContentTypeString())); //$NON-NLS-1$ //$NON-NLS-2$ + return headers; + } + private void handleInsert(ODataUpdateQuery odataQuery) throws TranslatorException { try { - Map> headers = new HashMap>(); - headers.put("Accept", Arrays.asList( //$NON-NLS-1$ - AcceptType.fromContentType(ContentType.JSON).toString())); - headers.put("Content-Type", Arrays.asList( //$NON-NLS-1$ - ContentType.APPLICATION_JSON.toContentTypeString())); + Map> headers = getDefaultUpdateHeaders(); headers.put("Prefer", Arrays.asList("return=representation")); //$NON-NLS-1$ String uri = odataQuery.buildInsertURL(""); InputStream response = null; - BinaryWSProcedureExecution execution = invokeHTTP(odataQuery.getMethod(), + BinaryWSProcedureExecution execution = invokeHTTP(odataQuery.getInsertMethod(), uri, - odataQuery.getPayload(), + odataQuery.getPayload(null), headers); // 201 - the created entity returned diff --git a/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataUpdateQuery.java b/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataUpdateQuery.java index f4b1f643e1..199af5309a 100644 --- a/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataUpdateQuery.java +++ b/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataUpdateQuery.java @@ -24,6 +24,7 @@ import java.io.StringWriter; import java.net.URI; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -49,7 +50,6 @@ public class ODataUpdateQuery extends ODataQuery { private Map keys = new LinkedHashMap(); private Map expandKeys = new LinkedHashMap(); private List payloadProperties = new ArrayList(); - private String method = "POST"; //$NON-NLS-1$ private Condition condition; public ODataUpdateQuery(ODataExecutionFactory executionFactory, RuntimeMetadata metadata) { @@ -65,33 +65,36 @@ public String buildInsertURL(String serviceRoot) throws TranslatorException { uriBuilder.appendKeySegment(this.keys); } - this.method = "POST"; if (!this.complexTables.isEmpty()) { - if (this.complexTables.get(0).isCollection()) { - this.method = "PUT"; - } else { - this.method = "PATCH"; - } uriBuilder.appendPropertySegment(this.complexTables.get(0).getName()); } if (!this.expandTables.isEmpty()) { - if (!this.expandTables.get(0).isCollection()) { - this.method = "PUT"; - } uriBuilder.appendPropertySegment(this.expandTables.get(0).getName()); } -// -// if (this.expandKeys.size() == 1) { -// uriBuilder.appendKeySegment(this.expandKeys.values().iterator().next()); -// } else if (!this.expandKeys.isEmpty()){ -// uriBuilder.appendKeySegment(this.expandKeys); -// } URI uri = uriBuilder.build(); return uri.toString(); } - public String getPayload() throws TranslatorException { + public String getInsertMethod() { + String method = "POST"; + if (!this.complexTables.isEmpty()) { + if (this.complexTables.get(0).isCollection()) { + method = "PUT"; + } else { + method = "PATCH"; + } + } + if (!this.expandTables.isEmpty()) { + if (!this.expandTables.get(0).isCollection()) { + method = "PUT"; + } + } + return method; + } + + + public String getPayload(Entity parentEntity) throws TranslatorException { JsonSerializer serializer = new JsonSerializer(false, ContentType.APPLICATION_JSON); StringWriter writer = new StringWriter(); @@ -106,7 +109,11 @@ public String getPayload() throws TranslatorException { for (Property p:this.payloadProperties) { value.getValue().add(p); } - complexProperty.setValue(ValueType.COMPLEX, value); + if (this.complexTables.get(0).isCollection()) { + complexProperty.setValue(ValueType.COLLECTION_COMPLEX, Arrays.asList(value)); + } else { + complexProperty.setValue(ValueType.COMPLEX, value); + } serializer.write(writer, complexProperty); } else if (!this.expandTables.isEmpty()) { Table table = this.expandTables.get(0).getTable(); @@ -124,6 +131,14 @@ public String getPayload() throws TranslatorException { for (Property p:this.payloadProperties) { entity.addProperty(p); } + // for updates + if (parentEntity != null) { + // add all the key properties. + List keys = this.rootDocument.getTable().getPrimaryKey().getColumns(); + for (Column key: keys) { + entity.addProperty(parentEntity.getProperty(key.getName())); + } + } serializer.write(writer, entity); } } catch (ODataSerializerException e) { @@ -177,21 +192,18 @@ private String getName(Table table) { public void addInsertProperty(Column column, String type, Object value) { buildKeyColumns(getRootDocument().getTable(), (Table)column.getParent(), column.getName(), value); - if (!isPseudo(column)) { - this.payloadProperties.add(new Property(type, column.getName(), ValueType.PRIMITIVE, value)); - } + addUpdateProperty(column, type, value); } - public void addUpdateProperty(Table parentTable, Column column, String type, Object value) { + public void addUpdateProperty(Column column, String type, Object value) { + boolean collection = (value instanceof List); if (!isPseudo(column)) { - this.payloadProperties.add(new Property(type, column.getName(), ValueType.PRIMITIVE, value)); + this.payloadProperties.add(new Property(type, column.getName(), + collection ? ValueType.COLLECTION_PRIMITIVE + : ValueType.PRIMITIVE, value)); } } - public String getMethod() { - return this.method; - } - public void setCondition(Condition where) { this.condition = where; } @@ -220,8 +232,24 @@ public String buildUpdateSelectionURL(String serviceRoot) throws TranslatorExcep return uri.toString(); } + public String getUpdateMethod() { + String method = "PATCH"; + if (!this.complexTables.isEmpty()) { + if (this.complexTables.get(0).isCollection()) { + method = "PUT"; + } else { + method = "PATCH"; + } + } + if (!this.expandTables.isEmpty()) { + if (!this.expandTables.get(0).isCollection()) { + method = "PUT"; + } + } + return method; + } + public String buildUpdateURL(String serviceRoot, List row) { - this.method = "PATCH"; URIBuilderImpl uriBuilder = new URIBuilderImpl(new ConfigurationImpl(), serviceRoot); uriBuilder.appendEntitySetSegment(this.rootDocument.getName()); @@ -237,17 +265,9 @@ public String buildUpdateURL(String serviceRoot, List row) { } if (!this.complexTables.isEmpty()) { - if (this.complexTables.get(0).isCollection()) { - this.method = "PUT"; - } else { - this.method = "PATCH"; - } uriBuilder.appendPropertySegment(this.complexTables.get(0).getName()); } if (!this.expandTables.isEmpty()) { - if (!this.expandTables.get(0).isCollection()) { - this.method = "PUT"; - } uriBuilder.appendPropertySegment(this.expandTables.get(0).getName()); // add keys if present diff --git a/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataUpdateVisitor.java b/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataUpdateVisitor.java index 1b009122ff..e043cb5e40 100644 --- a/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataUpdateVisitor.java +++ b/connectors/translator-odata4/src/main/java/org/teiid/translator/odata4/ODataUpdateVisitor.java @@ -61,17 +61,55 @@ public void visit(Insert obj) { this.operationType = OperationType.INSERT; visitNode(obj.getTable()); - // read the properties - int elementCount = obj.getColumns().size(); - for (int i = 0; i < elementCount; i++) { - Column column = obj.getColumns().get(i).getMetadataObject(); - List values = ((ExpressionValueSource)obj.getValueSource()).getValues(); - String type = ODataTypeManager.odataType(column.getRuntimeType()) - .getFullQualifiedName().getFullQualifiedNameAsString(); - Object value = ((Literal)values.get(i)).getValue(); - this.odataQuery.addInsertProperty(column, type, value); + try { + // read the properties + int elementCount = obj.getColumns().size(); + for (int i = 0; i < elementCount; i++) { + Column column = obj.getColumns().get(i).getMetadataObject(); + List values = ((ExpressionValueSource)obj.getValueSource()).getValues(); + getNativeType(column); + Expression expr = values.get(i); + Object value = resolveExpressionValue(expr); + this.odataQuery.addInsertProperty(column, getNativeType(column), value); + } + } catch (TranslatorException e) { + this.exceptions.add(e); + } + } + + private String getNativeType(Column column) { + String nativeType = column.getNativeType(); + if (nativeType == null) { + nativeType = "Edm.String"; + } + return nativeType; + } + + private Object resolveExpressionValue(Expression expr) throws TranslatorException { + Object value = null; + if (expr instanceof Literal) { + value = ((Literal)expr).getValue(); } - } + else if (expr instanceof org.teiid.language.Array) { + org.teiid.language.Array contents = (org.teiid.language.Array)expr; + List arrayExprs = contents.getExpressions(); + List values = new ArrayList(); + for (Expression exp:arrayExprs) { + if (exp instanceof Literal) { + values.add(((Literal)exp).getValue()); + } + else { + this.exceptions.add(new TranslatorException(ODataPlugin.Util.gs(ODataPlugin.Event.TEIID17029))); + } + } + value = values; + } + else { + this.exceptions.add(new TranslatorException(ODataPlugin.Util.gs(ODataPlugin.Event.TEIID17029))); + } + return value; + } + @Override public void visit(Update obj) { @@ -79,17 +117,22 @@ public void visit(Update obj) { visitNode(obj.getTable()); this.odataQuery.setCondition(obj.getWhere()); - // read the properties - int elementCount = obj.getChanges().size(); - for (int i = 0; i < elementCount; i++) { - Column column = obj.getChanges().get(i).getSymbol().getMetadataObject(); - Literal value = (Literal)obj.getChanges().get(i).getValue(); - String type = ODataTypeManager.odataType(column.getRuntimeType()) - .getFullQualifiedName().getFullQualifiedNameAsString(); - this.odataQuery.addInsertProperty(column, type, value); + try { + // read the properties + int elementCount = obj.getChanges().size(); + for (int i = 0; i < elementCount; i++) { + Column column = obj.getChanges().get(i).getSymbol().getMetadataObject(); + String type = ODataTypeManager.odataType(column.getRuntimeType()) + .getFullQualifiedName().getFullQualifiedNameAsString(); + Expression expr = obj.getChanges().get(i).getValue(); + Object value = resolveExpressionValue(expr); + this.odataQuery.addUpdateProperty(column, type, value); + } + } catch (TranslatorException e) { + this.exceptions.add(e); } } - + @Override public void visit(Delete obj) { this.operationType = OperationType.DELETE; diff --git a/connectors/translator-odata4/src/main/resources/org/teiid/translator/odata/i18n.properties b/connectors/translator-odata4/src/main/resources/org/teiid/translator/odata/i18n.properties index 3c3f2cd241..b6a386fe08 100644 --- a/connectors/translator-odata4/src/main/resources/org/teiid/translator/odata/i18n.properties +++ b/connectors/translator-odata4/src/main/resources/org/teiid/translator/odata/i18n.properties @@ -49,5 +49,6 @@ TEIID17025=Primary Key not found for table {0} TEIID17026=The criteria is not supported by OData query; OR based conditions need to be on single Entityset, can not span to $expand TEIID17027={0} table is not supported to participate in LEFT JOIN, re-write the query. TEIID17028={0} does not have MERGE property. Complex and Navigation tables must define the MERGE property that defines the parent document. -TEIID17029= -TEIID17030= +TEIID17029=Unknown data type in {0} +TEIID17030=OData translator does not support "update" queries; Add the override translator property if source supports it. +TEIID17031=Empty response received from the source diff --git a/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataMetadataProcessor.java b/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataMetadataProcessor.java index 2e146269be..7e48b31ad3 100644 --- a/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataMetadataProcessor.java +++ b/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataMetadataProcessor.java @@ -123,8 +123,8 @@ protected XMLMetadata getSchema(WSConnection conn) throws TranslatorException { props.setProperty("schemaNamespace", "Microsoft.OData.SampleService.Models.TripPin"); MetadataFactory mf = new MetadataFactory("vdb", 1, "trippin", SystemMetadata.getInstance().getRuntimeTypeMap(), props, null); processor.process(mf, null); - //String ddl = DDLStringVisitor.getDDLString(mf.getSchema(), null, null); - //System.out.println(ddl); +// String ddl = DDLStringVisitor.getDDLString(mf.getSchema(), null, null); +// System.out.println(ddl); return mf; } @@ -293,7 +293,7 @@ public void testEnititySetWithComplexType() throws Exception { assertNotNull(addressTable.getColumnByName("ssn")); assertNotNull(addressTable.getColumnByName("ssn").getProperty(ODataMetadataProcessor.PSEUDO, false)); - assertFalse(addressTable.getColumnByName("ssn").isSelectable()); + assertTrue(addressTable.getColumnByName("ssn").isSelectable()); assertEquals(1, addressTable.getForeignKeys().size()); assertEquals("Persons", addressTable.getForeignKeys().get(0).getReferenceTableName()); } diff --git a/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataQueryExecution.java b/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataQueryExecution.java index 8a978cee66..934441a047 100644 --- a/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataQueryExecution.java +++ b/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataQueryExecution.java @@ -600,5 +600,27 @@ public void testActionReturnsComplexCollection() throws Exception { assertArrayEquals(new Object[] {"China", "Newyork", "NY"}, excution.next().toArray(new Object[3])); assertNull(excution.next()); - } + } + + @Test + public void testReadComplexType() throws Exception { + String query = "select pa.People_UserName, pa.Address from People_AddressInfo pa "; + String expectedURL = "People?$select=UserName,AddressInfo"; + + FileReader reader = new FileReader(UnitTestUtil.getTestDataFile("people.json")); + ResultSetExecution excution = helpExecute(TestODataMetadataProcessor.tripPinMetadata(), + query, ObjectConverterUtil.convertToString(reader), expectedURL); + reader.close(); + + assertArrayEquals(new Object[] {"russellwhyte", "187 Suffolk Ln."}, + excution.next().toArray(new Object[2])); + assertArrayEquals(new Object[] {"scottketchum", "2817 Milton Dr."}, + excution.next().toArray(new Object[2])); + assertArrayEquals(new Object[] {"javieralfred", "89 Jefferson Way Suite 2"}, + excution.next().toArray(new Object[2])); + assertArrayEquals(new Object[] {"vincentcalabrese", "55 Grizzly Peak Rd."}, + excution.next().toArray(new Object[2])); + assertNull(excution.next()); + + } } diff --git a/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataSQLVistor.java b/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataSQLVistor.java index d61f7a3eb9..81c09e59cc 100644 --- a/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataSQLVistor.java +++ b/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataSQLVistor.java @@ -236,7 +236,7 @@ public void testSelectFromNavigationTable2() throws Exception { @Test public void testSelectFromComplexTable() throws Exception { helpExecute("SELECT * FROM People_AddressInfo where Address = 'foo'", - "People?$select=AddressInfo&$filter=AddressInfo/Address eq 'foo'"); + "People?$select=UserName,AddressInfo&$filter=AddressInfo/Address eq 'foo'"); } } diff --git a/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataUpdateExecution.java b/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataUpdateExecution.java index 0ab614a0ec..9af06922de 100644 --- a/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataUpdateExecution.java +++ b/connectors/translator-odata4/src/test/java/org/teiid/translator/odata4/TestODataUpdateExecution.java @@ -137,7 +137,8 @@ public String getContentType() { @Test public void testInsertEntitySet() throws Exception { - String query = "INSERT INTO People(UserName,FirstName,LastName) values ('jdoe', 'John', 'Doe')"; + String query = "INSERT INTO People(UserName,FirstName,LastName, EMails, Gender, Concurrency) " + + "values ('jdoe', 'John', 'Doe', ('jdoe@cantfind.ws',), 'Male', 1234)"; String expectedURL = "People"; String returnResponse = "{\n" + " \"UserName\":\"russellwhyte\",\n" + @@ -147,7 +148,11 @@ public void testInsertEntitySet() throws Exception { String expectedPayload = "{\"@odata.type\":\"#Microsoft.OData.SampleService.Models.TripPin.Person\"," + "\"UserName@odata.type\":\"String\",\"UserName\":\"jdoe\"," + "\"FirstName@odata.type\":\"String\",\"FirstName\":\"John\"," - + "\"LastName@odata.type\":\"String\",\"LastName\":\"Doe\"}"; + + "\"LastName@odata.type\":\"String\",\"LastName\":\"Doe\"," + + "\"Emails@odata.type\":\"#Collection(String)\",\"Emails\":[\"jdoe@cantfind.ws\"]," + + "\"Gender@odata.type\":\"#Microsoft.OData.SampleService.Models.TripPin.PersonGender\"," + + "\"Gender\":\"Male\"," + + "\"Concurrency@odata.type\":\"Int64\",\"Concurrency\":1234}"; UpdateExecution excution = helpExecute(TestODataMetadataProcessor.tripPinMetadata(), query, expectedPayload, returnResponse, expectedURL, "POST", 201); @@ -187,7 +192,9 @@ public void testInsertComplexTypeTripPin() throws Exception { " \"LastName\":\"Whyte\"\n" + "}"; String expectedPayload = "{\"@odata.type\":\"#Microsoft.OData.SampleService.Models.TripPin.Location\"," - + "\"Address@odata.type\":\"String\",\"Address\":\"sesame street\"}"; + + "\"value\":[{\"@odata.type\":\"#Microsoft.OData.SampleService.Models.TripPin.Location\"," + + "\"Address@odata.type\":\"String\"," + + "\"Address\":\"sesame street\"}]}"; //collection needs PUT UpdateExecution excution = helpExecute(TestODataMetadataProcessor.tripPinMetadata(),