ef560144058cf5045d07dedbe2868061d4b0e6e5
[music.git] / music-core / src / main / java / org / onap / music / datastore / jsonobjects / JsonTable.java
1 /*
2  * ============LICENSE_START==========================================
3  * org.onap.music
4  * ===================================================================
5  *  Copyright (c) 2017 AT&T Intellectual Property
6  * ===================================================================
7  *  Modifications Copyright (C) 2019 IBM 
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.datastore.jsonobjects;
26
27 import java.util.Map;
28
29 import javax.ws.rs.core.Response.Status;
30
31 import org.apache.commons.lang3.StringUtils;
32 import org.onap.music.datastore.PreparedQueryObject;
33 import org.onap.music.eelf.logging.EELFLoggerDelegate;
34 import org.onap.music.exceptions.MusicQueryException;
35 import org.onap.music.main.MusicUtil;
36
37 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
38
39 import io.swagger.annotations.ApiModel;
40 import io.swagger.annotations.ApiModelProperty;
41
42 @ApiModel(value = "JsonTable", description = "Defines the Json for Creating a new Table.")
43 @JsonIgnoreProperties(ignoreUnknown = true)
44 public class JsonTable {
45     private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(JsonTable.class);
46     
47     private String keyspaceName;
48     private String tableName;
49
50     private Map<String, String> fields;
51     private Map<String, Object> properties;
52     private String primaryKey;
53     private String partitionKey;
54     private String clusteringKey;
55     private String filteringKey;
56     private String clusteringOrder;
57     private Map<String, String> consistencyInfo;
58
59     @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic")
60     public Map<String, String> getConsistencyInfo() {
61         return consistencyInfo;
62     }
63
64     public void setConsistencyInfo(Map<String, String> consistencyInfo) {
65         this.consistencyInfo = consistencyInfo;
66     }
67
68     @ApiModelProperty(value = "Properties")
69     public Map<String, Object> getProperties() {
70         return properties;
71     }
72
73     public void setProperties(Map<String, Object> properties) {
74         this.properties = properties;
75     }
76
77     @ApiModelProperty(value = "Fields")
78     public Map<String, String> getFields() {
79         return fields;
80     }
81
82     public void setFields(Map<String, String> fields) {
83         this.fields = fields;
84     }
85
86     @ApiModelProperty(value = "KeySpace Name")
87     public String getKeyspaceName() {
88         return keyspaceName;
89     }
90
91     public void setKeyspaceName(String keyspaceName) {
92         this.keyspaceName = keyspaceName;
93     }
94
95     @ApiModelProperty(value = "Table Name")
96     public String getTableName() {
97         return tableName;
98     }
99
100     public void setTableName(String tableName) {
101         this.tableName = tableName;
102     }
103
104     @ApiModelProperty(value = "Clustering Order", notes = "")
105     public String getClusteringOrder() {
106         return clusteringOrder;
107     }
108
109     public void setClusteringOrder(String clusteringOrder) {
110         this.clusteringOrder = clusteringOrder;
111     }
112
113     @ApiModelProperty(value = "Primary Key")
114     public String getPrimaryKey() {
115         return primaryKey;
116     }
117
118     public void setPrimaryKey(String primaryKey) {
119         this.primaryKey = primaryKey;
120     }
121
122     public String getClusteringKey() {
123         return clusteringKey;
124     }
125
126     public void setClusteringKey(String clusteringKey) {
127         this.clusteringKey = clusteringKey;
128     }
129
130     public String getFilteringKey() {
131         return filteringKey;
132     }
133
134     public void setFilteringKey(String filteringKey) {
135         this.filteringKey = filteringKey;
136     }
137
138     public String getPartitionKey() {
139         return partitionKey;
140     }
141
142     public void setPartitionKey(String partitionKey) {
143         this.partitionKey = partitionKey;
144     }
145     
146     public PreparedQueryObject genCreateTableQuery()  throws MusicQueryException {
147         if (logger.isDebugEnabled()) {
148             logger.debug("Coming inside genCreateTableQuery method " + this.getKeyspaceName());
149             logger.debug("Coming inside genCreateTableQuery method " + this.getTableName());
150         }
151
152         String primaryKey = null;
153         String partitionKey = this.getPartitionKey();
154         String clusterKey = this.getClusteringKey();
155         String filteringKey = this.getFilteringKey();
156         if (filteringKey != null) {
157             clusterKey = clusterKey + "," + filteringKey;
158         }
159         primaryKey = this.getPrimaryKey(); // get primaryKey if available
160
161         PreparedQueryObject queryObject = new PreparedQueryObject();
162         // first read the information about the table fields
163         Map<String, String> fields = this.getFields();
164         if (fields == null) {
165            throw new MusicQueryException(
166                     "Create Table Error: No fields in request", Status.BAD_REQUEST.getStatusCode());
167         }
168         StringBuilder fieldsString = new StringBuilder("(vector_ts text,");
169         int counter = 0;
170         for (Map.Entry<String, String> entry : fields.entrySet()) {
171             if (entry.getKey().equals("PRIMARY KEY")) {
172                 primaryKey = entry.getValue(); // replaces primaryKey
173                 primaryKey = primaryKey.trim();
174             } else {
175                 if (counter == 0 )  fieldsString.append("" + entry.getKey() + " " + entry.getValue() + "");
176                 else fieldsString.append("," + entry.getKey() + " " + entry.getValue() + "");
177             }
178
179             if (counter != (fields.size() - 1) ) {
180                 counter = counter + 1; 
181             } else {
182         
183                 if((primaryKey != null) && (partitionKey == null)) {
184                     primaryKey = primaryKey.trim();
185                     int count1 = StringUtils.countMatches(primaryKey, ')');
186                     int count2 = StringUtils.countMatches(primaryKey, '(');
187                     if (count1 != count2) {
188                         throw new MusicQueryException(
189                                 "Create Table Error: primary key '(' and ')' do not match, primary key=" + primaryKey,
190                                 Status.BAD_REQUEST.getStatusCode());
191                     }
192
193                     if ( primaryKey.indexOf('(') == -1  || ( count2 == 1 && (primaryKey.lastIndexOf(')') +1) ==  primaryKey.length() ) ) {
194                         if (primaryKey.contains(",") ) {
195                             partitionKey= primaryKey.substring(0,primaryKey.indexOf(','));
196                             partitionKey=partitionKey.replaceAll("[\\(]+","");
197                             clusterKey=primaryKey.substring(primaryKey.indexOf(',')+1);  // make sure index
198                             clusterKey=clusterKey.replaceAll("[)]+", "");
199                         } else {
200                             partitionKey=primaryKey;
201                             partitionKey=partitionKey.replaceAll("[\\)]+","");
202                             partitionKey=partitionKey.replaceAll("[\\(]+","");
203                             clusterKey="";
204                         }
205                     } else {   // not null and has ) before the last char
206                         partitionKey= primaryKey.substring(0,primaryKey.indexOf(')'));
207                         partitionKey=partitionKey.replaceAll("[\\(]+","");
208                         partitionKey = partitionKey.trim();
209                         clusterKey= primaryKey.substring(primaryKey.indexOf(')'));
210                         clusterKey=clusterKey.replaceAll("[\\(]+","");
211                         clusterKey=clusterKey.replaceAll("[\\)]+","");
212                         clusterKey = clusterKey.trim();
213                         if (clusterKey.indexOf(',') == 0) {
214                             clusterKey=clusterKey.substring(1);
215                         }
216                         clusterKey = clusterKey.trim();
217                         if (clusterKey.equals(",") ) clusterKey=""; // print error if needed    ( ... ),)
218                     }
219
220                     if (!(partitionKey.isEmpty() || clusterKey.isEmpty())
221                         && (partitionKey.equalsIgnoreCase(clusterKey) ||
222                         clusterKey.contains(partitionKey) || partitionKey.contains(clusterKey)) ) {
223                         logger.error("DataAPI createTable partition/cluster key ERROR: partitionKey="+partitionKey+", clusterKey=" + clusterKey + " and primary key=" + primaryKey );
224                         /*return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(
225                             "Create Table primary key error: clusterKey(" + clusterKey + ") equals/contains/overlaps partitionKey(" +partitionKey+ ")  of"
226                             + " primary key=" + primaryKey)
227                             .toMap()).build();*/
228                         throw new MusicQueryException("Create Table primary key error: clusterKey(" + clusterKey
229                                 + ") equals/contains/overlaps partitionKey(" + partitionKey + ")  of" + " primary key="
230                                 + primaryKey, Status.BAD_REQUEST.getStatusCode());
231
232                     }
233
234                     if (partitionKey.isEmpty() )  primaryKey="";
235                     else  if (clusterKey.isEmpty() ) primaryKey=" (" + partitionKey  + ")";
236                     else  primaryKey=" (" + partitionKey + ")," + clusterKey;
237
238             
239                     if (primaryKey != null) fieldsString.append(", PRIMARY KEY (" + primaryKey + " )");
240
241                 } else { // end of length > 0
242                 
243                     if (!(partitionKey.isEmpty() || clusterKey.isEmpty())
244                         && (partitionKey.equalsIgnoreCase(clusterKey) ||
245                         clusterKey.contains(partitionKey) || partitionKey.contains(clusterKey)) ) {
246                         logger.error("DataAPI createTable partition/cluster key ERROR: partitionKey="+partitionKey+", clusterKey=" + clusterKey);
247                         /*return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(
248                             "Create Table primary key error: clusterKey(" + clusterKey + ") equals/contains/overlaps partitionKey(" +partitionKey+ ")")
249                             .toMap()).build();*/
250                         throw new MusicQueryException(
251                                 "Create Table primary key error: clusterKey(" + clusterKey
252                                         + ") equals/contains/overlaps partitionKey(" + partitionKey + ")",
253                                 Status.BAD_REQUEST.getStatusCode());
254                     }
255
256                     if (partitionKey.isEmpty() )  primaryKey="";
257                     else  if (clusterKey.isEmpty() ) primaryKey=" (" + partitionKey  + ")";
258                     else  primaryKey=" (" + partitionKey + ")," + clusterKey;
259
260                     if (primaryKey != null) fieldsString.append(", PRIMARY KEY (" + primaryKey + " )");
261                 }
262                 fieldsString.append(")");
263
264             } // end of last field check
265
266         } // end of for each
267         // information about the name-value style properties
268         Map<String, Object> propertiesMap = this.getProperties();
269         StringBuilder propertiesString = new StringBuilder();
270         if (propertiesMap != null) {
271             counter = 0;
272             for (Map.Entry<String, Object> entry : propertiesMap.entrySet()) {
273                 Object ot = entry.getValue();
274                 String value = ot + "";
275                 if (ot instanceof String) {
276                     value = "'" + value + "'";
277                 } else if (ot instanceof Map) {
278                     @SuppressWarnings("unchecked")
279                     Map<String, Object> otMap = (Map<String, Object>) ot;
280                     try {
281                         value = "{" + MusicUtil.jsonMaptoSqlString(otMap, ",") + "}";
282                     } catch (Exception e) {
283                         throw new MusicQueryException(e.getMessage(),
284                                 Status.BAD_REQUEST.getStatusCode());
285                     }
286                 }
287
288                 propertiesString.append(entry.getKey() + "=" + value + "");
289                 if (counter != propertiesMap.size() - 1)
290                     propertiesString.append(" AND ");
291
292                 counter = counter + 1;
293             }
294         }
295
296         String clusteringOrder = this.getClusteringOrder();
297
298         if (clusteringOrder != null && !(clusteringOrder.isEmpty())) {
299             String[] arrayClusterOrder = clusteringOrder.split("[,]+");
300
301             for (int i = 0; i < arrayClusterOrder.length; i++) {
302                 String[] clusterS = arrayClusterOrder[i].trim().split("[ ]+");
303                 if ( (clusterS.length ==2)  && (clusterS[1].equalsIgnoreCase("ASC") || clusterS[1].equalsIgnoreCase("DESC"))) {
304                     continue;
305                 } else {
306                     /*return response.status(Status.BAD_REQUEST)
307                     .entity(new JsonResponse(ResultType.FAILURE)
308                     .setError("createTable/Clustering Order vlaue ERROR: valid clustering order is ASC or DESC or expecting colname  order; please correct clusteringOrder:"+ clusteringOrder+".")
309                     .toMap()).build();*/
310                     
311                     throw new MusicQueryException(
312                             "createTable/Clustering Order vlaue ERROR: valid clustering order is ASC or DESC or expecting colname  order; please correct clusteringOrder:"
313                                     + clusteringOrder + ".",
314                             Status.BAD_REQUEST.getStatusCode());
315                 }
316                 // add validation for column names in cluster key
317             }
318
319             if (!(clusterKey.isEmpty())) {
320                 clusteringOrder = "CLUSTERING ORDER BY (" +clusteringOrder +")";
321                 //cjc check if propertiesString.length() >0 instead propertiesMap
322                 if (propertiesMap != null) {
323                     propertiesString.append(" AND  "+ clusteringOrder);
324                 } else {
325                     propertiesString.append(clusteringOrder);
326                 }
327             } else {
328                 logger.warn("Skipping clustering order=("+clusteringOrder+ ") since clustering key is empty ");
329             }
330         } //if non empty
331
332         queryObject.appendQueryString(
333             "CREATE TABLE " + this.getKeyspaceName() + "." + this.getTableName() + " " + fieldsString);
334
335
336         if (propertiesString != null &&  propertiesString.length()>0 )
337             queryObject.appendQueryString(" WITH " + propertiesString);
338         queryObject.appendQueryString(";");
339
340         return queryObject;
341     }
342
343     /**
344      * 
345      * @return
346      */
347     public PreparedQueryObject genCreateShadowLockingTableQuery() {
348         if (logger.isDebugEnabled()) {
349             logger.debug("Coming inside genCreateShadowLockingTableQuery method " + this.getKeyspaceName());
350             logger.debug("Coming inside genCreateShadowLockingTableQuery method " + this.getTableName());
351         }
352
353         String tableName = "unsyncedKeys_" + this.getTableName();
354         String tabQuery = "CREATE TABLE IF NOT EXISTS " + this.getKeyspaceName() + "." + tableName
355                 + " ( key text,PRIMARY KEY (key) );";
356         PreparedQueryObject queryObject = new PreparedQueryObject();
357         queryObject.appendQueryString(tabQuery);
358
359         return queryObject;
360     }
361
362     /**
363      * genDropTableQuery
364      * 
365      * @return PreparedQueryObject
366      */
367     public PreparedQueryObject genDropTableQuery() {
368         if (logger.isDebugEnabled()) {
369             logger.debug("Coming inside genDropTableQuery method " + this.getKeyspaceName());
370             logger.debug("Coming inside genDropTableQuery method " + this.getTableName());
371         }
372
373         PreparedQueryObject query = new PreparedQueryObject();
374         query.appendQueryString("DROP TABLE  " + this.getKeyspaceName() + "." + this.getTableName() + ";");
375         logger.info("Delete Query ::::: " + query.getQuery());
376
377         return query;
378     }
379
380
381 }