Update junits
[music.git] / music-core / src / main / java / org / onap / music / datastore / jsonobjects / JsonUpdate.java
1 /*
2  * ============LICENSE_START==========================================
3  * org.onap.music
4  * ===================================================================
5  *  Copyright (c) 2017 AT&T Intellectual Property
6  *  Modifications Copyright (C) 2019 IBM 
7  * ===================================================================
8  *  Licensed under the Apache License, Version 2.0 (the "License");
9  *  you may not use this file except in compliance with the License.
10  *  You may obtain a copy of the License at
11  * 
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  * 
14  *  Unless required by applicable law or agreed to in writing, software
15  *  distributed under the License is distributed on an "AS IS" BASIS,
16  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  *  See the License for the specific language governing permissions and
18  *  limitations under the License.
19  * 
20  * ============LICENSE_END=============================================
21  * ====================================================================
22  */
23
24 package org.onap.music.datastore.jsonobjects;
25
26 import java.io.ByteArrayOutputStream;
27 import java.io.IOException;
28 import java.io.ObjectOutput;
29 import java.io.ObjectOutputStream;
30 import java.io.Serializable;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.UUID;
34
35 import javax.ws.rs.core.MultivaluedMap;
36 import javax.ws.rs.core.Response.Status;
37
38 import org.onap.music.datastore.Condition;
39 import org.onap.music.datastore.MusicDataStoreHandle;
40 import org.onap.music.datastore.PreparedQueryObject;
41 import org.onap.music.eelf.logging.EELFLoggerDelegate;
42 import org.onap.music.eelf.logging.format.AppMessages;
43 import org.onap.music.eelf.logging.format.ErrorSeverity;
44 import org.onap.music.eelf.logging.format.ErrorTypes;
45 import org.onap.music.exceptions.MusicQueryException;
46 import org.onap.music.exceptions.MusicServiceException;
47 import org.onap.music.main.MusicUtil;
48 import org.onap.music.main.ReturnType;
49
50 import com.datastax.driver.core.DataType;
51 import com.datastax.driver.core.TableMetadata;
52 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
53
54 import io.swagger.annotations.ApiModel;
55 import io.swagger.annotations.ApiModelProperty;
56
57 @ApiModel(value = "JsonTable", description = "Json model for table update")
58 @JsonIgnoreProperties(ignoreUnknown = true)
59 public class JsonUpdate implements Serializable {
60     private String keyspaceName;
61     private String tableName;
62     private transient Map<String, Object> values;
63     private String ttl;
64     private String timestamp;
65     private Map<String, String> consistencyInfo;
66     private transient Map<String, Object> conditions;
67     private transient Map<String, Object> rowSpecification;
68     private String rowIdString;
69     private String primarKeyValue;
70     private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(JsonUpdate.class);
71
72     @ApiModelProperty(value = "Conditions")
73     public Map<String, Object> getConditions() {
74         return conditions;
75     }
76
77     public void setConditions(Map<String, Object> conditions) {
78         this.conditions = conditions;
79     }
80
81     @ApiModelProperty(value = "Information for selecting sepcific rows")
82     public Map<String, Object> getRow_specification() {
83         return rowSpecification;
84     }
85
86     public void setRow_specification(Map<String, Object> rowSpecification) {
87         this.rowSpecification = rowSpecification;
88     }
89
90
91     @ApiModelProperty(value = "Keyspace name")
92     public String getKeyspaceName() {
93         return keyspaceName;
94     }
95
96     public void setKeyspaceName(String keyspaceName) {
97         this.keyspaceName = keyspaceName;
98     }
99
100     @ApiModelProperty(value = "Table name")
101     public String getTableName() {
102         return tableName;
103     }
104
105     public void setTableName(String tableName) {
106         this.tableName = tableName;
107     }
108
109     @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic")
110     public Map<String, String> getConsistencyInfo() {
111         return consistencyInfo;
112     }
113
114     public void setConsistencyInfo(Map<String, String> consistencyInfo) {
115         this.consistencyInfo = consistencyInfo;
116     }
117
118     @ApiModelProperty(value = "Time to live value")
119     public String getTtl() {
120         return ttl;
121     }
122
123     public void setTtl(String ttl) {
124         this.ttl = ttl;
125     }
126
127     @ApiModelProperty(value = "Time stamp")
128     public String getTimestamp() {
129         return timestamp;
130     }
131
132     public void setTimestamp(String timestamp) {
133         this.timestamp = timestamp;
134     }
135
136     @ApiModelProperty(value = "Column values")
137     public Map<String, Object> getValues() {
138         return values;
139     }
140
141     public void setValues(Map<String, Object> values) {
142         this.values = values;
143     }
144     
145     public String getRowIdString() {
146         return rowIdString;
147     }
148
149     public void setRowIdString(String rowIdString) {
150         this.rowIdString = rowIdString;
151     }
152
153     public String getPrimarKeyValue() {
154         return primarKeyValue;
155     }
156
157     public void setPrimarKeyValue(String primarKeyValue) {
158         this.primarKeyValue = primarKeyValue;
159     }
160
161     public byte[] serialize() {
162         ByteArrayOutputStream bos = new ByteArrayOutputStream();
163         ObjectOutput out = null;
164         try {
165             out = new ObjectOutputStream(bos);
166             out.writeObject(this);
167         } catch (IOException e) {
168             logger.error(EELFLoggerDelegate.errorLogger, e,AppMessages.IOERROR, ErrorSeverity.ERROR, ErrorTypes.DATAERROR);
169         }
170         return bos.toByteArray();
171     }
172     
173     /**
174      * Generate TableInsertQuery
175      * @return
176      * @throws MusicQueryException
177      */
178     public PreparedQueryObject genUpdatePreparedQueryObj(MultivaluedMap<String, String> rowParams) throws MusicQueryException {
179         PreparedQueryObject queryObject = new PreparedQueryObject();
180         
181          if((this.getKeyspaceName() == null || this.getKeyspaceName().isEmpty()) || 
182                  (this.getTableName() == null || this.getTableName().isEmpty())){
183              
184              /*return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
185                  .setError("one or more path parameters are not set, please check and try again")
186                  .toMap()).build();*/
187              
188              throw new MusicQueryException("one or more path parameters are not set, please check and try again",
189                      Status.BAD_REQUEST.getStatusCode());
190          }
191          
192          EELFLoggerDelegate.mdcPut("keyspace", "( "+this.getKeyspaceName()+" ) ");
193          long startTime = System.currentTimeMillis();
194          String operationId = UUID.randomUUID().toString();  // just for infoging purposes.
195          String consistency = this.getConsistencyInfo().get("type");
196
197          logger.info(EELFLoggerDelegate.applicationLogger, "--------------Music " + consistency
198              + " update-" + operationId + "-------------------------");
199          // obtain the field value pairs of the update
200          
201         Map<String, Object> valuesMap = this.getValues();
202
203         TableMetadata tableInfo = getColumnMetadata(this.getKeyspaceName(), this.getTableName());
204         
205         if (tableInfo == null) {
206             logger.error(EELFLoggerDelegate.errorLogger,"Table information not found. Please check input for table name= "+this.getTableName(), AppMessages.MISSINGINFO  ,ErrorSeverity.WARN, ErrorTypes.AUTHENTICATIONERROR);
207            
208             /*return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
209                 .setError("Table information not found. Please check input for table name= "
210                 + this.getKeyspaceName() + "." + this.getTableName()).toMap()).build();*/
211             
212             throw new MusicQueryException("Table information not found. Please check input for table name= "
213                     + this.getKeyspaceName() + "." + this.getTableName(), Status.BAD_REQUEST.getStatusCode());
214         }
215         
216         String vectorTs = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
217         StringBuilder fieldValueString = new StringBuilder("vector_ts=?,");
218         queryObject.addValue(vectorTs);
219         int counter = 0;
220         for (Map.Entry<String, Object> entry : valuesMap.entrySet()) {
221             Object valueObj = entry.getValue();
222             DataType colType = null;
223             try {
224                 colType = tableInfo.getColumn(entry.getKey()).getType();
225             } catch(NullPointerException ex) {
226                 logger.error(EELFLoggerDelegate.errorLogger, ex, "Invalid column name : "+entry.getKey(), ex);
227                 /*return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).
228                  * setError("Invalid column name : "+entry.getKey()).toMap()).build();*/
229                 
230                 throw new MusicQueryException("Invalid column name : " + entry.getKey(),Status.BAD_REQUEST.getStatusCode());
231             }
232             Object valueString = null;
233             try {
234                 valueString = MusicUtil.convertToActualDataType(colType, valueObj);
235             } catch (Exception e) {
236                 logger.error(EELFLoggerDelegate.errorLogger,e);
237             }
238             fieldValueString.append(entry.getKey() + "= ?");
239             queryObject.addValue(valueString);
240             if (counter != valuesMap.size() - 1) {
241                 fieldValueString.append(",");
242             }    
243             counter = counter + 1;
244         }
245         String ttl = this.getTtl();
246         String timestamp = this.getTimestamp();
247
248         queryObject.appendQueryString("UPDATE " + this.getKeyspaceName() + "." + this.getTableName() + " ");
249         if ((ttl != null) && (timestamp != null)) {
250             logger.info("both there");
251             queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?");
252             queryObject.addValue(Integer.parseInt(ttl));
253             queryObject.addValue(Long.parseLong(timestamp));
254         }
255
256         if ((ttl != null) && (timestamp == null)) {
257             logger.info("ONLY TTL there");
258             queryObject.appendQueryString(" USING TTL ?");
259             queryObject.addValue(Integer.parseInt(ttl));
260         }
261
262         if ((ttl == null) && (timestamp != null)) {
263             logger.info("ONLY timestamp there");
264             queryObject.appendQueryString(" USING TIMESTAMP ?");
265             queryObject.addValue(Long.parseLong(timestamp));
266         }
267         
268         // get the row specifier
269         RowIdentifier rowId = null;
270         try {
271             rowId = getRowIdentifier(this.getKeyspaceName(), this.getTableName(), rowParams, queryObject);
272             this.setRowIdString(rowId.rowIdString);
273             this.setPrimarKeyValue(rowId.primarKeyValue);
274             if(rowId == null || rowId.getPrimaryKeyValue().isEmpty()) {
275                 /*return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
276                         .setError("Mandatory WHERE clause is missing. Please check the input request.").toMap()).build();*/
277                 
278                 throw new MusicQueryException("Mandatory WHERE clause is missing. Please check the input request.", 
279                         Status.BAD_REQUEST.getStatusCode());
280             }
281         } catch (MusicQueryException ex) {
282              throw new MusicQueryException("Mandatory WHERE clause is missing. Please check the input request.", 
283                      Status.BAD_REQUEST.getStatusCode());
284             
285         }catch (MusicServiceException ex) {
286             logger.error(EELFLoggerDelegate.errorLogger,ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
287                 .GENERALSERVICEERROR, ex);
288             /*return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();*/
289             
290             throw new MusicQueryException(ex.getMessage(), Status.BAD_REQUEST.getStatusCode());
291             
292         }
293         
294         
295
296         queryObject.appendQueryString(
297             " SET " + fieldValueString + " WHERE " + rowId.getRowIdString() + ";");
298             
299         
300
301         // get the conditional, if any
302         Condition conditionInfo;
303         if (this.getConditions() == null) {
304             conditionInfo = null;
305         } else {
306             // to avoid parsing repeatedly, just send the select query to obtain row
307             PreparedQueryObject selectQuery = new PreparedQueryObject();
308             selectQuery.appendQueryString("SELECT *  FROM " + this.getKeyspaceName() + "." + this.getTableName() + " WHERE "
309                 + rowId.getRowIdString() + ";");
310             selectQuery.addValue(rowId.primarKeyValue);
311             conditionInfo = new Condition(this.getConditions(), selectQuery);
312         }
313
314         ReturnType operationResult = null;
315         long jsonParseCompletionTime = System.currentTimeMillis();
316
317         if(consistency.equalsIgnoreCase(MusicUtil.EVENTUAL) && this.getConsistencyInfo().get("consistency") != null) {
318             if(MusicUtil.isValidConsistency(this.getConsistencyInfo().get("consistency"))) {
319                 queryObject.setConsistency(this.getConsistencyInfo().get("consistency"));
320             } else {
321                 /*return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR)
322                         .setError("Invalid Consistency type").toMap()).build();*/
323                 
324                 logger.error("Invalid Consistency type");
325                 throw new MusicQueryException("Invalid Consistency type", Status.BAD_REQUEST.getStatusCode());
326             }
327         }
328         
329         queryObject.setOperation("update");
330         
331         return queryObject;
332     }
333
334     TableMetadata getColumnMetadata(String keyspaceName, String tableName) throws MusicQueryException {
335         TableMetadata tableInfo;
336         try {
337             tableInfo = returnColumnMetadata(keyspaceName, tableName);
338         } catch (MusicServiceException e) {
339             logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
340                 .GENERALSERVICEERROR, e);
341             /*return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();*/
342             throw new MusicQueryException(e.getMessage(), Status.BAD_REQUEST.getStatusCode());
343         }catch (Exception e) {
344             logger.error(EELFLoggerDelegate.errorLogger, e, AppMessages.UNKNOWNERROR, ErrorSeverity.CRITICAL,
345                     ErrorTypes.GENERALSERVICEERROR);
346             throw new MusicQueryException(e.getMessage(), Status.BAD_REQUEST.getStatusCode());
347         }
348         return tableInfo;
349     }
350     
351     /** wrapper around static method for testing */
352     TableMetadata returnColumnMetadata(String keyspace, String tableName) throws MusicServiceException {
353         return MusicDataStoreHandle.returnColumnMetadata(keyspace, tableName);
354     }
355     
356     class RowIdentifier {
357         private String primarKeyValue;
358         private String rowIdString;
359         @SuppressWarnings("unused")
360         public PreparedQueryObject queryObject; // the string with all the row
361                                                 // identifiers separated by AND
362
363         public RowIdentifier(String primaryKeyValue, String rowIdString,
364                         PreparedQueryObject queryObject) {
365             this.primarKeyValue = primaryKeyValue;
366             this.rowIdString = rowIdString;
367             this.queryObject = queryObject;
368         }
369
370         public String getPrimaryKeyValue() {
371             return this.primarKeyValue;
372         }
373
374         public void setPrimaryKeyValue(String primaryKeyValue) {
375             this.primarKeyValue = primaryKeyValue;
376         }
377
378         public String getRowIdString() {
379             return this.rowIdString.toString();
380         }
381
382         public void setRowIdString(String rowIdString) {
383             this.rowIdString = rowIdString;
384         }
385
386         public PreparedQueryObject getQueryObject() {
387             return this.queryObject;
388         }
389     }
390     
391     /**
392     *
393     * @param keyspace
394     * @param tablename
395     * @param rowParams
396     * @param queryObject
397     * @return
398     * @throws MusicServiceException
399     */
400     RowIdentifier getRowIdentifier(String keyspace, String tablename,
401        MultivaluedMap<String, String> rowParams, PreparedQueryObject queryObject)
402        throws MusicServiceException {
403        StringBuilder rowSpec = new StringBuilder();
404        int counter = 0;
405        TableMetadata tableInfo = returnColumnMetadata(keyspace, tablename);
406        if (tableInfo == null) {
407            logger.error(EELFLoggerDelegate.errorLogger,
408                "Table information not found. Please check input for table name= "
409                + keyspace + "." + tablename);
410            throw new MusicServiceException(
411                "Table information not found. Please check input for table name= "
412                + keyspace + "." + tablename);
413        }
414        StringBuilder primaryKey = new StringBuilder();
415        for (MultivaluedMap.Entry<String, List<String>> entry : rowParams.entrySet()) {
416            String keyName = entry.getKey();
417            List<String> valueList = entry.getValue();
418            String indValue = valueList.get(0);
419            DataType colType = null;
420            Object formattedValue = null;
421            try {
422                colType = tableInfo.getColumn(entry.getKey()).getType();
423                formattedValue = MusicUtil.convertToActualDataType(colType, indValue);
424            } catch (Exception e) {
425                logger.error(EELFLoggerDelegate.errorLogger,e);
426            }
427            if(tableInfo.getPrimaryKey().get(0).getName().equals(entry.getKey())) {
428                primaryKey.append(indValue);
429            }
430            rowSpec.append(keyName + "= ?");
431            queryObject.addValue(formattedValue);
432            if (counter != rowParams.size() - 1) {
433                rowSpec.append(" AND ");
434            }
435            counter = counter + 1;
436        }
437        return new RowIdentifier(primaryKey.toString(), rowSpec.toString(), queryObject);
438     }
439
440 }