← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 17837: support partial updates on /api/type/id/property, only simple properties are supported (string, i...

 

------------------------------------------------------------
revno: 17837
committer: Morten Olav Hansen <mortenoh@xxxxxxxxx>
branch nick: dhis2
timestamp: Tue 2014-12-30 15:09:22 +0100
message:
  support partial updates on /api/type/id/property, only simple properties are supported (string, int, bool, etc), will be expanded in 2.19 to support references (needs components from dxf2 importer which will be extracted), also adds utility methods in AbstractCrudController for checking for json/xml paylods, also utility method to deserialize from correct ct.
modified:
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/Jackson2PropertyIntrospectorService.java
  dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/Jackson2PropertyIntrospectorService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/Jackson2PropertyIntrospectorService.java	2014-12-22 13:52:51 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/Jackson2PropertyIntrospectorService.java	2014-12-30 14:09:22 +0000
@@ -94,7 +94,16 @@
 
             String fieldName = getFieldName( method );
             property.setName( !StringUtils.isEmpty( jsonProperty.value() ) ? jsonProperty.value() : fieldName );
-            property.setReadable( true );
+
+            if ( property.getGetterMethod() != null )
+            {
+                property.setReadable( true );
+            }
+
+            if ( property.getSetterMethod() != null )
+            {
+                property.setWritable( true );
+            }
 
             if ( classFieldNames.contains( fieldName ) )
             {

=== modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java'
--- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java	2014-12-26 12:27:14 +0000
+++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java	2014-12-30 14:09:22 +0000
@@ -66,6 +66,7 @@
 import org.hisp.dhis.webapi.webdomain.WebOptions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
+import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
@@ -74,6 +75,7 @@
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -195,7 +197,7 @@
             else
             {
                 // Get full list when using filters other than name / objects without persisted name
-                
+
                 if ( !filters.isEmpty() )
                 {
                     if ( options.hasPaging() )
@@ -271,11 +273,67 @@
 
     @RequestMapping( value = "/{uid}/{property}", method = RequestMethod.GET )
     public @ResponseBody RootNode getObjectProperty(
-        @PathVariable( "uid" ) String uid,
-        @PathVariable( "property" ) String pvProperty,
-        @RequestParam Map<String, String> parameters, HttpServletRequest request, HttpServletResponse response ) throws Exception
-    {
-        return getObjectInternal( uid, parameters, Lists.<String>newArrayList(), Lists.newArrayList( pvProperty + "[:all]" ) );
+        @PathVariable( "uid" ) String pvUid,
+        @PathVariable( "property" ) String pvProperty,
+        @RequestParam Map<String, String> parameters, HttpServletRequest request, HttpServletResponse response ) throws Exception
+    {
+        return getObjectInternal( pvUid, parameters, Lists.<String>newArrayList(), Lists.newArrayList( pvProperty + "[:all]" ) );
+    }
+
+    @RequestMapping( value = "/{uid}/{property}", method = { RequestMethod.PUT, RequestMethod.PATCH } )
+    public void updateObjectProperty(
+        @PathVariable( "uid" ) String pvUid,
+        @PathVariable( "property" ) String pvProperty,
+        @RequestParam Map<String, String> parameters, HttpServletRequest request, HttpServletResponse response ) throws Exception
+    {
+        WebOptions options = new WebOptions( parameters );
+        List<T> entities = getEntity( pvUid, options );
+
+        if ( entities.isEmpty() )
+        {
+            ContextUtils.notFoundResponse( response, getEntityName() + " does not exist: " + pvUid );
+            return;
+        }
+
+        if ( !getSchema().getPropertyMap().containsKey( pvProperty ) )
+        {
+            ContextUtils.notFoundResponse( response, "Property " + pvProperty + " does not exist on " + getEntityName() );
+            return;
+        }
+
+        Property property = getSchema().getProperty( pvProperty );
+        T persistedObject = entities.get( 0 );
+
+        if ( !aclService.canUpdate( currentUserService.getCurrentUser(), persistedObject ) )
+        {
+            throw new UpdateAccessDeniedException( "You don't have the proper permissions to update this object." );
+        }
+
+        if ( !property.isWritable() )
+        {
+            throw new UpdateAccessDeniedException( "This property is read-only." );
+        }
+
+        if ( !property.isSimple() )
+        {
+            // TODO we need to split out the reference scanner from the importer for this to work with idObjects
+            throw new UpdateAccessDeniedException( "Only simple properties are currently supported for update." );
+        }
+
+        T object = deserialize( request );
+
+        if ( object == null )
+        {
+            ContextUtils.badRequestResponse( response, "Unknown payload format." );
+            return;
+        }
+
+        Object value = property.getGetterMethod().invoke( object );
+
+        property.getSetterMethod().invoke( persistedObject, value );
+
+        response.setStatus( HttpServletResponse.SC_NO_CONTENT );
+        manager.update( persistedObject );
     }
 
     private RootNode getObjectInternal( String uid, Map<String, String> parameters,
@@ -816,6 +874,50 @@
         return InclusionStrategy.Include.NON_NULL;
     }
 
+    /**
+     * Deserializes a payload from the request, handles JSON/XML payloads
+     *
+     * @param request HttpServletRequest from current session
+     * @return Parsed entity or null if invalid type
+     */
+    protected T deserialize( HttpServletRequest request ) throws IOException
+    {
+        if ( isJson( request ) )
+        {
+            return renderService.fromJson( request.getInputStream(), getEntityClass() );
+        }
+        else if ( isXml( request ) )
+        {
+            return renderService.fromXml( request.getInputStream(), getEntityClass() );
+        }
+
+        return null;
+    }
+
+    /**
+     * Are we receiving JSON data?
+     *
+     * @param request HttpServletRequest from current session
+     * @return true if JSON compatible
+     */
+    protected boolean isJson( HttpServletRequest request )
+    {
+        String contentType = request.getContentType();
+        return !StringUtils.isEmpty( contentType ) && MediaType.parseMediaType( contentType ).isCompatibleWith( MediaType.APPLICATION_JSON );
+    }
+
+    /**
+     * Are we receiving XML data?
+     *
+     * @param request HttpServletRequest from current session
+     * @return true if XML compatible
+     */
+    protected boolean isXml( HttpServletRequest request )
+    {
+        String contentType = request.getContentType();
+        return !StringUtils.isEmpty( contentType ) && MediaType.parseMediaType( contentType ).isCompatibleWith( MediaType.APPLICATION_XML );
+    }
+
     //--------------------------------------------------------------------------
     // Reflection helpers
     //--------------------------------------------------------------------------