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