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