Improve Batches
[aaf/authz.git] / auth / auth-cass / src / main / java / org / onap / aaf / auth / dao / cass / ApprovalDAO.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.dao.cass;
23
24 import java.io.IOException;
25 import java.util.Date;
26 import java.util.List;
27 import java.util.UUID;
28
29 import org.onap.aaf.auth.dao.CassAccess;
30 import org.onap.aaf.auth.dao.CassDAOImpl;
31 import org.onap.aaf.auth.dao.Loader;
32 import org.onap.aaf.auth.env.AuthzTrans;
33 import org.onap.aaf.auth.layer.Result;
34 import org.onap.aaf.misc.env.APIException;
35 import org.onap.aaf.misc.env.Env;
36 import org.onap.aaf.misc.env.TimeTaken;
37 import org.onap.aaf.misc.env.util.Chrono;
38
39 import com.datastax.driver.core.Cluster;
40 import com.datastax.driver.core.ResultSet;
41 import com.datastax.driver.core.Row;
42 import com.datastax.driver.core.exceptions.DriverException;
43
44
45 public class ApprovalDAO extends CassDAOImpl<AuthzTrans,ApprovalDAO.Data> {
46     public static final String PENDING = "pending";
47     public static final String DENIED = "denied";
48     public static final String APPROVED = "approved";
49     
50     private static final String TABLE = "approval";
51     private static final String TABLELOG = "approved";
52     private HistoryDAO historyDAO;
53     private PSInfo psByUser, psByApprover, psByTicket, psByStatus;
54
55     
56     public ApprovalDAO(AuthzTrans trans, Cluster cluster, String keyspace) {
57         super(trans, ApprovalDAO.class.getSimpleName(),cluster,keyspace,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
58         historyDAO = new HistoryDAO(trans, this);
59         init(trans);
60     }
61
62
63     public ApprovalDAO(AuthzTrans trans, HistoryDAO hDAO) {
64         super(trans, ApprovalDAO.class.getSimpleName(),hDAO,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
65         historyDAO=hDAO;
66         init(trans);
67     }
68
69     private static final int KEYLIMIT = 1;
70     public static class Data {
71         public UUID   id;
72         public UUID   ticket;
73         public String user;
74         public String approver;
75         public String type;
76         public String status;
77         public String memo;
78         public String operation;
79 //        public Date last_notified;
80         public Date updated;
81     }
82     
83     private static class ApprovalLoader extends Loader<Data> {
84         public static final ApprovalLoader deflt = new ApprovalLoader(KEYLIMIT);
85         
86         public ApprovalLoader(int keylimit) {
87             super(keylimit);
88         }
89         
90         @Override
91         public Data load(Data data, Row row) {
92             data.id = row.getUUID(0);
93             data.ticket = row.getUUID(1);
94             data.user = row.getString(2);
95             data.approver = row.getString(3);
96             data.type = row.getString(4);
97             data.status = row.getString(5);
98             data.memo = row.getString(6);
99             data.operation = row.getString(7);
100 //            data.last_notified = row.getTimestamp(8);
101             // This is used to get "WRITETIME(STATUS)" from Approval, which gives us an "updated" 
102             if (row.getColumnDefinitions().size()>8) {
103                 // Rows reported in MicroSeconds
104                 data.updated = new Date(row.getLong(8)/1000);
105             }
106             return data;
107         }
108
109         @Override
110         protected void key(Data data, int idx, Object[] obj) {
111             obj[idx]=data.id;
112         }
113
114         @Override
115         protected void body(Data data, int _idx, Object[] obj) {
116                 int idx = _idx;
117             obj[idx]=data.ticket;
118             obj[++idx]=data.user;
119             obj[++idx]=data.approver;
120             obj[++idx]=data.type;
121             obj[++idx]=data.status;
122             obj[++idx]=data.memo;
123             obj[++idx]=data.operation;
124 //            obj[++idx]=data.last_notified;
125         }
126     }    
127     
128     private void init(AuthzTrans trans) {
129         String[] helpers = setCRUD(trans, TABLE, Data.class, ApprovalLoader.deflt,8);
130         psByUser = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + ", WRITETIME(status) FROM " + TABLE + 
131                 " WHERE user = ?", new ApprovalLoader(1) {
132             @Override
133             protected void key(Data data, int idx, Object[] obj) {
134                 obj[idx]=data.user;
135             }
136         }, readConsistency);
137         
138         psByApprover = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + ", WRITETIME(status) FROM " + TABLE + 
139                 " WHERE approver = ?", new ApprovalLoader(1) {
140             @Override
141             protected void key(Data data, int idx, Object[] obj) {
142                 obj[idx]=data.approver;
143             }
144         }, readConsistency);
145
146         psByTicket = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + ", WRITETIME(status) FROM " + TABLE + 
147                 " WHERE ticket = ?", new ApprovalLoader(1) {
148             @Override
149             protected void key(Data data, int idx, Object[] obj) {
150                 obj[idx]=data.ticket;
151             }
152         }, readConsistency);
153
154         psByStatus = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + ", WRITETIME(status) FROM " + TABLE + 
155                 " WHERE status = ?", new ApprovalLoader(1) {
156             @Override
157             protected void key(Data data, int idx, Object[] obj) {
158                 obj[idx]=data.status;
159             }
160         }, readConsistency);
161
162
163     }
164
165     /* (non-Javadoc)
166      * @see org.onap.aaf.auth.dao.CassDAOImpl#create(com.att.inno.env.TransStore, java.lang.Object)
167      */
168     @Override
169     public Result<Data> create(AuthzTrans trans, Data data) {
170         // If ID is not set (typical), create one.
171         if (data.id==null) {
172             data.id = Chrono.dateToUUID(System.currentTimeMillis());
173         }
174         Result<ResultSet> rs = createPS.exec(trans, C_TEXT, data);
175         if (rs.notOK()) {
176             return Result.err(rs);
177         }
178         return Result.ok(data);    
179     }
180
181
182     public Result<List<ApprovalDAO.Data>> readByUser(AuthzTrans trans, String user) {
183         return psByUser.read(trans, R_TEXT, new Object[]{user});
184     }
185
186     public Result<List<ApprovalDAO.Data>> readByApprover(AuthzTrans trans, String approver) {
187         return psByApprover.read(trans, R_TEXT, new Object[]{approver});
188     }
189
190     public Result<List<ApprovalDAO.Data>> readByTicket(AuthzTrans trans, UUID ticket) {
191         return psByTicket.read(trans, R_TEXT, new Object[]{ticket});
192     }
193
194     public Result<List<ApprovalDAO.Data>> readByStatus(AuthzTrans trans, String status) {
195         return psByStatus.read(trans, R_TEXT, new Object[]{status});
196     }    
197
198     /* (non-Javadoc)
199      * @see org.onap.aaf.auth.dao.CassDAOImpl#delete(com.att.inno.env.TransStore, java.lang.Object, boolean)
200      */
201     @Override
202     public Result<Void> delete(AuthzTrans trans, Data data, boolean reread) {
203         if (reread || data.status == null) { // if Memo is empty, likely not full record
204             Result<ResultSet> rd = readPS.exec(trans, R_TEXT, data);
205             if (rd.notOK()) {
206                 return Result.err(rd);
207             }
208             ApprovalLoader.deflt.load(data, rd.value.one());
209         }
210         if ("approved".equals(data.status) || "denied".equals(data.status)) { 
211             StringBuilder sb = new StringBuilder("BEGIN BATCH\n");
212             sb.append("INSERT INTO ");
213             sb.append(TABLELOG);
214             sb.append(" (id,user,approver,type,status,memo,operation) VALUES (");
215             sb.append(data.id);
216             sb.append(",'"); sb.append(data.user);
217             sb.append("','"); sb.append(data.approver);
218             sb.append("','"); sb.append(data.type);
219             sb.append("','"); sb.append(data.status);
220             sb.append("','"); sb.append(data.memo.replace("'", "''"));
221             sb.append("','"); sb.append(data.operation);
222             sb.append("');\n");
223             sb.append("DELETE FROM ");
224             sb.append(TABLE);
225             sb.append(" WHERE id=");
226             sb.append(data.id);
227             sb.append(";\n");
228             sb.append("APPLY BATCH;\n");
229             TimeTaken tt = trans.start("DELETE APPROVAL",Env.REMOTE);
230             try {
231                 if (async) {
232                     getSession(trans).executeAsync(sb.toString());
233                     return Result.ok();
234                 } else {
235                     getSession(trans).execute(sb.toString());
236                     return Result.ok();
237                 }
238             } catch (DriverException | APIException | IOException e) {
239                 reportPerhapsReset(trans,e);
240                 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
241             } finally {
242                 tt.done();
243             }
244         } else {
245             return super.delete(trans, data, false);
246         }
247
248     }
249
250
251     /**
252      * Log Modification statements to History
253      *
254      * @param modified        which CRUD action was done
255      * @param data            entity data that needs a log entry
256      * @param overrideMessage if this is specified, we use it rather than crafting a history message based on data
257      */
258     @Override
259     protected void wasModified(AuthzTrans trans, CRUD modified, Data data, String ... override) {
260         boolean memo = override.length>0 && override[0]!=null;
261         boolean subject = override.length>1 && override[1]!=null;
262
263         HistoryDAO.Data hd = HistoryDAO.newInitedData();
264         hd.user = trans.user();
265         hd.action = modified.name();
266         hd.target = TABLE;
267         hd.subject = subject?override[1]:data.user + "|" + data.approver;
268         hd.memo = memo
269                 ? String.format("%s by %s", override[0], hd.user)
270                 : (modified.name() + "d approval for " + data.user);
271         // Detail?
272         // Reconstruct?
273         if (historyDAO.create(trans, hd).status!=Status.OK) {
274             trans.error().log("Cannot log to History");
275         }
276     }
277 }