MUSIC-ORM-Implemetation
[music.git] / src / main / java / org / onap / music / datastore / jsonobjects / JsonInsert.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.nio.ByteBuffer;
32 import java.util.List;
33 import java.util.Map;
34
35 import javax.ws.rs.core.MultivaluedMap;
36 import javax.ws.rs.core.Response.Status;
37
38 import org.onap.music.datastore.MusicDataStoreHandle;
39 import org.onap.music.datastore.PreparedQueryObject;
40 import org.onap.music.eelf.logging.EELFLoggerDelegate;
41 import org.onap.music.eelf.logging.format.AppMessages;
42 import org.onap.music.eelf.logging.format.ErrorSeverity;
43 import org.onap.music.eelf.logging.format.ErrorTypes;
44 import org.onap.music.exceptions.MusicQueryException;
45 import org.onap.music.exceptions.MusicServiceException;
46 import org.onap.music.main.MusicUtil;
47 import org.onap.music.main.ReturnType;
48
49 import com.datastax.driver.core.DataType;
50 import com.datastax.driver.core.TableMetadata;
51 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
52
53 import io.swagger.annotations.ApiModel;
54 import io.swagger.annotations.ApiModelProperty;
55
56 @ApiModel(value = "InsertTable", description = "Json model for table vlaues insert")
57 @JsonIgnoreProperties(ignoreUnknown = true)
58 public class JsonInsert implements Serializable {
59     private static final long serialVersionUID = 1L;
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 transient Map<String, Object> rowSpecification;
66     private Map<String, String> consistencyInfo;
67     private Map<String, byte[]> objectMap;
68     private String primaryKeyVal;
69     private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(JsonInsert.class);
70
71     @ApiModelProperty(value = "objectMap",hidden = true)
72     public Map<String, byte[]> getObjectMap() {
73         return objectMap;
74     }
75     
76     public void setObjectMap(Map<String, byte[]> objectMap) {
77         this.objectMap = objectMap;
78     }
79     
80     @ApiModelProperty(value = "keyspace")
81     public String getKeyspaceName() {
82         return keyspaceName;
83     }
84
85     public void setKeyspaceName(String keyspaceName) {
86         this.keyspaceName = keyspaceName;
87     }
88
89     @ApiModelProperty(value = "Table name")
90     public String getTableName() {
91         return tableName;
92     }
93
94     public void setTableName(String tableName) {
95         this.tableName = tableName;
96     }
97
98     @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic")
99     public Map<String, String> getConsistencyInfo() {
100         return consistencyInfo;
101     }
102
103     public void setConsistencyInfo(Map<String, String> consistencyInfo) {
104         this.consistencyInfo = consistencyInfo;
105     }
106
107     @ApiModelProperty(value = "Columns and tables support an optional "
108         + "expiration period called TTL (time-to-live) in seconds.",
109         notes="TTL precision is one second, which is calculated by the coordinator "
110         + "node. When using TTL, ensure that all nodes in the cluster have synchronized clocks.",allowEmptyValue = true)
111     public String getTtl() {
112         return ttl;
113     }
114
115     public void setTtl(String ttl) {
116         this.ttl = ttl;
117     }
118
119     @ApiModelProperty(value = "Time stamp (epoch_in_microseconds)",
120         notes = "Marks inserted data (write time) with TIMESTAMP. "
121         + "Enter the time since epoch (January 1, 1970) in microseconds."
122         + "By default, the actual time of write is used.", allowEmptyValue = true)
123     public String getTimestamp() {
124         return timestamp;
125     }
126
127     public void setTimestamp(String timestamp) {
128         this.timestamp = timestamp;
129     }
130
131     @ApiModelProperty(value = "Json Object of key/values", notes="Where key is the column name and value is the data value for that column.",
132         example = "{'emp_id': 'df98a3d40cd6','emp_name': 'john',"
133         + "'emp_salary': 50,'address':{'street' : '1 Some way','city' : 'New York'}}")
134     public Map<String, Object> getValues() {
135         return values;
136     }
137
138     public void setValues(Map<String, Object> values) {
139         this.values = values;
140     }
141
142     @ApiModelProperty(value = "Information for selecting specific rows for insert",hidden = true)
143     public Map<String, Object> getRowSpecification() {
144         return rowSpecification;
145     }
146
147     public void setRowSpecification(Map<String, Object> rowSpecification) {
148         this.rowSpecification = rowSpecification;
149     }
150     
151     public String getPrimaryKeyVal() {
152         return primaryKeyVal;
153     }
154
155     public void setPrimaryKeyVal(String primaryKeyVal) {
156         this.primaryKeyVal = primaryKeyVal;
157     }
158
159     public byte[] serialize() {
160         ByteArrayOutputStream bos = new ByteArrayOutputStream();
161         ObjectOutput out = null;
162         try {
163             out = new ObjectOutputStream(bos);
164             out.writeObject(this);
165         } catch (IOException e) {
166             logger.error(EELFLoggerDelegate.errorLogger, e, AppMessages.IOERROR, ErrorSeverity.ERROR, ErrorTypes.DATAERROR);
167         }
168         return bos.toByteArray();
169     }
170     
171     /**
172      * Generate TableInsertQuery
173      * @return
174      * @throws MusicQueryException
175      */
176     public PreparedQueryObject genInsertPreparedQueryObj() throws MusicQueryException {
177         if (logger.isDebugEnabled()) {
178             logger.debug("Coming inside genTableInsertQuery method " + this.getKeyspaceName());
179             logger.debug("Coming inside genTableInsertQuery method " + this.getTableName());
180         }
181
182         PreparedQueryObject queryObject = new PreparedQueryObject();
183         TableMetadata tableInfo = null;
184         try {
185             tableInfo = MusicDataStoreHandle.returnColumnMetadata(this.getKeyspaceName(), this.getTableName());
186             if(tableInfo == null) {
187                 //return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Table name doesn't exists. Please check the table name.").toMap()).build();
188                 throw new MusicQueryException("Table name doesn't exists. Please check the table name.",
189                         Status.BAD_REQUEST.getStatusCode());
190             }
191         } catch (MusicServiceException e) {
192             logger.error(EELFLoggerDelegate.errorLogger, e, AppMessages.UNKNOWNERROR  ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR);
193             //return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build();
194             throw new MusicQueryException(e.getMessage(),Status.BAD_REQUEST.getStatusCode());
195             
196         }
197         String primaryKeyName = tableInfo.getPrimaryKey().get(0).getName();
198         StringBuilder fieldsString = new StringBuilder("(vector_ts,");
199         String vectorTs =
200                         String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
201         StringBuilder valueString = new StringBuilder("(" + "?" + ",");
202         queryObject.addValue(vectorTs);
203         
204         Map<String, Object> valuesMap = this.getValues();
205         if (valuesMap==null) {
206             //return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
207                     //.setError("Nothing to insert. No values provided in request.").toMap()).build();
208             throw new MusicQueryException("Nothing to insert. No values provided in request.",
209                     Status.BAD_REQUEST.getStatusCode());
210         }
211         int counter = 0;
212         String primaryKey = "";
213         for (Map.Entry<String, Object> entry : valuesMap.entrySet()) {
214             fieldsString.append("" + entry.getKey());
215             Object valueObj = entry.getValue();
216             if (primaryKeyName.equals(entry.getKey())) {
217                 primaryKey = entry.getValue() + "";
218                 primaryKey = primaryKey.replace("'", "''");
219             }
220             DataType colType = null;
221             try {
222                 colType = tableInfo.getColumn(entry.getKey()).getType();
223             } catch(NullPointerException ex) {
224                 logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage() +" Invalid column name : "+entry.getKey
225                     (), AppMessages.INCORRECTDATA  ,ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR, ex);
226                 //return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Invalid column name : "+entry.getKey()).toMap()).build();
227                 throw new MusicQueryException("Invalid column name : " + entry.getKey(),
228                         Status.BAD_REQUEST.getStatusCode());
229             }
230
231             Object formattedValue = null;
232             try {
233                 formattedValue = MusicUtil.convertToActualDataType(colType, valueObj);
234             } catch (Exception e) {
235                 logger.error(EELFLoggerDelegate.errorLogger,e);
236             }
237             valueString.append("?");
238
239             queryObject.addValue(formattedValue);
240
241             if (counter == valuesMap.size() - 1) {
242                 fieldsString.append(")");
243                 valueString.append(")");
244             } else {
245                 fieldsString.append(",");
246                 valueString.append(",");
247             }
248             counter = counter + 1;
249         }
250
251         //blobs..
252         Map<String, byte[]> objectMap = this.getObjectMap();
253         if(objectMap != null) {
254             for (Map.Entry<String, byte[]> entry : objectMap.entrySet()) {
255                 if(counter > 0) {
256                     fieldsString.replace(fieldsString.length()-1, fieldsString.length(), ",");
257                     valueString.replace(valueString.length()-1, valueString.length(), ",");
258                 }
259                 fieldsString.append("" + entry.getKey());
260                 byte[] valueObj = entry.getValue();
261                 if (primaryKeyName.equals(entry.getKey())) {
262                     primaryKey = entry.getValue() + "";
263                     primaryKey = primaryKey.replace("'", "''");
264                 }
265                 DataType colType = tableInfo.getColumn(entry.getKey()).getType();
266                 ByteBuffer formattedValue = null;
267                 if(colType.toString().toLowerCase().contains("blob")) {
268                     formattedValue = MusicUtil.convertToActualDataType(colType, valueObj);
269                 }
270                 valueString.append("?");
271                 queryObject.addValue(formattedValue);
272                 counter = counter + 1;
273                 fieldsString.append(",");
274                 valueString.append(",");
275             } 
276         }
277         this.setPrimaryKeyVal(primaryKey);
278         if(primaryKey == null || primaryKey.length() <= 0) {
279             logger.error(EELFLoggerDelegate.errorLogger, "Some required partition key parts are missing: "+primaryKeyName );
280             //return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.SYNTAXERROR).setError("Some required partition key parts are missing: "+primaryKeyName).toMap()).build();
281             throw new MusicQueryException("Some required partition key parts are missing: " + primaryKeyName,
282                     Status.BAD_REQUEST.getStatusCode());
283         }
284
285         fieldsString.replace(fieldsString.length()-1, fieldsString.length(), ")");
286         valueString.replace(valueString.length()-1, valueString.length(), ")");
287
288         queryObject.appendQueryString("INSERT INTO " + this.getKeyspaceName() + "." + this.getTableName() + " "
289                         + fieldsString + " VALUES " + valueString);
290
291         String ttl = this.getTtl();
292         String timestamp = this.getTimestamp();
293
294         if ((ttl != null) && (timestamp != null)) {
295             logger.info(EELFLoggerDelegate.applicationLogger, "both there");
296             queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?");
297             queryObject.addValue(Integer.parseInt(ttl));
298             queryObject.addValue(Long.parseLong(timestamp));
299         }
300
301         if ((ttl != null) && (timestamp == null)) {
302             logger.info(EELFLoggerDelegate.applicationLogger, "ONLY TTL there");
303             queryObject.appendQueryString(" USING TTL ?");
304             queryObject.addValue(Integer.parseInt(ttl));
305         }
306
307         if ((ttl == null) && (timestamp != null)) {
308             logger.info(EELFLoggerDelegate.applicationLogger, "ONLY timestamp there");
309             queryObject.appendQueryString(" USING TIMESTAMP ?");
310             queryObject.addValue(Long.parseLong(timestamp));
311         }
312
313         queryObject.appendQueryString(";");
314
315         ReturnType result = null;
316         String consistency = this.getConsistencyInfo().get("type");
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).setError("Invalid Consistency type").toMap()).build();
322                 throw new MusicQueryException("Invalid Consistency type", Status.BAD_REQUEST.getStatusCode());
323             }
324         }
325         queryObject.setOperation("insert");
326
327         logger.info("Data insert Query ::::: " + queryObject.getQuery());
328
329         return queryObject;
330     }
331     
332     /**
333      * 
334      * @param rowParams
335      * @return
336      * @throws MusicQueryException
337      */
338     public PreparedQueryObject genSelectCriticalPreparedQueryObj(MultivaluedMap<String, String> rowParams) throws MusicQueryException {
339         
340         PreparedQueryObject queryObject = new PreparedQueryObject();
341         
342         if((this.getKeyspaceName() == null || this.getKeyspaceName().isEmpty()) 
343                 || (this.getTableName() == null || this.getTableName().isEmpty())){
344            /* return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE)
345                 .setError("one or more path parameters are not set, please check and try again")
346                 .toMap()).build();*/
347             throw new MusicQueryException("one or more path parameters are not set, please check and try again",
348                      Status.BAD_REQUEST.getStatusCode());
349         }
350         EELFLoggerDelegate.mdcPut("keyspace", "( "+this.getKeyspaceName()+" ) ");
351         RowIdentifier rowId = null;
352         try {
353             rowId = getRowIdentifier(this.getKeyspaceName(), this.getTableName(), rowParams, queryObject);
354             this.setPrimaryKeyVal(rowId.primarKeyValue);
355         } catch (MusicServiceException ex) {
356             logger.error(EELFLoggerDelegate.errorLogger,ex, AppMessages.UNKNOWNERROR  ,ErrorSeverity.WARN, ErrorTypes
357                 .GENERALSERVICEERROR, ex);
358             /*return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build();*/
359             throw new MusicQueryException(ex.getMessage(), Status.BAD_REQUEST.getStatusCode());
360         }
361         
362         queryObject.appendQueryString(
363             "SELECT *  FROM " + this.getKeyspaceName() + "." + this.getTableName() + " WHERE " + rowId.rowIdString + ";");
364         
365         return queryObject;
366     }
367     
368     private class RowIdentifier {
369         public String primarKeyValue;
370         public StringBuilder rowIdString;
371         @SuppressWarnings("unused")
372         public PreparedQueryObject queryObject; // the string with all the row
373                                                 // identifiers separated by AND
374
375         public RowIdentifier(String primaryKeyValue, StringBuilder rowIdString,
376                         PreparedQueryObject queryObject) {
377             this.primarKeyValue = primaryKeyValue;
378             this.rowIdString = rowIdString;
379             this.queryObject = queryObject;
380         }
381     }
382     
383     /**
384     *
385     * @param keyspace
386     * @param tablename
387     * @param rowParams
388     * @param queryObject
389     * @return
390     * @throws MusicServiceException
391     */
392    private RowIdentifier getRowIdentifier(String keyspace, String tablename,
393        MultivaluedMap<String, String> rowParams, PreparedQueryObject queryObject)
394        throws MusicServiceException {
395        StringBuilder rowSpec = new StringBuilder();
396        int counter = 0;
397        TableMetadata tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename);
398        if (tableInfo == null) {
399            logger.error(EELFLoggerDelegate.errorLogger,
400                "Table information not found. Please check input for table name= "
401                + keyspace + "." + tablename);
402            throw new MusicServiceException(
403                "Table information not found. Please check input for table name= "
404                + keyspace + "." + tablename);
405        }
406        StringBuilder primaryKey = new StringBuilder();
407        for (MultivaluedMap.Entry<String, List<String>> entry : rowParams.entrySet()) {
408            String keyName = entry.getKey();
409            List<String> valueList = entry.getValue();
410            String indValue = valueList.get(0);
411            DataType colType = null;
412            Object formattedValue = null;
413            try {
414                colType = tableInfo.getColumn(entry.getKey()).getType();
415                formattedValue = MusicUtil.convertToActualDataType(colType, indValue);
416            } catch (Exception e) {
417                logger.error(EELFLoggerDelegate.errorLogger,e);
418            }
419            if(tableInfo.getPrimaryKey().get(0).getName().equals(entry.getKey())) {
420                primaryKey.append(indValue);
421            }
422            rowSpec.append(keyName + "= ?");
423            queryObject.addValue(formattedValue);
424            if (counter != rowParams.size() - 1) {
425                rowSpec.append(" AND ");
426            }
427            counter = counter + 1;
428        }
429        return new RowIdentifier(primaryKey.toString(), rowSpec, queryObject);
430     }
431
432
433 }