Changes Listed below:
[music.git] / src / main / java / org / onap / music / rest / RestMusicDataAPI.java
1 /*
2  * ============LICENSE_START==========================================
3  * org.onap.music
4  * ===================================================================
5  *  Copyright (c) 2017 AT&T Intellectual Property
6  * ===================================================================
7  *  Modifications Copyright (c) 2019 Samsung
8  * ===================================================================
9  *  Licensed under the Apache License, Version 2.0 (the "License");
10  *  you may not use this file except in compliance with the License.
11  *  You may obtain a copy of the License at
12  *
13  *     http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *  Unless required by applicable law or agreed to in writing, software
16  *  distributed under the License is distributed on an "AS IS" BASIS,
17  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  *  See the License for the specific language governing permissions and
19  *  limitations under the License.
20  *
21  * ============LICENSE_END=============================================
22  * ====================================================================
23  */
24
25 package org.onap.music.rest;
26
27 import java.nio.ByteBuffer;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.UUID;
31
32 import javax.ws.rs.Consumes;
33 import javax.ws.rs.DELETE;
34 import javax.ws.rs.GET;
35 import javax.ws.rs.HeaderParam;
36 import javax.ws.rs.POST;
37 import javax.ws.rs.PUT;
38 import javax.ws.rs.Path;
39 import javax.ws.rs.PathParam;
40 import javax.ws.rs.Produces;
41 import javax.ws.rs.core.Context;
42 import javax.ws.rs.core.MediaType;
43 import javax.ws.rs.core.MultivaluedMap;
44 import javax.ws.rs.core.Response;
45 import javax.ws.rs.core.Response.ResponseBuilder;
46 import javax.ws.rs.core.Response.Status;
47 import javax.ws.rs.core.UriInfo;
48
49 import org.apache.commons.lang3.StringUtils;
50 import org.mindrot.jbcrypt.BCrypt;
51 import org.onap.music.authentication.CachingUtil;
52 import org.onap.music.authentication.MusicAAFAuthentication;
53 import org.onap.music.authentication.MusicAuthenticator;
54 import org.onap.music.authentication.MusicAuthenticator.Operation;
55 import org.onap.music.datastore.PreparedQueryObject;
56 import org.onap.music.datastore.jsonobjects.JsonDelete;
57 import org.onap.music.datastore.jsonobjects.JsonInsert;
58 import org.onap.music.datastore.jsonobjects.JsonKeySpace;
59 import org.onap.music.datastore.jsonobjects.JsonTable;
60 import org.onap.music.datastore.jsonobjects.JsonUpdate;
61 import org.onap.music.eelf.logging.EELFLoggerDelegate;
62 import org.onap.music.exceptions.MusicLockingException;
63 import org.onap.music.exceptions.MusicQueryException;
64 import org.onap.music.eelf.logging.format.AppMessages;
65 import org.onap.music.eelf.logging.format.ErrorSeverity;
66 import org.onap.music.eelf.logging.format.ErrorTypes;
67 import org.onap.music.exceptions.MusicServiceException;
68 import org.onap.music.main.MusicCore;
69 import org.onap.music.datastore.Condition;
70 import org.onap.music.datastore.MusicDataStoreHandle;
71 import org.onap.music.main.MusicUtil;
72 import org.onap.music.main.ResultType;
73 import org.onap.music.main.ReturnType;
74 import org.onap.music.response.jsonobjects.JsonResponse;
75
76 import com.datastax.driver.core.DataType;
77 import com.datastax.driver.core.ResultSet;
78 import com.datastax.driver.core.Row;
79 import com.datastax.driver.core.TableMetadata;
80
81 import io.swagger.annotations.Api;
82 import io.swagger.annotations.ApiOperation;
83 import io.swagger.annotations.ApiParam;
84 import io.swagger.annotations.ApiResponses;
85 import io.swagger.annotations.ApiResponse;
86
87 /* Version 2 Class */
88 //@Path("/v{version: [0-9]+}/keyspaces")
89 @Path("/v2/keyspaces")
90 @Api(value = "Data Api")
91 public class RestMusicDataAPI {
92     /*
93      * Header values for Versioning X-minorVersion *** - Used to request or communicate a MINOR
94      * version back from the client to the server, and from the server back to the client - This
95      * will be the MINOR version requested by the client, or the MINOR version of the last MAJOR
96      * version (if not specified by the client on the request) - Contains a single position value
97      * (e.g. if the full version is 1.24.5, X-minorVersion = "24") - Is optional for the client on
98      * request; however, this header should be provided if the client needs to take advantage of
99      * MINOR incremented version functionality - Is mandatory for the server on response
100      *
101      *** X-patchVersion *** - Used only to communicate a PATCH version in a response for
102      * troubleshooting purposes only, and will not be provided by the client on request - This will
103      * be the latest PATCH version of the MINOR requested by the client, or the latest PATCH version
104      * of the MAJOR (if not specified by the client on the request) - Contains a single position
105      * value (e.g. if the full version is 1.24.5, X-patchVersion = "5") - Is mandatory for the
106      * server on response  (CURRENTLY NOT USED)
107      *
108      *** X-latestVersion *** - Used only to communicate an API's latest version - Is mandatory for the
109      * server on response, and shall include the entire version of the API (e.g. if the full version
110      * is 1.24.5, X-latestVersion = "1.24.5") - Used in the response to inform clients that they are
111      * not using the latest version of the API (CURRENTLY NOT USED)
112      *
113      */
114
115     private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RestMusicDataAPI.class);
116     private static final String XMINORVERSION = "X-minorVersion";
117     private static final String XPATCHVERSION = "X-patchVersion";
118     private static final String NS = "ns";
119     private static final String VERSION = "v2";
120     private MusicAuthenticator authenticator = new MusicAAFAuthentication();
121     // Set to true in env like ONAP. Where access to creating and dropping keyspaces exist.    
122     private static final boolean KEYSPACE_ACTIVE = false;
123
124     private class RowIdentifier {
125         public String primarKeyValue;
126         public StringBuilder rowIdString;
127         @SuppressWarnings("unused")
128         public PreparedQueryObject queryObject; // the string with all the row
129                                                 // identifiers separated by AND
130
131         public RowIdentifier(String primaryKeyValue, StringBuilder rowIdString,
132                         PreparedQueryObject queryObject) {
133             this.primarKeyValue = primaryKeyValue;
134             this.rowIdString = rowIdString;
135             this.queryObject = queryObject;
136         }
137     }
138
139
140     /**
141      * Create Keyspace REST
142      *
143      * @param kspObject
144      * @param keyspaceName
145      * @return
146      * @throws Exception
147      */
148     @POST
149     @Path("/{name}")
150     @ApiOperation(value = "Create Keyspace", response = String.class,hidden = true)
151     @Consumes(MediaType.APPLICATION_JSON)
152     @Produces(MediaType.APPLICATION_JSON)
153     public Response createKeySpace(
154         @ApiParam(value = "Major Version",required = true) @PathParam("version") String version,
155         @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion,
156         @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
157         @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid,
158         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
159         @ApiParam(value = "Application namespace",required = true) @HeaderParam(NS) String ns,
160         JsonKeySpace kspObject,
161         @ApiParam(value = "Keyspace Name",required = true) @PathParam("name") String keyspaceName) {
162         try {
163             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
164             EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspaceName+" ) ");
165             logger.info(EELFLoggerDelegate.applicationLogger,"In Create Keyspace " + keyspaceName);
166             if ( MusicUtil.isKeyspaceActive() ) {
167                 logger.info(EELFLoggerDelegate.applicationLogger,"Creating Keyspace " + keyspaceName);
168                 Map<String,String> userCredentials = MusicUtil.extractBasicAuthentication(authorization);
169                 String userId = userCredentials.get(MusicUtil.USERID);
170                 String password = userCredentials.get(MusicUtil.PASSWORD);
171                 Map<String, Object> authMap = CachingUtil.verifyOnboarding(ns, userId, password);
172                 if (!authMap.isEmpty()) {
173                     logger.error(EELFLoggerDelegate.errorLogger,authMap.get("Exception").toString(), AppMessages.MISSINGDATA  ,ErrorSeverity.CRITICAL, ErrorTypes.AUTHENTICATIONERROR);
174                     response.status(Status.UNAUTHORIZED);
175                     return response.entity(new JsonResponse(ResultType.FAILURE).setError(String.valueOf(authMap.get("Exception"))).toMap()).build();
176                 }
177         
178                 if (!authenticator.authenticateUser(ns, authorization, keyspaceName, aid, Operation.CREATE_KEYSPACE)) {
179                     return response.status(Status.UNAUTHORIZED)
180                             .entity(new JsonResponse(ResultType.FAILURE)
181                                     .setError("Unauthorized: Please check username, password and make sure your app is onboarded")
182                                     .toMap()).build();
183                 }  
184         
185                 String consistency = MusicUtil.EVENTUAL;// for now this needs only
186                                                         // eventual consistency
187         
188                 if(kspObject == null || kspObject.getReplicationInfo() == null) {
189                     response.status(Status.BAD_REQUEST);
190                     return response.entity(new JsonResponse(ResultType.FAILURE).setError(ResultType.BODYMISSING.getResult()).toMap()).build();
191                 }
192                 PreparedQueryObject queryObject = new PreparedQueryObject();
193                 if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL) && kspObject.getConsistencyInfo().get("consistency") != null) {
194                     if (MusicUtil.isValidConsistency(kspObject.getConsistencyInfo().get("consistency"))) { 
195                         queryObject.setConsistency(kspObject.getConsistencyInfo().get("consistency")); 
196                     } else {
197                         return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR)
198                             .setError("Invalid Consistency type").toMap()).build();
199                     }
200                 }
201                 long start = System.currentTimeMillis();
202                 Map<String, Object> replicationInfo = kspObject.getReplicationInfo();
203                 String repString = null;
204                 try {
205                     repString = "{" + MusicUtil.jsonMaptoSqlString(replicationInfo, ",") + "}";
206                 } catch (Exception e) {
207                     logger.error(EELFLoggerDelegate.errorLogger,e.getMessage(), AppMessages.MISSINGDATA  ,ErrorSeverity
208                         .CRITICAL, ErrorTypes.DATAERROR, e);
209         
210                 }
211                 queryObject.appendQueryString(
212                                 "CREATE KEYSPACE " + keyspaceName + " WITH replication = " + repString);
213                 if (kspObject.getDurabilityOfWrites() != null) {
214                     queryObject.appendQueryString(
215                                     " AND durable_writes = " + kspObject.getDurabilityOfWrites());
216                 }
217         
218                 queryObject.appendQueryString(";");
219                 long end = System.currentTimeMillis();
220                 logger.info(EELFLoggerDelegate.applicationLogger,
221                                 "Time taken for setting up query in create keyspace:" + (end - start));
222         
223                 ResultType result = ResultType.FAILURE;
224                 try {
225                     result = MusicCore.nonKeyRelatedPut(queryObject, consistency);
226                     logger.info(EELFLoggerDelegate.applicationLogger, "result = " + result);
227                 } catch ( MusicServiceException ex) {
228                     logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity
229                         .WARN, ErrorTypes.MUSICSERVICEERROR, ex);
230                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("err:" + ex.getMessage()).toMap()).build();
231                 }
232         
233                 try {
234                     queryObject = new PreparedQueryObject();
235                     queryObject.appendQueryString("CREATE ROLE IF NOT EXISTS '" + userId
236                                     + "' WITH PASSWORD = '" + password + "' AND LOGIN = true;");
237                     MusicCore.nonKeyRelatedPut(queryObject, consistency);
238                     queryObject = new PreparedQueryObject();
239                     queryObject.appendQueryString("GRANT ALL PERMISSIONS on KEYSPACE " + keyspaceName
240                                         + " to '" + userId + "'");
241                     queryObject.appendQueryString(";");
242                     MusicCore.nonKeyRelatedPut(queryObject, consistency);
243                 } catch (Exception e) {
244                     logger.error(EELFLoggerDelegate.errorLogger,e.getMessage(), AppMessages.UNKNOWNERROR,ErrorSeverity
245                         .WARN, ErrorTypes.MUSICSERVICEERROR, e);
246                 }
247         
248                 try {
249                     boolean isAAF = Boolean.valueOf(CachingUtil.isAAFApplication(ns));
250                     String hashedpwd = BCrypt.hashpw(password, BCrypt.gensalt());
251                     queryObject = new PreparedQueryObject();
252                     queryObject.appendQueryString(
253                                 "INSERT into admin.keyspace_master (uuid, keyspace_name, application_name, is_api, "
254                                                 + "password, username, is_aaf) values (?,?,?,?,?,?,?)");
255                     queryObject.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), aid));
256                     queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), keyspaceName));
257                     queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), ns));
258                     queryObject.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), "True"));
259                     queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), hashedpwd));
260                     queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId));
261                     queryObject.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), isAAF));
262                     CachingUtil.updateMusicCache(keyspaceName, ns);
263                     CachingUtil.updateMusicValidateCache(ns, userId, hashedpwd);
264                     MusicCore.eventualPut(queryObject);
265                 } catch (Exception e) {
266                     logger.error(EELFLoggerDelegate.errorLogger,e.getMessage(), AppMessages.UNKNOWNERROR,ErrorSeverity
267                         .WARN, ErrorTypes.MUSICSERVICEERROR, e);
268                     return response.status(Response.Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();
269                 }
270         
271                 return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("Keyspace " + keyspaceName + " Created").toMap()).build();
272             } else {
273                 String vError = "Keyspace Creation has been turned off. Contact DBA to create the keyspace or set keyspace.active to true.";
274                 logger.info(EELFLoggerDelegate.applicationLogger,vError);
275                 logger.error(EELFLoggerDelegate.errorLogger,vError, AppMessages.UNKNOWNERROR,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR);
276                 return response.status(Response.Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(vError).toMap()).build();
277             }
278         } finally {
279             EELFLoggerDelegate.mdcRemove("keyspace");
280         }
281         
282     }
283
284     /**
285      *
286      * @param kspObject
287      * @param keyspaceName
288      * @return
289      * @throws Exception
290      */
291     @DELETE
292     @Path("/{name}")
293     @ApiOperation(value = "Delete Keyspace", response = String.class,hidden=true)
294     @Produces(MediaType.APPLICATION_JSON)
295     public Response dropKeySpace(
296         @ApiParam(value = "Major Version",required = true) @PathParam("version") String version,
297         @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion,
298         @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
299         @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid,
300         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
301         @ApiParam(value = "Application namespace",required = true) @HeaderParam(NS) String ns,
302         @ApiParam(value = "Keyspace Name",required = true) @PathParam("name") String keyspaceName) throws Exception {
303         try {
304             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
305             EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspaceName + " ) ");
306             logger.info(EELFLoggerDelegate.applicationLogger,"In Drop Keyspace " + keyspaceName);
307             if (MusicUtil.isKeyspaceActive()) {
308                 if (!authenticator.authenticateUser(ns, authorization, keyspaceName, aid, Operation.DROP_KEYSPACE)) {
309                     return response.status(Status.UNAUTHORIZED)
310                         .entity(new JsonResponse(ResultType.FAILURE)
311                         .setError("Unauthorized: Please check username, password and make sure your app is onboarded")
312                         .toMap()).build();
313                 }
314                 String consistency = MusicUtil.EVENTUAL;// for now this needs only
315                                                         // eventual
316                 // consistency
317                 String appName = CachingUtil.getAppName(keyspaceName);
318                 String uuid = CachingUtil.getUuidFromMusicCache(keyspaceName);
319                 PreparedQueryObject pQuery = new PreparedQueryObject();
320                 pQuery.appendQueryString(
321                                 "select  count(*) as count from admin.keyspace_master where application_name=? allow filtering;");
322                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName));
323                 Row row = MusicCore.get(pQuery).one();
324                 long count = row.getLong(0);
325         
326                 if (count == 0) {
327                     logger.error(EELFLoggerDelegate.errorLogger,"Keyspace not found. Please make sure keyspace exists.", AppMessages.INCORRECTDATA  ,ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR);
328                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Keyspace not found. Please make sure keyspace exists.").toMap()).build();
329                 // Admin Functions:
330                 } else if (count == 1) {
331                     pQuery = new PreparedQueryObject();
332                     pQuery.appendQueryString(
333                             "UPDATE admin.keyspace_master SET keyspace_name=? where uuid = ?;");
334                     pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(),
335                             MusicUtil.DEFAULTKEYSPACENAME));
336                     pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid));
337                     MusicCore.nonKeyRelatedPut(pQuery, consistency);
338                 } else {
339                     pQuery = new PreparedQueryObject();
340                     pQuery.appendQueryString("delete from admin.keyspace_master where uuid = ?");
341                     pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid));
342                     MusicCore.nonKeyRelatedPut(pQuery, consistency);
343                 }
344         
345                 PreparedQueryObject queryObject = new PreparedQueryObject();
346                 queryObject.appendQueryString("DROP KEYSPACE " + keyspaceName + ";");
347                 ResultType result = MusicCore.nonKeyRelatedPut(queryObject, consistency);
348                 if ( result.equals(ResultType.FAILURE) ) {
349                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result).setError("Error Deleteing Keyspace " + keyspaceName).toMap()).build();
350                 }
351                 return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("Keyspace " + keyspaceName + " Deleted").toMap()).build();
352             } else {
353                 String vError = "Keyspace deletion has been turned off. Contact DBA to delete the keyspace or set keyspace.active to true.";
354                 logger.info(EELFLoggerDelegate.applicationLogger,vError);
355                 logger.error(EELFLoggerDelegate.errorLogger,vError, AppMessages.UNKNOWNERROR,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR);
356                 return response.status(Response.Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(vError).toMap()).build();
357             }
358         } finally {
359             EELFLoggerDelegate.mdcRemove("keyspace");
360         }
361     }
362
363     /**
364      *
365      * @param tableObj
366      * @param version
367      * @param keyspace
368      * @param tablename
369      * @param headers
370      * @return
371      * @throws Exception -
372      */
373     @POST
374     @Path("/{keyspace: .*}/tables/{tablename: .*}")
375     @ApiOperation(value = "Create Table", response = String.class)
376     @Consumes(MediaType.APPLICATION_JSON)
377     @Produces(MediaType.APPLICATION_JSON)
378     @ApiResponses(value={
379         @ApiResponse(code= 400, message = "Will return JSON response with message"),
380         @ApiResponse(code= 401, message = "Unautorized User")
381     })
382     public Response createTable(
383         @ApiParam(value = "Major Version",required = true) @PathParam("version") String version,
384         @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion,
385         @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
386         @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid,
387         @ApiParam(value = "Application namespace",required = true) @HeaderParam(NS) String ns,
388         @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
389         JsonTable tableObj,
390         @ApiParam(value = "Keyspace Name",required = true) @PathParam("keyspace") String keyspace,
391         @ApiParam(value = "Table Name",required = true) @PathParam("tablename") String tablename) throws Exception {
392         try {
393             ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
394             if(keyspace == null || keyspace.isEmpty() || tablename == null || tablename.isEmpty()){
395                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
396                         .setError("One or more path parameters are not set, please check and try again."
397                             + "Parameter values: keyspace='" + keyspace + "' tablename='" + tablename + "'")
398                             .toMap()).build();
399             }
400             EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
401             if (!authenticator.authenticateUser(ns, authorization, keyspace, aid, Operation.CREATE_TABLE)) {
402                 return response.status(Status.UNAUTHORIZED)
403                         .entity(new JsonResponse(ResultType.FAILURE)
404                                 .setError("Unauthorized: Please check username, password and make sure your app is onboarded")
405                                 .toMap()).build();
406             }       
407             String consistency = MusicUtil.EVENTUAL;
408             // for now this needs only eventual consistency
409             String primaryKey = null;
410             String partitionKey = tableObj.getPartitionKey();
411             String clusterKey = tableObj.getClusteringKey();
412             String filteringKey = tableObj.getFilteringKey();
413             if(filteringKey != null) {
414                 clusterKey = clusterKey + "," + filteringKey;
415             }
416             primaryKey = tableObj.getPrimaryKey(); // get primaryKey if available
417
418             PreparedQueryObject queryObject = new PreparedQueryObject();
419             // first read the information about the table fields
420             Map<String, String> fields = tableObj.getFields();
421             if (fields == null) {
422                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
423                         .setError("Create Table Error: No fields in request").toMap()).build();
424             }
425
426             StringBuilder fieldsString = new StringBuilder("(vector_ts text,");
427             int counter = 0;
428             for (Map.Entry<String, String> entry : fields.entrySet()) {
429                 if (entry.getKey().equals("PRIMARY KEY")) {
430                     primaryKey = entry.getValue(); // replaces primaryKey
431                     primaryKey = primaryKey.trim();
432                 } else {
433                     if (counter == 0 )  fieldsString.append("" + entry.getKey() + " " + entry.getValue() + "");
434                     else fieldsString.append("," + entry.getKey() + " " + entry.getValue() + "");
435                 }
436
437                 if (counter != (fields.size() - 1) ) {
438                     counter = counter + 1; 
439                 } else {
440             
441                     if((primaryKey != null) && (partitionKey == null)) {
442                         primaryKey = primaryKey.trim();
443                         int count1 = StringUtils.countMatches(primaryKey, ')');
444                         int count2 = StringUtils.countMatches(primaryKey, '(');
445                         if (count1 != count2) {
446                             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
447                                 .setError("Create Table Error: primary key '(' and ')' do not match, primary key=" + primaryKey)
448                                 .toMap()).build();
449                         }
450
451                         if ( primaryKey.indexOf('(') == -1  || ( count2 == 1 && (primaryKey.lastIndexOf(')') +1) ==  primaryKey.length() ) ) {
452                             if (primaryKey.contains(",") ) {
453                                 partitionKey= primaryKey.substring(0,primaryKey.indexOf(','));
454                                 partitionKey=partitionKey.replaceAll("[\\(]+","");
455                                 clusterKey=primaryKey.substring(primaryKey.indexOf(',')+1);  // make sure index
456                                 clusterKey=clusterKey.replaceAll("[)]+", "");
457                             } else {
458                                 partitionKey=primaryKey;
459                                 partitionKey=partitionKey.replaceAll("[\\)]+","");
460                                 partitionKey=partitionKey.replaceAll("[\\(]+","");
461                                 clusterKey="";
462                             }
463                         } else {   // not null and has ) before the last char
464                             partitionKey= primaryKey.substring(0,primaryKey.indexOf(')'));
465                             partitionKey=partitionKey.replaceAll("[\\(]+","");
466                             partitionKey = partitionKey.trim();
467                             clusterKey= primaryKey.substring(primaryKey.indexOf(')'));
468                             clusterKey=clusterKey.replaceAll("[\\(]+","");
469                             clusterKey=clusterKey.replaceAll("[\\)]+","");
470                             clusterKey = clusterKey.trim();
471                             if (clusterKey.indexOf(',') == 0) {
472                                 clusterKey=clusterKey.substring(1);
473                             }
474                             clusterKey = clusterKey.trim();
475                             if (clusterKey.equals(",") ) clusterKey=""; // print error if needed    ( ... ),)
476                         }
477
478                         if (!(partitionKey.isEmpty() || clusterKey.isEmpty())
479                             && (partitionKey.equalsIgnoreCase(clusterKey) ||
480                             clusterKey.contains(partitionKey) || partitionKey.contains(clusterKey)) ) {
481                             logger.error("DataAPI createTable partition/cluster key ERROR: partitionKey="+partitionKey+", clusterKey=" + clusterKey + " and primary key=" + primaryKey );
482                             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(
483                                 "Create Table primary key error: clusterKey(" + clusterKey + ") equals/contains/overlaps partitionKey(" +partitionKey+ ")  of"
484                                 + " primary key=" + primaryKey)
485                                 .toMap()).build();
486
487                         }
488
489                         if (partitionKey.isEmpty() )  primaryKey="";
490                         else  if (clusterKey.isEmpty() ) primaryKey=" (" + partitionKey  + ")";
491                         else  primaryKey=" (" + partitionKey + ")," + clusterKey;
492
493                 
494                         if (primaryKey != null) fieldsString.append(", PRIMARY KEY (" + primaryKey + " )");
495
496                     } else { // end of length > 0
497                     
498                         if (!(partitionKey.isEmpty() || clusterKey.isEmpty())
499                             && (partitionKey.equalsIgnoreCase(clusterKey) ||
500                             clusterKey.contains(partitionKey) || partitionKey.contains(clusterKey)) ) {
501                             logger.error("DataAPI createTable partition/cluster key ERROR: partitionKey="+partitionKey+", clusterKey=" + clusterKey);
502                             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(
503                                 "Create Table primary key error: clusterKey(" + clusterKey + ") equals/contains/overlaps partitionKey(" +partitionKey+ ")")
504                                 .toMap()).build();
505                         }
506
507                         if (partitionKey.isEmpty() )  primaryKey="";
508                         else  if (clusterKey.isEmpty() ) primaryKey=" (" + partitionKey  + ")";
509                         else  primaryKey=" (" + partitionKey + ")," + clusterKey;
510
511                         if (primaryKey != null) fieldsString.append(", PRIMARY KEY (" + primaryKey + " )");
512                     }
513                     fieldsString.append(")");
514
515                 } // end of last field check
516
517             } // end of for each
518             // information about the name-value style properties
519             Map<String, Object> propertiesMap = tableObj.getProperties();
520             StringBuilder propertiesString = new StringBuilder();
521             if (propertiesMap != null) {
522                 counter = 0;
523                 for (Map.Entry<String, Object> entry : propertiesMap.entrySet()) {
524                     Object ot = entry.getValue();
525                     String value = ot + "";
526                     if (ot instanceof String) {
527                         value = "'" + value + "'";
528                     } else if (ot instanceof Map) {
529                         @SuppressWarnings("unchecked")
530                         Map<String, Object> otMap = (Map<String, Object>) ot;
531                         value = "{" + MusicUtil.jsonMaptoSqlString(otMap, ",") + "}";
532                     }
533
534                     propertiesString.append(entry.getKey() + "=" + value + "");
535                     if (counter != propertiesMap.size() - 1)
536                         propertiesString.append(" AND ");
537
538                     counter = counter + 1;
539                 }
540             }
541
542             String clusteringOrder = tableObj.getClusteringOrder();
543
544             if (clusteringOrder != null && !(clusteringOrder.isEmpty())) {
545                 String[] arrayClusterOrder = clusteringOrder.split("[,]+");
546
547                 for (int i = 0; i < arrayClusterOrder.length; i++) {
548                     String[] clusterS = arrayClusterOrder[i].trim().split("[ ]+");
549                     if ( (clusterS.length ==2)  && (clusterS[1].equalsIgnoreCase("ASC") || clusterS[1].equalsIgnoreCase("DESC"))) {
550                         continue;
551                     } else {
552                         return response.status(Status.BAD_REQUEST)
553                         .entity(new JsonResponse(ResultType.FAILURE)
554                         .setError("createTable/Clustering Order vlaue ERROR: valid clustering order is ASC or DESC or expecting colname  order; please correct clusteringOrder:"+ clusteringOrder+".")
555                         .toMap()).build();
556                     }
557                     // add validation for column names in cluster key
558                 }
559
560                 if (!(clusterKey.isEmpty())) {
561                     clusteringOrder = "CLUSTERING ORDER BY (" +clusteringOrder +")";
562                     //cjc check if propertiesString.length() >0 instead propertiesMap
563                     if (propertiesMap != null) {
564                         propertiesString.append(" AND  "+ clusteringOrder);
565                     } else {
566                         propertiesString.append(clusteringOrder);
567                     }
568                 } else {
569                     logger.warn("Skipping clustering order=("+clusteringOrder+ ") since clustering key is empty ");
570                 }
571             } //if non empty
572
573             queryObject.appendQueryString(
574                 "CREATE TABLE " + keyspace + "." + tablename + " " + fieldsString);
575
576
577             if (propertiesString != null &&  propertiesString.length()>0 )
578                 queryObject.appendQueryString(" WITH " + propertiesString);
579             queryObject.appendQueryString(";");
580             ResultType result = ResultType.FAILURE;
581             try {
582                 result = MusicCore.createTable(keyspace, tablename, queryObject, consistency);
583             } catch (MusicServiceException ex) {
584                 logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity.CRITICAL, ErrorTypes.MUSICSERVICEERROR);
585                 response.status(Status.BAD_REQUEST);
586                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
587             }
588             if ( result.equals(ResultType.FAILURE) ) {
589                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result).setError("Error Creating Table " + tablename).toMap()).build();
590             }
591             return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("TableName " + tablename.trim() + " Created under keyspace " + keyspace.trim()).toMap()).build();
592         } finally {
593             EELFLoggerDelegate.mdcRemove("keyspace");
594         }
595     }
596
597     /**
598      *
599      * @param keyspace
600      * @param tablename
601      * @param fieldName
602      * @param info
603      * @throws Exception
604      */
605     @POST
606     @Path("/{keyspace: .*}/tables/{tablename: .*}/index/{field: .*}")
607     @ApiOperation(value = "Create Index", response = String.class)
608     @Produces(MediaType.APPLICATION_JSON)
609     public Response createIndex(
610                     @ApiParam(value = "Major Version",required = true) @PathParam("version") String version,
611                     @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion,
612                     @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
613                     @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid,
614                     @ApiParam(value = "Application namespace",required = true) @HeaderParam(NS) String ns,
615                     @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
616                     @ApiParam(value = "Keyspace Name",required = true) @PathParam("keyspace") String keyspace,
617                     @ApiParam(value = "Table Name",required = true) @PathParam("tablename") String tablename,
618                     @ApiParam(value = "Field Name",required = true) @PathParam("field") String fieldName,
619                     @Context UriInfo info) throws Exception {
620         try {
621         ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
622         if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty()) || (fieldName == null || fieldName.isEmpty())){
623             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
624                 .setError("one or more path parameters are not set, please check and try again")
625                 .toMap()).build();
626         }
627         EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
628         if (!authenticator.authenticateUser(ns, authorization, keyspace, aid, Operation.CREATE_INDEX)) {
629             return response.status(Status.UNAUTHORIZED)
630                     .entity(new JsonResponse(ResultType.FAILURE)
631                             .setError("Unauthorized: Please check username, password and make sure your app is onboarded")
632                             .toMap()).build();
633         } 
634
635         MultivaluedMap<String, String> rowParams = info.getQueryParameters();
636         String indexName = "";
637         if (rowParams.getFirst("index_name") != null)
638             indexName = rowParams.getFirst("index_name");
639         PreparedQueryObject query = new PreparedQueryObject();
640         query.appendQueryString("Create index if not exists " + indexName + "  on " + keyspace + "."
641                         + tablename + " (" + fieldName + ");");
642
643         ResultType result = ResultType.FAILURE;
644         try {
645             result = MusicCore.nonKeyRelatedPut(query, "eventual");
646         } catch (MusicServiceException ex) {
647             logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity
648                 .CRITICAL, ErrorTypes.GENERALSERVICEERROR, ex);
649             response.status(Status.BAD_REQUEST);
650             return response.entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
651         }
652         if ( result.equals(ResultType.SUCCESS) ) {
653             return response.status(Status.OK).entity(new JsonResponse(result).setMessage("Index Created on " + keyspace+"."+tablename+"."+fieldName).toMap()).build();
654         } else {
655             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result).setError("Unknown Error in create index.").toMap()).build();
656         }
657         } finally {
658             EELFLoggerDelegate.mdcRemove("keyspace");
659         }
660     }
661
662     /**
663      *
664      * @param insObj
665      * @param keyspace
666      * @param tablename
667      * @return
668      * @throws Exception
669      */
670     @POST
671     @Path("/{keyspace: .*}/tables/{tablename: .*}/rows")
672     @ApiOperation(value = "Insert Into Table", response = String.class)
673     @Consumes(MediaType.APPLICATION_JSON)
674     @Produces(MediaType.APPLICATION_JSON)
675     public Response insertIntoTable(
676                     @ApiParam(value = "Major Version",required = true) @PathParam("version") String version,
677                     @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion,
678                     @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
679                     @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid,
680                     @ApiParam(value = "Application namespace",required = true) @HeaderParam(NS) String ns,
681                     @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
682                     JsonInsert insObj,
683                     @ApiParam(value = "Keyspace Name",
684                                     required = true) @PathParam("keyspace") String keyspace,
685                     @ApiParam(value = "Table Name",
686                                     required = true) @PathParam("tablename") String tablename) {
687         try {
688         ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
689         if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
690             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
691                 .setError("one or more path parameters are not set, please check and try again")
692                 .toMap()).build();
693         }
694         EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
695         if (!authenticator.authenticateUser(ns, authorization, keyspace, aid, Operation.INSERT_INTO_TABLE)) {
696             return response.status(Status.UNAUTHORIZED)
697                     .entity(new JsonResponse(ResultType.FAILURE)
698                             .setError("Unauthorized: Please check username, password and make sure your app is onboarded")
699                             .toMap()).build();
700         }
701
702         PreparedQueryObject queryObject = new PreparedQueryObject();
703         TableMetadata tableInfo = null;
704         try {
705             tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename);
706             if(tableInfo == null) {
707                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Table name doesn't exists. Please check the table name.").toMap()).build();
708             }
709         } catch (MusicServiceException e) {
710             logger.error(EELFLoggerDelegate.errorLogger, e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR);
711             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();
712         }
713         String primaryKeyName = tableInfo.getPrimaryKey().get(0).getName();
714         StringBuilder fieldsString = new StringBuilder("(vector_ts,");
715         String vectorTs =
716                         String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
717         StringBuilder valueString = new StringBuilder("(" + "?" + ",");
718         queryObject.addValue(vectorTs);
719         
720         Map<String, Object> valuesMap = insObj.getValues();
721         if (valuesMap==null) {
722             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
723                     .setError("Nothing to insert. No values provided in request.").toMap()).build();
724         }
725         int counter = 0;
726         String primaryKey = "";
727         for (Map.Entry<String, Object> entry : valuesMap.entrySet()) {
728             fieldsString.append("" + entry.getKey());
729             Object valueObj = entry.getValue();
730             if (primaryKeyName.equals(entry.getKey())) {
731                 primaryKey = entry.getValue() + "";
732                 primaryKey = primaryKey.replace("'", "''");
733             }
734             DataType colType = null;
735             try {
736                 colType = tableInfo.getColumn(entry.getKey()).getType();
737             } catch(NullPointerException ex) {
738                 logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage() +" Invalid column name : "+entry.getKey
739                     (), AppMessages.INCORRECTDATA  ,ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR, ex);
740                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Invalid column name : "+entry.getKey()).toMap()).build();
741             }
742
743             Object formattedValue = null;
744             try {
745                 formattedValue = MusicUtil.convertToActualDataType(colType, valueObj);
746             } catch (Exception e) {
747               logger.error(EELFLoggerDelegate.errorLogger,e);
748           }
749             valueString.append("?");
750
751             queryObject.addValue(formattedValue);
752
753             if (counter == valuesMap.size() - 1) {
754                 fieldsString.append(")");
755                 valueString.append(")");
756             } else {
757                 fieldsString.append(",");
758                 valueString.append(",");
759             }
760             counter = counter + 1;
761         }
762
763         //blobs..
764         Map<String, byte[]> objectMap = insObj.getObjectMap();
765         if(objectMap != null) {
766             for (Map.Entry<String, byte[]> entry : objectMap.entrySet()) {
767                 if(counter > 0) {
768                     fieldsString.replace(fieldsString.length()-1, fieldsString.length(), ",");
769                     valueString.replace(valueString.length()-1, valueString.length(), ",");
770                 }
771                 fieldsString.append("" + entry.getKey());
772                 byte[] valueObj = entry.getValue();
773                 if (primaryKeyName.equals(entry.getKey())) {
774                     primaryKey = entry.getValue() + "";
775                     primaryKey = primaryKey.replace("'", "''");
776                 }
777
778                 DataType colType = tableInfo.getColumn(entry.getKey()).getType();
779
780                 ByteBuffer formattedValue = null;
781
782                 if(colType.toString().toLowerCase().contains("blob"))
783                     formattedValue = MusicUtil.convertToActualDataType(colType, valueObj);
784
785                 valueString.append("?");
786
787                 queryObject.addValue(formattedValue);
788                 counter = counter + 1;
789                 fieldsString.append(",");
790                 valueString.append(",");
791             } 
792         }
793
794         if(primaryKey == null || primaryKey.length() <= 0) {
795             logger.error(EELFLoggerDelegate.errorLogger, "Some required partition key parts are missing: "+primaryKeyName );
796             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR).setError("Some required partition key parts are missing: "+primaryKeyName).toMap()).build();
797         }
798
799         fieldsString.replace(fieldsString.length()-1, fieldsString.length(), ")");
800         valueString.replace(valueString.length()-1, valueString.length(), ")");
801
802         queryObject.appendQueryString("INSERT INTO " + keyspace + "." + tablename + " "
803                         + fieldsString + " VALUES " + valueString);
804
805         String ttl = insObj.getTtl();
806         String timestamp = insObj.getTimestamp();
807
808         if ((ttl != null) && (timestamp != null)) {
809             logger.info(EELFLoggerDelegate.applicationLogger, "both there");
810             queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?");
811             queryObject.addValue(Integer.parseInt(ttl));
812             queryObject.addValue(Long.parseLong(timestamp));
813         }
814
815         if ((ttl != null) && (timestamp == null)) {
816             logger.info(EELFLoggerDelegate.applicationLogger, "ONLY TTL there");
817             queryObject.appendQueryString(" USING TTL ?");
818             queryObject.addValue(Integer.parseInt(ttl));
819         }
820
821         if ((ttl == null) && (timestamp != null)) {
822             logger.info(EELFLoggerDelegate.applicationLogger, "ONLY timestamp there");
823             queryObject.appendQueryString(" USING TIMESTAMP ?");
824             queryObject.addValue(Long.parseLong(timestamp));
825         }
826
827         queryObject.appendQueryString(";");
828
829         ReturnType result = null;
830         String consistency = insObj.getConsistencyInfo().get("type");
831         if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL) && insObj.getConsistencyInfo().get("consistency") != null) {
832             if(MusicUtil.isValidConsistency(insObj.getConsistencyInfo().get("consistency")))
833                 queryObject.setConsistency(insObj.getConsistencyInfo().get("consistency"));
834             else
835                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR).setError("Invalid Consistency type").toMap()).build();
836         }
837         queryObject.setOperation("insert");
838         try {
839             if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) {
840                 result = MusicCore.eventualPut(queryObject);
841             } else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
842                 String lockId = insObj.getConsistencyInfo().get("lockId");
843                 if(lockId == null) {
844                     logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or"
845                             + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR);
846                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock "
847                             + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build();
848                 }
849                 result = MusicCore.criticalPut(keyspace, tablename, primaryKey, queryObject, lockId,null);
850             } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
851
852                 result = MusicCore.atomicPut(keyspace, tablename, primaryKey, queryObject, null);
853
854             }
855         } catch (Exception ex) {
856             logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity
857                 .WARN, ErrorTypes.MUSICSERVICEERROR, ex);
858             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
859         }
860
861         if (result==null) {
862             logger.error(EELFLoggerDelegate.errorLogger,"Null result - Please Contact admin", AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR);
863             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Null result - Please Contact admin").toMap()).build();
864         }else if(result.getResult() == ResultType.FAILURE) {
865             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result.getResult()).setError(result.getMessage()).toMap()).build();
866         }
867         return response.status(Status.OK).entity(new JsonResponse(result.getResult()).setMessage("Insert Successful").toMap()).build();
868         } finally {
869             EELFLoggerDelegate.mdcRemove("keyspace");
870         }
871     }
872
873     /**
874      *
875      * @param insObj
876      * @param keyspace
877      * @param tablename
878      * @param info
879      * @return
880      * @throws MusicServiceException 
881      * @throws MusicQueryException 
882      * @throws Exception
883      */
884     @PUT
885     @Path("/{keyspace: .*}/tables/{tablename: .*}/rows")
886     @ApiOperation(value = "Update Table", response = String.class)
887     @Consumes(MediaType.APPLICATION_JSON)
888     @Produces(MediaType.APPLICATION_JSON)
889     public Response updateTable(
890                     @ApiParam(value = "Major Version",
891                                     required = true) @PathParam("version") String version,
892                     @ApiParam(value = "Minor Version",
893                                     required = false) @HeaderParam(XMINORVERSION) String minorVersion,
894                     @ApiParam(value = "Patch Version",
895                                     required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
896                     @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid,
897                     @ApiParam(value = "Application namespace",
898                                     required = true) @HeaderParam(NS) String ns,
899                     @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
900                     JsonUpdate updateObj,
901                     @ApiParam(value = "Keyspace Name",
902                                     required = true) @PathParam("keyspace") String keyspace,
903                     @ApiParam(value = "Table Name",
904                                     required = true) @PathParam("tablename") String tablename,
905                     @Context UriInfo info) throws MusicQueryException, MusicServiceException {
906         try {
907         ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
908         if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
909             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
910                     .setError("one or more path parameters are not set, please check and try again")
911                     .toMap()).build();
912         }
913         EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
914         if (!authenticator.authenticateUser(ns, authorization, keyspace, aid, Operation.UPDATE_TABLE)) {
915             return response.status(Status.UNAUTHORIZED)
916                     .entity(new JsonResponse(ResultType.FAILURE)
917                             .setError("Unauthorized: Please check username, password and make sure your app is onboarded")
918                             .toMap()).build();
919         }
920
921         long startTime = System.currentTimeMillis();
922         String operationId = UUID.randomUUID().toString();  // just for infoging
923                                                             // purposes.
924         String consistency = updateObj.getConsistencyInfo().get("type");
925
926         logger.info(EELFLoggerDelegate.applicationLogger, "--------------Music " + consistency
927                         + " update-" + operationId + "-------------------------");
928         // obtain the field value pairs of the update
929
930         PreparedQueryObject queryObject = new PreparedQueryObject();
931         Map<String, Object> valuesMap = updateObj.getValues();
932
933         TableMetadata tableInfo;
934         try {
935             tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename);
936         } catch (MusicServiceException e) {
937             logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
938                 .GENERALSERVICEERROR, e);
939               return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();
940         }
941         if (tableInfo == null) {
942             logger.error(EELFLoggerDelegate.errorLogger,"Table information not found. Please check input for table name= "+tablename, AppMessages.MISSINGINFO  ,ErrorSeverity.WARN, ErrorTypes.AUTHENTICATIONERROR);
943             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
944                 .setError("Table information not found. Please check input for table name= "
945                 + keyspace + "." + tablename).toMap()).build();
946         }
947         String vectorTs = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
948         StringBuilder fieldValueString = new StringBuilder("vector_ts=?,");
949         queryObject.addValue(vectorTs);
950         int counter = 0;
951         for (Map.Entry<String, Object> entry : valuesMap.entrySet()) {
952             Object valueObj = entry.getValue();
953             DataType colType = null;
954             try {
955                 colType = tableInfo.getColumn(entry.getKey()).getType();
956             } catch(NullPointerException ex) {
957                 logger.error(EELFLoggerDelegate.errorLogger, ex, "Invalid column name : "+entry.getKey(), ex);
958                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Invalid column name : "+entry.getKey()).toMap()).build();
959             }
960             Object valueString = null;
961             try {
962                 valueString = MusicUtil.convertToActualDataType(colType, valueObj);
963             } catch (Exception e) {
964               logger.error(EELFLoggerDelegate.errorLogger,e);
965             }
966             fieldValueString.append(entry.getKey() + "= ?");
967             queryObject.addValue(valueString);
968             if (counter != valuesMap.size() - 1)
969                 fieldValueString.append(",");
970             counter = counter + 1;
971         }
972         String ttl = updateObj.getTtl();
973         String timestamp = updateObj.getTimestamp();
974
975         queryObject.appendQueryString("UPDATE " + keyspace + "." + tablename + " ");
976         if ((ttl != null) && (timestamp != null)) {
977             logger.info("both there");
978             queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?");
979             queryObject.addValue(Integer.parseInt(ttl));
980             queryObject.addValue(Long.parseLong(timestamp));
981         }
982
983         if ((ttl != null) && (timestamp == null)) {
984             logger.info("ONLY TTL there");
985             queryObject.appendQueryString(" USING TTL ?");
986             queryObject.addValue(Integer.parseInt(ttl));
987         }
988
989         if ((ttl == null) && (timestamp != null)) {
990             logger.info("ONLY timestamp there");
991             queryObject.appendQueryString(" USING TIMESTAMP ?");
992             queryObject.addValue(Long.parseLong(timestamp));
993         }
994         // get the row specifier
995         RowIdentifier rowId = null;
996         try {
997             rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), queryObject);
998             if(rowId == null || rowId.primarKeyValue.isEmpty()) {
999                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1000                         .setError("Mandatory WHERE clause is missing. Please check the input request.").toMap()).build();
1001             }
1002         } catch (MusicServiceException ex) {
1003             logger.error(EELFLoggerDelegate.errorLogger,ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
1004                 .GENERALSERVICEERROR, ex);
1005               return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1006         }
1007
1008         queryObject.appendQueryString(
1009                         " SET " + fieldValueString + " WHERE " + rowId.rowIdString + ";");
1010
1011         // get the conditional, if any
1012         Condition conditionInfo;
1013         if (updateObj.getConditions() == null)
1014             conditionInfo = null;
1015         else {
1016             // to avoid parsing repeatedly, just send the select query to obtain row
1017             PreparedQueryObject selectQuery = new PreparedQueryObject();
1018             selectQuery.appendQueryString("SELECT *  FROM " + keyspace + "." + tablename + " WHERE "
1019                 + rowId.rowIdString + ";");
1020             selectQuery.addValue(rowId.primarKeyValue);
1021             conditionInfo = new Condition(updateObj.getConditions(), selectQuery);
1022         }
1023
1024         ReturnType operationResult = null;
1025         long jsonParseCompletionTime = System.currentTimeMillis();
1026
1027         if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL) && updateObj.getConsistencyInfo().get("consistency") != null) {
1028             if(MusicUtil.isValidConsistency(updateObj.getConsistencyInfo().get("consistency")))
1029                 queryObject.setConsistency(updateObj.getConsistencyInfo().get("consistency"));
1030             else
1031                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR).setError("Invalid Consistency type").toMap()).build();
1032         }
1033         queryObject.setOperation("update");
1034         if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL))
1035             operationResult = MusicCore.eventualPut(queryObject);
1036         else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
1037             String lockId = updateObj.getConsistencyInfo().get("lockId");
1038             if(lockId == null) {
1039                 logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or"
1040                         + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR);
1041                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock "
1042                         + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build();
1043             }
1044             operationResult = MusicCore.criticalPut(keyspace, tablename, rowId.primarKeyValue,
1045                             queryObject, lockId, conditionInfo);
1046         } else if (consistency.equalsIgnoreCase("atomic_delete_lock")) {
1047             // this function is mainly for the benchmarks
1048             try {
1049                 operationResult = MusicCore.atomicPutWithDeleteLock(keyspace, tablename,
1050                     rowId.primarKeyValue, queryObject, conditionInfo);
1051             } catch (MusicLockingException e) {
1052                 logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN,
1053                     ErrorTypes.GENERALSERVICEERROR, e);
1054                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();
1055             }
1056         } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
1057             try {
1058                 operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue,
1059                     queryObject, conditionInfo);
1060             } catch (MusicLockingException e) {
1061                 logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR, e);
1062                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();
1063             }
1064         }else if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL_NB)) {
1065             operationResult = MusicCore.eventualPut_nb(queryObject, keyspace, tablename, rowId.primarKeyValue);
1066         }
1067         long actualUpdateCompletionTime = System.currentTimeMillis();
1068
1069         long endTime = System.currentTimeMillis();
1070         String timingString = "Time taken in ms for Music " + consistency + " update-" + operationId
1071                         + ":" + "|total operation time:" + (endTime - startTime)
1072                         + "|json parsing time:" + (jsonParseCompletionTime - startTime)
1073                         + "|update time:" + (actualUpdateCompletionTime - jsonParseCompletionTime)
1074                         + "|";
1075
1076         if (operationResult != null && operationResult.getTimingInfo() != null) {
1077             String lockManagementTime = operationResult.getTimingInfo();
1078             timingString = timingString + lockManagementTime;
1079         }
1080         logger.info(EELFLoggerDelegate.applicationLogger, timingString);
1081
1082         if (operationResult==null) {
1083             logger.error(EELFLoggerDelegate.errorLogger,"Null result - Please Contact admin", AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR);
1084             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Null result - Please Contact admin").toMap()).build();
1085         }
1086         if ( operationResult.getResult() == ResultType.SUCCESS ) {
1087             return response.status(Status.OK).entity(new JsonResponse(operationResult.getResult()).setMessage(operationResult.getMessage()).toMap()).build();
1088         } else {
1089             logger.error(EELFLoggerDelegate.errorLogger,operationResult.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR);
1090             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(operationResult.getResult()).setError(operationResult.getMessage()).toMap()).build();
1091         }
1092         } finally {
1093             EELFLoggerDelegate.mdcRemove("keyspace");
1094         }
1095     }
1096
1097     /**
1098      *
1099      * @param delObj
1100      * @param keyspace
1101      * @param tablename
1102      * @param info
1103      * @return
1104      * @throws MusicServiceException 
1105      * @throws MusicQueryException 
1106      * @throws Exception
1107      */
1108     @DELETE
1109     @Path("/{keyspace: .*}/tables/{tablename: .*}/rows")
1110     @ApiOperation(value = "Delete From table", response = String.class)
1111     @Consumes(MediaType.APPLICATION_JSON)
1112     @Produces(MediaType.APPLICATION_JSON)
1113     public Response deleteFromTable(
1114                     @ApiParam(value = "Major Version",
1115                                     required = true) @PathParam("version") String version,
1116                     @ApiParam(value = "Minor Version",
1117                                     required = false) @HeaderParam(XMINORVERSION) String minorVersion,
1118                     @ApiParam(value = "Patch Version",
1119                                     required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
1120                     @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid,
1121                     @ApiParam(value = "Application namespace",
1122                                     required = true) @HeaderParam(NS) String ns,
1123                     @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
1124                     JsonDelete delObj,
1125                     @ApiParam(value = "Keyspace Name",
1126                                     required = true) @PathParam("keyspace") String keyspace,
1127                     @ApiParam(value = "Table Name",
1128                                     required = true) @PathParam("tablename") String tablename,
1129                     @Context UriInfo info) throws MusicQueryException, MusicServiceException {
1130         try {
1131         ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
1132         if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
1133             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1134                     .setError("one or more path parameters are not set, please check and try again")
1135                     .toMap()).build();
1136         }
1137         EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
1138         if (!authenticator.authenticateUser(ns, authorization, keyspace, aid, Operation.DELETE_FROM_TABLE)) {
1139             return response.status(Status.UNAUTHORIZED)
1140                     .entity(new JsonResponse(ResultType.FAILURE)
1141                             .setError("Unauthorized: Please check username, password and make sure your app is onboarded")
1142                             .toMap()).build();
1143         }
1144  
1145         if(delObj == null) {
1146             logger.error(EELFLoggerDelegate.errorLogger,"Required HTTP Request body is missing.", AppMessages.MISSINGDATA  ,ErrorSeverity.WARN, ErrorTypes.DATAERROR);
1147             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Required HTTP Request body is missing.").toMap()).build();
1148         }
1149         PreparedQueryObject queryObject = new PreparedQueryObject();
1150         StringBuilder columnString = new StringBuilder();
1151
1152         int counter = 0;
1153         List<String> columnList = delObj.getColumns();
1154         if (columnList != null) {
1155             for (String column : columnList) {
1156                 columnString.append(column);
1157                 if (counter != columnList.size() - 1)
1158                     columnString.append(",");
1159                 counter = counter + 1;
1160             }
1161         }
1162
1163         // get the row specifier
1164         RowIdentifier rowId = null;
1165         try {
1166             rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), queryObject);
1167         } catch (MusicServiceException ex) {
1168             logger.error(EELFLoggerDelegate.errorLogger,ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
1169                 .GENERALSERVICEERROR, ex);
1170               return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1171         }
1172         String rowSpec = rowId.rowIdString.toString();
1173
1174         if ((columnList != null) && (!rowSpec.isEmpty())) {
1175             queryObject.appendQueryString("DELETE " + columnString + " FROM " + keyspace + "."
1176                             + tablename + " WHERE " + rowSpec + ";");
1177         }
1178
1179         if ((columnList == null) && (!rowSpec.isEmpty())) {
1180             queryObject.appendQueryString("DELETE FROM " + keyspace + "." + tablename + " WHERE "
1181                             + rowSpec + ";");
1182         }
1183
1184         if ((columnList != null) && (rowSpec.isEmpty())) {
1185             queryObject.appendQueryString(
1186                             "DELETE " + columnString + " FROM " + keyspace + "." + rowSpec + ";");
1187         }
1188         // get the conditional, if any
1189         Condition conditionInfo;
1190         if (delObj.getConditions() == null) {
1191             conditionInfo = null;
1192         } else {
1193             // to avoid parsing repeatedly, just send the select query to
1194             // obtain row
1195             PreparedQueryObject selectQuery = new PreparedQueryObject();
1196             selectQuery.appendQueryString("SELECT *  FROM " + keyspace + "." + tablename + " WHERE "
1197                             + rowId.rowIdString + ";");
1198             selectQuery.addValue(rowId.primarKeyValue);
1199             conditionInfo = new Condition(delObj.getConditions(), selectQuery);
1200         }
1201
1202         String consistency = delObj.getConsistencyInfo().get("type");
1203
1204
1205         if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL) && delObj.getConsistencyInfo().get("consistency")!=null) {
1206
1207             if(MusicUtil.isValidConsistency(delObj.getConsistencyInfo().get("consistency")))
1208                 queryObject.setConsistency(delObj.getConsistencyInfo().get("consistency"));
1209             else
1210                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR).setError("Invalid Consistency type").toMap()).build();
1211         }
1212
1213         ReturnType operationResult = null;
1214         queryObject.setOperation("delete");
1215         try {
1216             if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL))
1217                 operationResult = MusicCore.eventualPut(queryObject);
1218             else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
1219                 String lockId = delObj.getConsistencyInfo().get("lockId");
1220                 if(lockId == null) {
1221                     logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or"
1222                             + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR);
1223                     return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock "
1224                             + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build();
1225                 }
1226                 operationResult = MusicCore.criticalPut(keyspace, tablename, rowId.primarKeyValue,
1227                                 queryObject, lockId, conditionInfo);
1228             } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
1229                     operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue,
1230                                     queryObject, conditionInfo);
1231             } else if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL_NB)) {
1232                 
1233                 operationResult = MusicCore.eventualPut_nb(queryObject, keyspace, tablename, rowId.primarKeyValue);
1234             }
1235         } catch (MusicLockingException e) {
1236             logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR, e);
1237             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1238                     .setError("Unable to perform Delete operation. Exception from music").toMap()).build();
1239         }
1240         if (operationResult==null) {
1241             logger.error(EELFLoggerDelegate.errorLogger,"Null result - Please Contact admin", AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR);
1242             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Null result - Please Contact admin").toMap()).build();
1243         }
1244         if (operationResult.getResult().equals(ResultType.SUCCESS)) {
1245             return response.status(Status.OK).entity(new JsonResponse(operationResult.getResult()).setMessage(operationResult.getMessage()).toMap()).build();
1246         } else {
1247             logger.error(EELFLoggerDelegate.errorLogger,operationResult.getMessage(), AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR);
1248             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(operationResult.getMessage()).toMap()).build();
1249         }
1250         } finally {
1251             EELFLoggerDelegate.mdcRemove("keyspace");
1252         }
1253     }
1254
1255     /**
1256      *
1257      * @param tabObj
1258      * @param keyspace
1259      * @param tablename
1260      * @throws Exception
1261      */
1262     @DELETE
1263     @Path("/{keyspace: .*}/tables/{tablename: .*}")
1264     @ApiOperation(value = "Drop Table", response = String.class)
1265     @Produces(MediaType.APPLICATION_JSON)
1266     public Response dropTable(
1267                     @ApiParam(value = "Major Version",
1268                                     required = true) @PathParam("version") String version,
1269                     @ApiParam(value = "Minor Version",
1270                                     required = false) @HeaderParam(XMINORVERSION) String minorVersion,
1271                     @ApiParam(value = "Patch Version",
1272                                     required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
1273                     @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid,
1274                     @ApiParam(value = "Application namespace",
1275                                     required = true) @HeaderParam(NS) String ns,
1276                     @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
1277                     @ApiParam(value = "Keyspace Name",
1278                                     required = true) @PathParam("keyspace") String keyspace,
1279                     @ApiParam(value = "Table Name",
1280                                     required = true) @PathParam("tablename") String tablename) throws Exception {
1281         try {
1282         ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
1283         if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
1284             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1285                 .setError("one or more path parameters are not set, please check and try again")
1286                 .toMap()).build();
1287         }
1288         EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
1289         if (!authenticator.authenticateUser(ns, authorization, keyspace, aid, Operation.DROP_TABLE)) {
1290             return response.status(Status.UNAUTHORIZED)
1291                     .entity(new JsonResponse(ResultType.FAILURE)
1292                             .setError("Unauthorized: Please check username, password and make sure your app is onboarded")
1293                             .toMap()).build();
1294         }
1295
1296         String consistency = "eventual";// for now this needs only eventual
1297                                         // consistency
1298         PreparedQueryObject query = new PreparedQueryObject();
1299         query.appendQueryString("DROP TABLE  " + keyspace + "." + tablename + ";");
1300         try {
1301             return response.status(Status.OK).entity(new JsonResponse(MusicCore.nonKeyRelatedPut(query, consistency)).toMap()).build();
1302         } catch (MusicServiceException ex) {
1303             logger.error(EELFLoggerDelegate.errorLogger, ex, AppMessages.MISSINGINFO  ,ErrorSeverity.WARN, ErrorTypes
1304                 .GENERALSERVICEERROR);
1305             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1306         }
1307         } finally {
1308             EELFLoggerDelegate.mdcRemove("keyspace");
1309         }
1310     }
1311
1312     /**
1313      *
1314      * @param selObj
1315      * @param keyspace
1316      * @param tablename
1317      * @param info
1318      * @return
1319      */
1320     @PUT
1321     @Path("/{keyspace: .*}/tables/{tablename: .*}/rows/criticalget")
1322     @ApiOperation(value = "Select Critical", response = Map.class)
1323     @Consumes(MediaType.APPLICATION_JSON)
1324     @Produces(MediaType.APPLICATION_JSON)
1325     public Response selectCritical(
1326                     @ApiParam(value = "Major Version",
1327                                     required = true) @PathParam("version") String version,
1328                     @ApiParam(value = "Minor Version",
1329                                     required = false) @HeaderParam(XMINORVERSION) String minorVersion,
1330                     @ApiParam(value = "Patch Version",
1331                                     required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
1332                     @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid,
1333                     @ApiParam(value = "Application namespace",
1334                                     required = true) @HeaderParam(NS) String ns,
1335                     @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
1336                     JsonInsert selObj,
1337                     @ApiParam(value = "Keyspace Name",
1338                                     required = true) @PathParam("keyspace") String keyspace,
1339                     @ApiParam(value = "Table Name",
1340                                     required = true) @PathParam("tablename") String tablename,
1341                     @Context UriInfo info) throws Exception {
1342         try {
1343         ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
1344         if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
1345             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1346                 .setError("one or more path parameters are not set, please check and try again")
1347                 .toMap()).build();
1348         }
1349         EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
1350         if (!authenticator.authenticateUser(ns, authorization, keyspace, aid, Operation.SELECT_CRITICAL)) {
1351             return response.status(Status.UNAUTHORIZED)
1352                     .entity(new JsonResponse(ResultType.FAILURE)
1353                             .setError("Unauthorized: Please check username, password and make sure your app is onboarded")
1354                             .toMap()).build();
1355         }
1356
1357         String lockId = selObj.getConsistencyInfo().get("lockId");
1358
1359         PreparedQueryObject queryObject = new PreparedQueryObject();
1360
1361         RowIdentifier rowId = null;
1362         try {
1363             rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), queryObject);
1364         } catch (MusicServiceException ex) {
1365             logger.error(EELFLoggerDelegate.errorLogger,ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
1366                 .GENERALSERVICEERROR, ex);
1367               return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1368         }
1369         queryObject.appendQueryString(
1370             "SELECT *  FROM " + keyspace + "." + tablename + " WHERE " + rowId.rowIdString + ";");
1371
1372         ResultSet results = null;
1373
1374         String consistency = selObj.getConsistencyInfo().get("type");
1375         try {
1376         if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
1377             if(lockId == null) {
1378                 logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or"
1379                     + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR);
1380                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock "
1381                     + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build();
1382             }
1383             results = MusicCore.criticalGet(keyspace, tablename, rowId.primarKeyValue, queryObject,lockId);
1384         } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) {
1385             results = MusicCore.atomicGet(keyspace, tablename, rowId.primarKeyValue, queryObject);
1386         }
1387         }catch(Exception ex) {
1388             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1389         }
1390         
1391         if(results!=null && results.getAvailableWithoutFetching() >0) {
1392             return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setDataResult(MusicDataStoreHandle.marshallResults(results)).toMap()).build();
1393         }
1394         return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setError("No data found").toMap()).build();
1395
1396         } finally {
1397             EELFLoggerDelegate.mdcRemove("keyspace");
1398         }
1399     }
1400
1401     /**
1402      *
1403      * @param keyspace
1404      * @param tablename
1405      * @param info
1406      * @return
1407      * @throws Exception
1408      */
1409     @GET
1410     @Path("/{keyspace: .*}/tables/{tablename: .*}/rows")
1411     @ApiOperation(value = "Select All or Select Specific", response = Map.class)
1412     @Produces(MediaType.APPLICATION_JSON)
1413     public Response select(
1414             @ApiParam(value = "Major Version",
1415                             required = true) @PathParam("version") String version,
1416             @ApiParam(value = "Minor Version",
1417                             required = false) @HeaderParam(XMINORVERSION) String minorVersion,
1418             @ApiParam(value = "Patch Version",
1419                             required = false) @HeaderParam(XPATCHVERSION) String patchVersion,
1420             @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid,
1421             @ApiParam(value = "Application namespace",
1422                             required = true) @HeaderParam(NS) String ns,
1423             @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization,
1424             @ApiParam(value = "Keyspace Name",
1425                             required = true) @PathParam("keyspace") String keyspace,
1426             @ApiParam(value = "Table Name",
1427                             required = true) @PathParam("tablename") String tablename,
1428             @Context UriInfo info) throws Exception {
1429         try { 
1430         ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion);
1431         if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){
1432             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
1433                 .setError("one or more path parameters are not set, please check and try again")
1434                 .toMap()).build();
1435         }
1436         EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) ");
1437         if (!authenticator.authenticateUser(ns, authorization, keyspace, aid, Operation.SELECT)) {
1438             return response.status(Status.UNAUTHORIZED)
1439                     .entity(new JsonResponse(ResultType.FAILURE)
1440                             .setError("Unauthorized: Please check username, password and make sure your app is onboarded")
1441                             .toMap()).build();
1442         }
1443
1444         PreparedQueryObject queryObject = new PreparedQueryObject();
1445
1446         if (info.getQueryParameters().isEmpty())// select all
1447             queryObject.appendQueryString("SELECT *  FROM " + keyspace + "." + tablename + ";");
1448         else {
1449             int limit = -1; // do not limit the number of results
1450             try {
1451                 queryObject = selectSpecificQuery(keyspace, tablename, info, limit);
1452             } catch (MusicServiceException ex) {
1453                 logger.error(EELFLoggerDelegate.errorLogger, ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN,
1454                     ErrorTypes.GENERALSERVICEERROR, ex);
1455                 return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1456             }
1457         }
1458
1459         try {
1460             ResultSet results = MusicCore.get(queryObject);
1461             if(results.getAvailableWithoutFetching() >0) {
1462                 return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setDataResult(MusicDataStoreHandle.marshallResults(results)).toMap()).build();
1463             }
1464             return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setDataResult(MusicDataStoreHandle.marshallResults(results)).setError("No data found").toMap()).build();
1465         } catch (MusicServiceException ex) {
1466             logger.error(EELFLoggerDelegate.errorLogger, ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.ERROR,
1467                 ErrorTypes.MUSICSERVICEERROR, ex);
1468             return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();
1469         }
1470         } finally {
1471             EELFLoggerDelegate.mdcRemove("keyspace");
1472         }
1473     }
1474
1475     /**
1476      *
1477      * @param keyspace
1478      * @param tablename
1479      * @param info
1480      * @param limit
1481      * @return
1482      * @throws MusicServiceException
1483      */
1484     public PreparedQueryObject selectSpecificQuery(String keyspace,
1485             String tablename, UriInfo info, int limit)
1486                     throws MusicServiceException {
1487
1488         PreparedQueryObject queryObject = new PreparedQueryObject();
1489         StringBuilder rowIdString = getRowIdentifier(keyspace, tablename, info.getQueryParameters(),
1490                         queryObject).rowIdString;
1491
1492         queryObject.appendQueryString(
1493                         "SELECT *  FROM " + keyspace + "." + tablename + " WHERE " + rowIdString);
1494
1495         if (limit != -1) {
1496             queryObject.appendQueryString(" LIMIT " + limit);
1497         }
1498
1499         queryObject.appendQueryString(";");
1500         return queryObject;
1501
1502     }
1503
1504     /**
1505      *
1506      * @param keyspace
1507      * @param tablename
1508      * @param rowParams
1509      * @param queryObject
1510      * @return
1511      * @throws MusicServiceException
1512      */
1513     private RowIdentifier getRowIdentifier(String keyspace, String tablename,
1514                     MultivaluedMap<String, String> rowParams, PreparedQueryObject queryObject)
1515                     throws MusicServiceException {
1516         StringBuilder rowSpec = new StringBuilder();
1517         int counter = 0;
1518         TableMetadata tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename);
1519         if (tableInfo == null) {
1520             logger.error(EELFLoggerDelegate.errorLogger,
1521                             "Table information not found. Please check input for table name= "
1522                                             + keyspace + "." + tablename);
1523             throw new MusicServiceException(
1524                             "Table information not found. Please check input for table name= "
1525                                             + keyspace + "." + tablename);
1526         }
1527         StringBuilder primaryKey = new StringBuilder();
1528         for (MultivaluedMap.Entry<String, List<String>> entry : rowParams.entrySet()) {
1529             String keyName = entry.getKey();
1530             List<String> valueList = entry.getValue();
1531             String indValue = valueList.get(0);
1532             DataType colType = null;
1533             Object formattedValue = null;
1534             try {
1535                 colType = tableInfo.getColumn(entry.getKey()).getType();
1536                 formattedValue = MusicUtil.convertToActualDataType(colType, indValue);
1537             } catch (Exception e) {
1538                 logger.error(EELFLoggerDelegate.errorLogger,e);
1539             }
1540             if(tableInfo.getPrimaryKey().get(0).getName().equals(entry.getKey()))
1541             primaryKey.append(indValue);
1542             rowSpec.append(keyName + "= ?");
1543             queryObject.addValue(formattedValue);
1544             if (counter != rowParams.size() - 1)
1545                 rowSpec.append(" AND ");
1546             counter = counter + 1;
1547         }
1548         return new RowIdentifier(primaryKey.toString(), rowSpec, queryObject);
1549     }
1550 }