View Javadoc
1   /*
2    * Copyright (c) 2002-2021, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.crm.modules.notifygru.web.rs;
35  
36  
37  import com.fasterxml.jackson.core.JsonParseException;
38  import com.fasterxml.jackson.core.JsonProcessingException;
39  import com.fasterxml.jackson.databind.DeserializationFeature;
40  import com.fasterxml.jackson.databind.JsonMappingException;
41  import com.fasterxml.jackson.databind.ObjectMapper;
42  
43  import fr.paris.lutece.plugins.crm.business.demand.Demand;
44  import fr.paris.lutece.plugins.crm.business.demand.DemandStatusCRM;
45  import fr.paris.lutece.plugins.crm.business.demand.DemandType;
46  import fr.paris.lutece.plugins.crm.business.demand.DemandTypeHome;
47  import fr.paris.lutece.plugins.crm.business.notification.Notification;
48  import fr.paris.lutece.plugins.crm.business.user.CRMUser;
49  import fr.paris.lutece.plugins.crm.service.CRMService;
50  import fr.paris.lutece.plugins.crm.service.demand.DemandService;
51  import fr.paris.lutece.plugins.crm.modules.notifygru.util.CrmNotifyGruConstants;
52  import fr.paris.lutece.plugins.crm.service.demand.DemandStatusCRMService;
53  import fr.paris.lutece.plugins.crm.service.demand.DemandTypeService;
54  import fr.paris.lutece.plugins.crm.service.user.CRMUserService;
55  import fr.paris.lutece.plugins.rest.service.RestConstants;
56  import fr.paris.lutece.portal.service.i18n.I18nService;
57  import fr.paris.lutece.portal.service.util.AppLogService;
58  import fr.paris.lutece.util.json.AbstractJsonResponse;
59  import fr.paris.lutece.util.json.ErrorJsonResponse;
60  import fr.paris.lutece.util.json.JsonUtil;
61  import java.io.IOException;
62  import java.sql.Timestamp;
63  import java.util.List;
64  import java.util.Locale;
65  
66  import javax.servlet.http.HttpServletRequest;
67  import javax.ws.rs.Consumes;
68  import javax.ws.rs.GET;
69  import javax.ws.rs.PUT;
70  import javax.ws.rs.Path;
71  import javax.ws.rs.PathParam;
72  import javax.ws.rs.Produces;
73  import javax.ws.rs.core.Context;
74  import javax.ws.rs.core.MediaType;
75  import javax.ws.rs.core.Response;
76  
77  import org.apache.commons.lang3.StringUtils;
78  import org.apache.log4j.Logger;
79  
80  @Path( RestConstants.BASE_PATH + CrmNotifyGruConstants.API_PATH + CrmNotifyGruConstants.VERSION_PATH + CrmNotifyGruConstants.PLUGIN_NAME )
81  public class CrmNotifyGruRestService
82  {
83      private final Logger _logger = Logger.getLogger( RestConstants.REST_LOGGER );
84      private final String CHARECTER_REGEXP_FILTER = "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\p{Sm}\\p{Sc}\\s]";
85  
86      /**
87       * Web Service methode which permit to store the notification flow into a data store
88       * 
89       * @param nVersion
90       * @param strJson
91       *            The JSON flow
92       * @param request
93       * @return The response
94       */
95      @PUT
96      @Path( CrmNotifyGruConstants.NOTICATION_PATH )
97      @Consumes( MediaType.APPLICATION_JSON )
98      @Produces( MediaType.APPLICATION_JSON )
99      public Response notifications( @PathParam( CrmNotifyGruConstants.VERSION ) Integer nVersion, String strJson, @Context HttpServletRequest request )
100     {
101 
102         if ( nVersion == CrmNotifyGruConstants.VERSION_1 )
103         {
104             return storeNotificationV1( strJson, I18nService.getDefaultLocale( ) );
105         }
106 
107         _logger.error( CrmNotifyGruConstants.ERROR_NOT_FOUND_VERSION );
108 
109         return Response.status( Response.Status.NOT_FOUND )
110                 .entity( JsonUtil
111                         .buildJsonResponse( new ErrorJsonResponse( Response.Status.NOT_FOUND.name( ), CrmNotifyGruConstants.ERROR_NOT_FOUND_VERSION ) ) )
112                 .build( );
113     }
114 
115     /**
116      * store the notification
117      * 
118      * @param strJson
119      * @return the json response message
120      */
121     private Response storeNotificationV1( String strJson, Locale locale )
122     {
123         try
124         {
125             // Format from JSON
126             ObjectMapper mapper = new ObjectMapper( );
127             mapper.configure( DeserializationFeature.UNWRAP_ROOT_VALUE, true );
128             mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
129 
130             fr.paris.lutece.plugins.grubusiness.business.notification.Notification gruNotification = mapper.readValue( strJson,
131                     fr.paris.lutece.plugins.grubusiness.business.notification.Notification.class );
132             AppLogService.debug( "crm-notifygru / notification - Received strJson : " + strJson );
133 
134             return store( gruNotification, locale );
135 
136         }
137         catch ( JsonProcessingException ex )
138         {
139             return error( ex + " :" + ex.getMessage( ), Response.Status.BAD_REQUEST, ex );
140         }
141         catch ( Exception ex )
142         {
143             return error( ex + " :" + ex.getMessage( ), Response.Status.INTERNAL_SERVER_ERROR, ex );
144         }
145         
146     }
147 
148     /**
149      * Stores a notification and the associated demand
150      * 
151      * @param notification
152      *            the notification to store
153      */
154     private Response store( fr.paris.lutece.plugins.grubusiness.business.notification.Notification gruNotification, Locale locale )
155     {
156         AbstractJsonResponse jsonResponse;
157 
158         // check if connection id is present
159         if ( gruNotification.getDemand( ) == null || gruNotification.getDemand( ).getCustomer( ) == null
160                 || StringUtils.isBlank( gruNotification.getDemand( ).getCustomer( ).getConnectionId( ) ) )
161         {
162             return error( CrmNotifyGruConstants.MESSAGE_MISSING_USER_ID, Response.Status.PRECONDITION_FAILED, null );
163 
164         }
165 
166         // check if Demand remote id and demand type id are present
167         if ( StringUtils.isBlank( gruNotification.getDemand( ).getId( ) ) || StringUtils.isBlank( gruNotification.getDemand( ).getTypeId( ) ) )
168         {
169             return error( CrmNotifyGruConstants.MESSAGE_MISSING_DEMAND_ID, Response.Status.PRECONDITION_FAILED, null );
170         }
171 
172         // check id demand_type_id is numeric
173         if ( !StringUtils.isNumeric( gruNotification.getDemand( ).getTypeId( ) ) )
174         {
175         	return error( CrmNotifyGruConstants.MESSAGE_INCORRECT_DEMAND_ID, Response.Status.PRECONDITION_FAILED, null );
176         }
177         
178         int demandTypeId = Integer.parseInt( gruNotification.getDemand( ).getTypeId( ) );
179         
180         // check if  demand type id exists
181         if ( DemandTypeService.getService().findByPrimaryKey( demandTypeId  ) == null ) 
182         {
183             return error( CrmNotifyGruConstants.MESSAGE_INCORRECT_DEMAND_ID, Response.Status.PRECONDITION_FAILED, null );
184         }
185 
186         // get CRM demand from GRU Demand
187         Demand crmDemand = getCrmDemand( gruNotification );
188 
189         // check if crm Demand already exists
190         Demand storedDemand = DemandService.getService( ).findByRemoteKey( crmDemand.getRemoteId( ), crmDemand.getIdDemandType( ) );
191         int demandId = -1;
192 
193         if ( storedDemand != null )
194         {
195             demandId = storedDemand.getIdDemand( );
196 
197             // check if the customer id still the same
198             CRMUser storedUser = CRMUserService.getService( ).findByUserGuid( gruNotification.getDemand( ).getCustomer( ).getConnectionId( ) );
199 
200             if ( storedUser != null && ( storedUser.getIdCRMUser( ) != storedDemand.getIdCRMUser( ) ) )
201             {
202                 return error( CrmNotifyGruConstants.MESSAGE_INVALID_USER_ID, Response.Status.PRECONDITION_FAILED, null );
203             }
204 
205             // set modification date
206             storedDemand.setDateModification( crmDemand.getDateModification( ) );
207 
208             // set status id if updated
209             if ( storedDemand.getIdStatusCRM( ) != crmDemand.getIdStatusCRM( ) && crmDemand.getIdStatusCRM( ) >= 0 )
210             {
211             	storedDemand.setIdStatusCRM( crmDemand.getIdStatusCRM( ) );
212             }
213             
214             // set status text if updated
215             if ( crmDemand.getStatusText( )!=null && !crmDemand.getStatusText( ).equals(storedDemand.getStatusText( ) ) )
216             {
217             	storedDemand.setStatusText( crmDemand.getStatusText( ) );
218             }
219             
220             // set status label if not set
221             if ( StringUtils.isBlank( storedDemand.getStatusText( ) ) )
222             {
223             	DemandStatusCRM statusCRM = DemandStatusCRMService.getService( ).getStatusCRM( crmDemand.getIdStatusCRM( ), locale );
224             	storedDemand.setStatusText( statusCRM.getLabel( ) );
225             }  
226       
227 
228             // update stored demand
229             DemandService.getService( ).update( storedDemand );
230         }
231         else
232         {
233             // create user if not exists in CRM database
234             int nUserId;
235             CRMUser storedUser = CRMUserService.getService( ).findByUserGuid( gruNotification.getDemand( ).getCustomer( ).getConnectionId( ) );
236             if ( storedUser != null )
237             {
238                 nUserId = storedUser.getIdCRMUser( );
239             }
240             else
241             {
242                 CRMUser crmUser = new CRMUser( );
243                 crmUser.setUserGuid( gruNotification.getDemand( ).getCustomer( ).getConnectionId( ) );
244                 crmUser.setMustBeUpdated( true );
245 
246                 nUserId = CRMUserService.getService( ).create( crmUser );
247             }
248 
249             crmDemand.setIdCRMUser( nUserId );
250 
251             // get status text if not set in notif
252             if ( StringUtils.isBlank( crmDemand.getStatusText() ) && crmDemand.getIdStatusCRM( ) >= 0 )
253             {
254                 DemandStatusCRM statusCRM = DemandStatusCRMService.getService( ).getStatusCRM( crmDemand.getIdStatusCRM( ), locale );
255                 if ( statusCRM != null )
256                 {
257                     crmDemand.setStatusText( statusCRM.getLabel( ) );
258                 }
259             }
260 
261             // create demand
262             demandId = DemandService.getService( ).create( crmDemand );
263         }
264 
265         // get CRM notification from GRU notification
266         Notification crmNotification = getCrmNotification( gruNotification );
267 
268         CRMService.getService( ).notify( demandId, crmNotification.getObject( ), crmNotification.getMessage( ), crmNotification.getSender( ) );
269 
270         // success
271         return Response.status( Response.Status.CREATED ).entity( CrmNotifyGruConstants.STATUS_RECEIVED ).build( );
272     }
273 
274     /**
275      * get crm Demand from the GRU Demand of the GRU notification
276      * 
277      * @param gruNotification
278      * @return the demand
279      */
280     private Demand getCrmDemand( fr.paris.lutece.plugins.grubusiness.business.notification.Notification gruNotification )
281     {
282         Demand crmDemand = new Demand( );
283 
284         if ( gruNotification.getDemand( ) != null )
285         {
286             crmDemand.setRemoteId( gruNotification.getDemand( ).getId( ) );
287             crmDemand.setIdStatusCRM( gruNotification.getDemand( ).getStatusId( ) );
288             crmDemand.setDateModification( new Timestamp( gruNotification.getDate( ) ) );
289             crmDemand.setData( StringUtils.EMPTY );
290 
291             if ( StringUtils.isNumeric( gruNotification.getDemand( ).getTypeId( ) ) )
292             {
293                 crmDemand.setIdDemandType( Integer.parseInt( gruNotification.getDemand( ).getTypeId( ) ) );
294             }
295             
296             // update  demand status from mydashboard
297             if ( gruNotification.getMyDashboardNotification( ).getStatusText( ) != null ) 
298             {
299             	crmDemand.setStatusText( gruNotification.getMyDashboardNotification( ).getStatusText( ) );
300             }
301             if ( gruNotification.getMyDashboardNotification( ).getStatusId( ) >= 0 ) 
302             {
303             	crmDemand.setIdStatusCRM( gruNotification.getMyDashboardNotification( ).getStatusId( ) );
304             }
305         }
306 
307         return crmDemand;
308     }
309 
310     /**
311      * get CRM notification from GRU notification
312      * 
313      * @param gruNotification
314      * @return the notification
315      */
316     private Notification getCrmNotification( fr.paris.lutece.plugins.grubusiness.business.notification.Notification gruNotification )
317     {
318         Notification crmNotification = new Notification( );
319 
320         if ( gruNotification.getMyDashboardNotification( ) != null )
321         {
322         	// clean message (avoir emoticons...s)
323             crmNotification.setMessage( gruNotification.getMyDashboardNotification( ).getMessage( ).replaceAll(CHARECTER_REGEXP_FILTER,"") );
324             
325             crmNotification.setSender( gruNotification.getMyDashboardNotification( ).getSenderName( ) );
326             
327             // clean subject
328             crmNotification.setObject( gruNotification.getMyDashboardNotification( ).getSubject( ).replaceAll(CHARECTER_REGEXP_FILTER,"") );
329         }
330 
331         return crmNotification;
332     }
333 
334     /**
335      * Build an error response
336      * 
337      * @param strMessage
338      *            The error message
339      * @param ex
340      *            An exception
341      * @return The response
342      */
343     private Response error( String strMessage, Response.Status status, Throwable ex )
344     {
345         if ( ex != null )
346         {
347             AppLogService.error( strMessage, ex );
348         }
349         else
350         {
351             AppLogService.error( strMessage );
352         }
353 
354         String strError = "{ \"status\": \"Error : " + strMessage + "\" }";
355 
356         return Response.status( status ).entity( strError ).build( );
357     }
358     
359     /**
360      * Web Service methode which permit to store the notification flow into a data store
361      * 
362      * @return The response
363      */
364     @GET
365     @Path( CrmNotifyGruConstants.DEMAND_TYPE_PATH )
366     @Consumes( MediaType.APPLICATION_JSON )
367     @Produces( MediaType.APPLICATION_JSON )
368     public Response getDemandTypes( )
369     {
370 
371         List<DemandType> listDemandTypes = DemandTypeHome.findAll( );
372         try
373         {
374             ObjectMapper mapper = new ObjectMapper( );
375             
376             String strResult = mapper.writeValueAsString( listDemandTypes );
377             return Response.ok( strResult ).build( );
378         }
379         catch( JsonProcessingException e )
380         {
381             return Response.serverError( ).build( );
382         }
383 
384     }
385 }