OLINGO-175 provided update (PUT) and X-HTTP-METHOD header management

This commit is contained in:
fmartelli 2014-03-07 18:06:38 +01:00
parent e624815544
commit 1e4d761b03
11 changed files with 233 additions and 49 deletions

View File

@ -44,6 +44,10 @@ public class EntityUpdateTestITCase extends AbstractTestITCase {
protected String getServiceRoot() {
return testDefaultServiceRootURL;
}
protected String getStaticServiceRoot() {
return testStaticServiceRootURL;
}
@Test
public void mergeAsAtom() {
@ -92,7 +96,7 @@ public class EntityUpdateTestITCase extends AbstractTestITCase {
@Test
public void replaceAsAtom() {
final ODataPubFormat format = ODataPubFormat.ATOM;
final ODataEntity changes = read(format, client.getURIBuilder(getServiceRoot()).
final ODataEntity changes = read(format, client.getURIBuilder(getStaticServiceRoot()).
appendEntityTypeSegment("Car").appendKeySegment(14).build());
updateEntityDescription(format, changes, UpdateType.REPLACE);
}
@ -100,7 +104,7 @@ public class EntityUpdateTestITCase extends AbstractTestITCase {
@Test
public void replaceAsJSON() {
final ODataPubFormat format = ODataPubFormat.JSON_FULL_METADATA;
final ODataEntity changes = read(format, client.getURIBuilder(getServiceRoot()).
final ODataEntity changes = read(format, client.getURIBuilder(getStaticServiceRoot()).
appendEntityTypeSegment("Car").appendKeySegment(14).build());
updateEntityDescription(format, changes, UpdateType.REPLACE);
}

View File

@ -26,6 +26,7 @@ import com.msopentech.odatajclient.testservice.utils.ODataVersion;
import com.msopentech.odatajclient.testservice.utils.FSManager;
import static com.msopentech.odatajclient.testservice.utils.Constants.*;
import java.io.File;
import java.io.InputStream;
import java.util.Collections;
@ -38,6 +39,7 @@ import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@ -122,14 +124,42 @@ public abstract class AbstractServices {
}
}
/**
* Sample failing entity POST.
*
* @param accept Accept header.
* @param format format query option.
* @param entitySetName entity set name.
* @return fault response.
*/
@PUT
@Path("/{entitySetName}({entityId})")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
@Consumes({ MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
public Response putNewEntity(
@HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
@HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
@PathParam("entitySetName") String entitySetName,
@PathParam("entityId") String entityId,
final String entity) {
try {
final Accept acceptType = Accept.parse(accept, getVersion());
if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
throw new UnsupportedMediaTypeException("Unsupported media type");
}
final InputStream res;
if (acceptType == Accept.ATOM) {
res = atom.saveSingleEntity(entityId, entitySetName, IOUtils.toInputStream(entity));
} else {
res = json.saveSingleEntity(entityId, entitySetName, IOUtils.toInputStream(entity));
}
res.close();
final Response response = atom.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
response.getHeaders().put("Preference-Applied", Collections.<Object>singletonList(prefer));
return response;
} catch (Exception e) {
e.printStackTrace();
return atom.createFaultResponse(accept, e);
}
}
@POST
@Path("/{entitySetName}")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
@ -150,11 +180,9 @@ public abstract class AbstractServices {
final InputStream res;
if (acceptType == Accept.ATOM) {
res = atom.createEntity(
null, null, ENTITY, IOUtils.toInputStream(entity), entitySetName, acceptType);
res = atom.createEntity(entitySetName, IOUtils.toInputStream(entity));
} else {
res = json.createEntity(
null, null, ENTITY, IOUtils.toInputStream(entity), entitySetName, acceptType);
res = json.createEntity(entitySetName, IOUtils.toInputStream(entity));
}
if (prefer.equalsIgnoreCase("return-no-content")) {

View File

@ -19,9 +19,12 @@
package com.msopentech.odatajclient.testservice;
import com.msopentech.odatajclient.testservice.utils.ODataVersion;
import com.msopentech.odatajclient.testservice.utils.XHTTPMethodInterceptor;
import javax.ws.rs.Path;
import org.apache.cxf.interceptor.InInterceptors;
@Path("/V3/Static.svc")
@InInterceptors(classes = XHTTPMethodInterceptor.class)
public class V3Services extends AbstractServices {
public V3Services() throws Exception {

View File

@ -19,9 +19,12 @@
package com.msopentech.odatajclient.testservice;
import com.msopentech.odatajclient.testservice.utils.ODataVersion;
import com.msopentech.odatajclient.testservice.utils.XHTTPMethodInterceptor;
import javax.ws.rs.Path;
import org.apache.cxf.interceptor.InInterceptors;
@Path("/V4/Static.svc")
@InInterceptors(classes = XHTTPMethodInterceptor.class)
public class V4Services extends AbstractServices {
public V4Services() throws Exception {

View File

@ -146,13 +146,51 @@ public abstract class AbstractUtilities {
final String entitySetName, final String entityKey, final InputStream is, final NavigationLinks links)
throws Exception;
public InputStream saveSingleEntity(
final String key,
final String entitySetName,
final InputStream is) throws Exception {
return saveSingleEntity(key, entitySetName, is, null);
}
public InputStream saveSingleEntity(
final String key,
final String entitySetName,
final InputStream is,
final NavigationLinks links) throws Exception {
// -----------------------------------------
// 0. Get the path
// -----------------------------------------
final String path =
entitySetName + File.separatorChar + Commons.getEntityKey(key) + File.separatorChar + ENTITY;
// -----------------------------------------
// -----------------------------------------
// 1. Normalize navigation info; edit link; ...
// -----------------------------------------
final InputStream normalized = normalizeLinks(entitySetName, key, is, links);
// -----------------------------------------
// -----------------------------------------
// 2. save the entity
// -----------------------------------------
final FileObject fo = fsManager.putInMemory(normalized, fsManager.getAbsolutePath(path, getDefaultFormat()));
// -----------------------------------------
return fo.getContent().getInputStream();
}
public InputStream createEntity(
final String entitySetName, final InputStream is) throws Exception {
return createEntity(null, entitySetName, is);
}
public InputStream createEntity(
final String key,
final String basePath,
final String relativePath,
final InputStream is,
final String entitySetName,
final Accept accept) throws Exception {
final InputStream is) throws Exception {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
IOUtils.copy(is, bos);
@ -162,9 +200,9 @@ public abstract class AbstractUtilities {
// 0. Get default entry key and path (N.B. operation will consume/close the stream; use a copy instead)
// -----------------------------------------
final String entityKey = key == null ? getDefaultEntryKey(
entitySetName, new ByteArrayInputStream(bos.toByteArray()), accept) : key;
final String path = StringUtils.isBlank(basePath)
? entitySetName + File.separatorChar + Commons.getEntityKey(entityKey) + File.separatorChar : basePath;
entitySetName, new ByteArrayInputStream(bos.toByteArray()), getDefaultFormat()) : key;
final String path = entitySetName + File.separatorChar + Commons.getEntityKey(entityKey) + File.separatorChar;
// -----------------------------------------
// -----------------------------------------
@ -175,21 +213,21 @@ public abstract class AbstractUtilities {
// -----------------------------------------
// -----------------------------------------
// 2. Retrieve navigation info
// 2. Normalize navigation info; edit link; ... and save entity ....
// -----------------------------------------
final InputStream normalized =
normalizeLinks(entitySetName, entityKey, new ByteArrayInputStream(bos.toByteArray()), links);
final InputStream createdEntity =
saveSingleEntity(entityKey, entitySetName, new ByteArrayInputStream(bos.toByteArray()), links);
// -----------------------------------------
bos.reset();
IOUtils.copy(normalized, bos);
IOUtils.copy(createdEntity, bos);
// -----------------------------------------
// 2. save the entity
// 3. save the entity
// -----------------------------------------
final FileObject fo = fsManager.putInMemory(
new ByteArrayInputStream(bos.toByteArray()),
fsManager.getAbsolutePath(path + relativePath, accept));
fsManager.getAbsolutePath(path + ENTITY, getDefaultFormat()));
IOUtils.closeQuietly(bos);
// -----------------------------------------
@ -211,15 +249,12 @@ public abstract class AbstractUtilities {
IOUtils.closeQuietly(stream);
final String inlineEntryKey = getDefaultEntryKey(
inlineEntitySetName, new ByteArrayInputStream(inlineBos.toByteArray()), accept);
inlineEntitySetName, new ByteArrayInputStream(inlineBos.toByteArray()), getDefaultFormat());
createEntity(
inlineEntryKey,
null,
ENTITY,
new ByteArrayInputStream(inlineBos.toByteArray()),
inlineEntitySetName,
accept);
new ByteArrayInputStream(inlineBos.toByteArray()));
hrefs.add(inlineEntitySetName + "(" + inlineEntryKey + ")");
}
@ -492,4 +527,6 @@ public abstract class AbstractUtilities {
final InputStream toBeChanged, final String linkName, final InputStream replacement) throws Exception;
public abstract InputStream selectEntity(final InputStream entity, final String[] propertyNames) throws Exception;
protected abstract Accept getDefaultFormat();
}

View File

@ -84,4 +84,6 @@ public class Constants {
public final static String JSON_EDITLINK_NAME = "odata.editLink";
public final static String XHTTP_HEADER_NAME = "X-HTTP-METHOD";
}

View File

@ -45,6 +45,11 @@ public class JSONUtilities extends AbstractUtilities {
super(version);
}
@Override
protected Accept getDefaultFormat() {
return Accept.JSON_FULLMETA;
}
/**
* {@inheritDoc }
*/
@ -94,26 +99,28 @@ public class JSONUtilities extends AbstractUtilities {
final ObjectMapper mapper = new ObjectMapper();
final ObjectNode srcNode = (ObjectNode) mapper.readTree(is);
for (String linkTitle : links.getLinkNames()) {
// normalize link
srcNode.remove(linkTitle + JSON_NAVIGATION_BIND_SUFFIX);
srcNode.set(
linkTitle + JSON_NAVIGATION_SUFFIX,
new TextNode(String.format("%s(%s)/%s", entitySetName, entityKey, linkTitle)));
}
if (links != null) {
for (String linkTitle : links.getLinkNames()) {
// normalize link
srcNode.remove(linkTitle + JSON_NAVIGATION_BIND_SUFFIX);
srcNode.set(
linkTitle + JSON_NAVIGATION_SUFFIX,
new TextNode(String.format("%s(%s)/%s", entitySetName, entityKey, linkTitle)));
}
for (String linkTitle : links.getInlineNames()) {
// normalize link if exist; declare a new one if missing
srcNode.remove(linkTitle + JSON_NAVIGATION_BIND_SUFFIX);
srcNode.set(
linkTitle + JSON_NAVIGATION_SUFFIX,
new TextNode(String.format("%s(%s)/%s", entitySetName, entityKey, linkTitle)));
for (String linkTitle : links.getInlineNames()) {
// normalize link if exist; declare a new one if missing
srcNode.remove(linkTitle + JSON_NAVIGATION_BIND_SUFFIX);
srcNode.set(
linkTitle + JSON_NAVIGATION_SUFFIX,
new TextNode(String.format("%s(%s)/%s", entitySetName, entityKey, linkTitle)));
// remove inline
srcNode.remove(linkTitle);
// remove inline
srcNode.remove(linkTitle);
// remove from links
links.removeLink(linkTitle);
// remove from links
links.removeLink(linkTitle);
}
}
srcNode.set(

View File

@ -0,0 +1,43 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.msopentech.odatajclient.testservice.utils;
import java.util.List;
import java.util.Map;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
public class XHTTPMethodInterceptor extends AbstractPhaseInterceptor<Message> {
public XHTTPMethodInterceptor() {
super(Phase.PRE_PROTOCOL);
}
@Override
public void handleMessage(final Message message) throws Fault {
@SuppressWarnings("unchecked")
final Map<String, List<String>> headers = (Map<String, List<String>>) message.get(Message.PROTOCOL_HEADERS);
if (headers.containsKey(Constants.XHTTP_HEADER_NAME)) {
message.put(Message.HTTP_REQUEST_METHOD, headers.get(Constants.XHTTP_HEADER_NAME).iterator().next());
}
}
}

View File

@ -60,6 +60,11 @@ public class XMLUtilities extends AbstractUtilities {
super(version);
}
@Override
protected Accept getDefaultFormat() {
return Accept.ATOM;
}
protected static XMLEventReader getEventReader(final InputStream is) throws XMLStreamException {
if (factory == null) {
factory = XMLInputFactory.newInstance();

View File

@ -0,0 +1,13 @@
{
"odata.metadata": "http://localhost:${cargo.servlet.port}/StaticService/V3/Static.svc/$metadata#Car/@Element",
"odata.type": "Microsoft.Test.OData.Services.AstoriaDefaultService.Car",
"odata.id": "http://localhost:${cargo.servlet.port}/StaticService/V3/Static.svc/Car(14)",
"odata.editLink": "Car(14)",
"odata.mediaEditLink": "Car(14)/$value",
"odata.mediaReadLink": "Car(14)/$value",
"odata.mediaContentType": "*/*",
"Photo@odata.mediaEditLink": "Car(14)/Photo",
"Video@odata.mediaEditLink": "Car(14)/Video",
"VIN": 14,
"Description": "畚チびンぁあяまぴひタバァンぴ歹チ歹歹ァまマぞ珱暦ぼ歹グ珱ボチタびゼソゼたグёま畚a畚歹匚畚ァゼ匚Я欲匚チチボびソァぴ暦ぺポソチバЯゼ黑ダ匚マび暦ダソク歹まあa裹ソハ歹暦弌aバ暦ぽネ"
}

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<entry xml:base="http://localhost:${cargo.servlet.port}/StaticService/V3/Static.svc/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
<id>http://localhost:${cargo.servlet.port}/StaticService/V3/Static.svc/Car(14)</id>
<category term="Microsoft.Test.OData.Services.AstoriaDefaultService.Car" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" title="Car" href="Car(14)" />
<title />
<updated>2014-03-07T15:15:31Z</updated>
<author>
<name />
</author>
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/edit-media/Photo" title="Photo" href="Car(14)/Photo" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/edit-media/Video" title="Video" href="Car(14)/Video" />
<link rel="edit-media" title="Car" href="Car(14)/$value" />
<content type="*/*" src="Car(14)/$value" />
<m:properties>
<d:VIN m:type="Edm.Int32">14</d:VIN>
<d:Description>畚チびンぁあяまぴひタバァンぴ歹チ歹歹ァまマぞ珱暦ぼ歹グ珱ボチタびゼソゼたグёま畚a畚歹匚畚ァゼ匚Я欲匚チチボびソァぴ暦ぺポソチバЯゼ黑ダ匚マび暦ダソク歹まあa裹ソハ歹暦弌aバ暦ぽネ</d:Description>
</m:properties>
</entry>