001/* 002 * (C) Copyright 2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Thomas Roger 018 * Yannis JULIENNE 019 */ 020package org.nuxeo.functionaltests; 021 022import static org.nuxeo.functionaltests.AbstractTest.NUXEO_URL; 023import static org.nuxeo.functionaltests.Constants.ADMINISTRATOR; 024 025import java.io.IOException; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Calendar; 029import java.util.HashMap; 030import java.util.HashSet; 031import java.util.List; 032import java.util.Map; 033import java.util.Set; 034import java.util.stream.Collectors; 035 036import org.apache.commons.lang.StringUtils; 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039import org.codehaus.jackson.JsonNode; 040import org.codehaus.jackson.map.ObjectMapper; 041import org.nuxeo.client.api.NuxeoClient; 042import org.nuxeo.client.api.objects.Document; 043import org.nuxeo.client.api.objects.Documents; 044import org.nuxeo.client.api.objects.acl.ACE; 045import org.nuxeo.client.api.objects.user.Group; 046import org.nuxeo.client.internals.spi.NuxeoClientException; 047import org.nuxeo.common.utils.URIUtils; 048 049import okhttp3.Response; 050import okhttp3.ResponseBody; 051 052/** 053 * @since 8.3 054 */ 055public class RestHelper { 056 057 private static final NuxeoClient CLIENT = new NuxeoClient(NUXEO_URL, ADMINISTRATOR, ADMINISTRATOR); 058 059 private static final String USER_WORKSPACE_PATH_FORMAT = "/default-domain/UserWorkspaces/%s"; 060 061 private static final String DEFAULT_USER_EMAIL = "devnull@nuxeo.com"; 062 063 private static final String DOCUMENT_QUERY_BY_PATH_BASE = "SELECT * FROM Document WHERE ecm:path = '%s'"; 064 065 private static final List<String> documentIdsToDelete = new ArrayList<>(); 066 067 private static final List<String> documentPathsToDelete = new ArrayList<>(); 068 069 private static final List<String> usersToDelete = new ArrayList<>(); 070 071 private static final List<String> groupsToDelete = new ArrayList<>(); 072 073 protected static final Map<String, Set<String>> directoryEntryIdsToDelete = new HashMap<>(); 074 075 private static final int NOT_FOUND_ERROR_STATUS = 404; 076 077 protected static final Log log = LogFactory.getLog(RestHelper.class); 078 079 // @yannis : temporary fix for setting user password before JAVACLIENT-91 080 private static final ObjectMapper MAPPER = new ObjectMapper(); 081 082 private RestHelper() { 083 // helper class 084 } 085 086 public static void cleanup() { 087 cleanupDocuments(); 088 cleanupUsers(); 089 cleanupGroups(); 090 cleanupDirectoryEntries(); 091 } 092 093 public static void cleanupDocuments() { 094 documentIdsToDelete.forEach(RestHelper::deleteDocument); 095 documentIdsToDelete.clear(); 096 documentPathsToDelete.clear(); 097 } 098 099 public static void cleanupUsers() { 100 for (String user : usersToDelete) { 101 RestHelper.deleteDocument(String.format(USER_WORKSPACE_PATH_FORMAT, user)); 102 } 103 usersToDelete.forEach(RestHelper::deleteUser); 104 usersToDelete.clear(); 105 } 106 107 public static void cleanupGroups() { 108 groupsToDelete.forEach(RestHelper::deleteGroup); 109 groupsToDelete.clear(); 110 } 111 112 public static void cleanupDirectoryEntries() { 113 directoryEntryIdsToDelete.forEach((directoryName, entryIds) -> { 114 entryIds.forEach(id -> deleteDirectoryEntry(directoryName, id)); 115 }); 116 clearDirectoryEntryIdsToDelete(); 117 } 118 119 public static String createUser(String username, String password) { 120 return createUser(username, password, null, null, null, null, null); 121 } 122 123 public static String createUser(String username, String password, String firstName, String lastName, String company, 124 String email, String group) { 125 126 String finalEmail = StringUtils.isBlank(email) ? DEFAULT_USER_EMAIL : email; 127 128 // @yannis : temporary fix for setting user password before JAVACLIENT-91 129 String json = buildUserJSON(username, password, firstName, lastName, company, finalEmail, group); 130 131 Response response = CLIENT.post(AbstractTest.NUXEO_URL + "/api/v1/user", json); 132 if (!response.isSuccessful()) { 133 throw new RuntimeException(String.format("Unable to create user '%s'", username)); 134 } 135 136 try (ResponseBody responseBody = response.body()) { 137 JsonNode jsonNode = MAPPER.readTree(responseBody.charStream()); 138 String id = jsonNode.get("id").getTextValue(); 139 usersToDelete.add(id); 140 return id; 141 } catch (IOException e) { 142 throw new RuntimeException(e); 143 } 144 } 145 146 public static void addUserToDelete(String userName) { 147 usersToDelete.add(userName); 148 } 149 150 public static void removeUserToDelete(String userName) { 151 usersToDelete.remove(userName); 152 } 153 154 public static void addDirectoryEntryToDelete(String directoryName, String entryId) { 155 directoryEntryIdsToDelete.computeIfAbsent(directoryName, k -> new HashSet<>()).add(entryId); 156 } 157 158 public static void clearDirectoryEntryIdsToDelete() { 159 directoryEntryIdsToDelete.clear(); 160 } 161 162 private static String buildUserJSON(String username, String password, String firstName, String lastName, 163 String company, String email, String group) { 164 StringBuilder sb = new StringBuilder(); 165 sb.append("{"); 166 sb.append("\"entity-type\": \"user\"").append(",\n"); 167 sb.append("\"id\": \"").append(username).append("\",\n"); 168 sb.append("\"properties\": {").append("\n"); 169 if (firstName != null) { 170 sb.append("\"firstName\": \"").append(firstName).append("\",\n"); 171 } 172 if (lastName != null) { 173 sb.append("\"lastName\": \"").append(lastName).append("\",\n"); 174 } 175 if (email != null) { 176 sb.append("\"email\": \"").append(email).append("\",\n"); 177 } 178 if (company != null) { 179 sb.append("\"company\": \"").append(company).append("\",\n"); 180 } 181 if (group != null) { 182 sb.append("\"groups\": [\"").append(group).append("\"]").append(",\n"); 183 } 184 sb.append("\"username\": \"").append(username).append("\",\n"); 185 sb.append("\"password\": \"").append(password).append("\"\n"); 186 sb.append("}").append("\n"); 187 sb.append("}"); 188 return sb.toString(); 189 } 190 191 public static void deleteUser(String username) { 192 try { 193 CLIENT.getUserManager().deleteUser(username); 194 } catch (NuxeoClientException e) { 195 if (NOT_FOUND_ERROR_STATUS == e.getStatus()) { 196 log.warn(String.format("User %s not deleted because not found", username)); 197 } else { 198 throw e; 199 } 200 } 201 } 202 203 public static void createGroup(String name, String label) { 204 createGroup(name, label, null, null); 205 } 206 207 public static void createGroup(String name, String label, String[] members, String[] subGroups) { 208 Group group = new Group(); 209 group.setGroupName(name); 210 group.setGroupLabel(label); 211 if (members != null) { 212 group.setMemberUsers(Arrays.asList(members)); 213 } 214 if (subGroups != null) { 215 group.setMemberGroups(Arrays.asList(subGroups)); 216 } 217 218 CLIENT.getUserManager().createGroup(group); 219 groupsToDelete.add(name); 220 } 221 222 public static void deleteGroup(String name) { 223 try { 224 CLIENT.getUserManager().deleteGroup(name); 225 } catch (NuxeoClientException e) { 226 if (NOT_FOUND_ERROR_STATUS == e.getStatus()) { 227 log.warn(String.format("Group %s not deleted because not found", name)); 228 } else { 229 throw e; 230 } 231 } 232 } 233 234 public static String createDocument(String idOrPath, String type, String title, String description) { 235 Document document = new Document(title, type); 236 Map<String, Object> properties = new HashMap<>(); 237 properties.put("dc:title", title); 238 if (description != null) { 239 properties.put("dc:description", description); 240 } 241 document.setProperties(properties); 242 243 if (idOrPath.startsWith("/")) { 244 document = CLIENT.repository().createDocumentByPath(idOrPath, document); 245 } else { 246 document = CLIENT.repository().createDocumentById(idOrPath, document); 247 } 248 249 String docId = document.getId(); 250 String docPath = document.getPath(); 251 // do we already have to delete one parent? 252 if (documentPathsToDelete.stream().noneMatch(docPath::startsWith)) { 253 documentIdsToDelete.add(docId); 254 documentPathsToDelete.add(docPath); 255 } 256 return docId; 257 } 258 259 public static void deleteDocument(String idOrPath) { 260 // TODO change that by proper deleteDocument(String) 261 if (idOrPath.startsWith("/")) { 262 // @yannis : temporary way to avoid DocumentNotFoundException in server log before NXP-19658 263 Documents documents = CLIENT.repository().query(String.format(DOCUMENT_QUERY_BY_PATH_BASE, idOrPath)); 264 if (documents.size() > 0) { 265 CLIENT.repository().deleteDocument(documents.getDocument(0)); 266 } 267 } else { 268 CLIENT.repository().deleteDocument(CLIENT.repository().fetchDocumentById(idOrPath)); 269 } 270 } 271 272 public static void addPermission(String idOrPath, String username, String permission) { 273 Document document; 274 if (idOrPath.startsWith("/")) { 275 document = CLIENT.repository().fetchDocumentByPath(idOrPath); 276 } else { 277 document = CLIENT.repository().fetchDocumentById(idOrPath); 278 } 279 280 ACE ace = new ACE(); 281 ace.setUsername(username); 282 ace.setPermission(permission); 283 284 // @yannis : temporary fix for setting permission before JAVACLIENT-90 is done 285 Calendar beginDate = Calendar.getInstance(); 286 ace.setBegin(beginDate); 287 Calendar endDate = Calendar.getInstance(); 288 endDate.add(Calendar.YEAR, 1); 289 ace.setEnd(endDate); 290 291 document.addPermission(ace); 292 } 293 294 public static void removePermissions(String idOrPath, String username) { 295 Document document; 296 if (idOrPath.startsWith("/")) { 297 document = CLIENT.repository().fetchDocumentByPath(idOrPath); 298 } else { 299 document = CLIENT.repository().fetchDocumentById(idOrPath); 300 } 301 302 document.removePermission(username); 303 } 304 305 /** 306 * @since 9.2 307 */ 308 public static String createDirectoryEntry(String directoryName, Map<String, String> properties) { 309 Response response = CLIENT.post(NUXEO_URL + "/api/v1/directory/" + directoryName, 310 buildDirectoryEntryJSON(directoryName, properties)); 311 if (!response.isSuccessful()) { 312 throw new NuxeoClientException( 313 String.format("Unable to create entry for directory %s: %s", directoryName, properties)); 314 } 315 try (ResponseBody responseBody = response.body()) { 316 JsonNode jsonNode = MAPPER.readTree(responseBody.charStream()); 317 String entryId = jsonNode.get("properties").get("id").getValueAsText(); 318 addDirectoryEntryToDelete(directoryName, entryId); 319 return entryId; 320 } catch (IOException e) { 321 throw new NuxeoClientException(e); 322 } 323 } 324 325 /** 326 * @since 9.2 327 */ 328 public static void deleteDirectoryEntry(String directoryName, String entryId) { 329 // Work around JAVACLIENT-133 by passing an empty string 330 CLIENT.delete(NUXEO_URL + "/api/v1/directory/" + directoryName + "/" + entryId, ""); 331 } 332 333 protected static String buildDirectoryEntryJSON(String directoryName, Map<String, String> properties) { 334 StringBuilder sb = new StringBuilder(); 335 sb.append("{"); 336 sb.append("\"entity-type\": \"directoryEntry\"").append(",\n"); 337 sb.append("\"directoryName\": \"").append(directoryName).append("\",\n"); 338 sb.append("\"properties\": {").append("\n"); 339 sb.append(properties.keySet() 340 .stream() 341 .map(key -> new StringBuilder().append("\"") 342 .append(key) 343 .append("\": \"") 344 .append(properties.get(key)) 345 .append("\"")) 346 .collect(Collectors.joining(",\n"))); 347 sb.append("\n").append("}").append("\n"); 348 sb.append("}"); 349 return sb.toString(); 350 } 351 352 public static void logOnServer(String level, String message) { 353 CLIENT.get(String.format("%s/restAPI/systemLog?token=dolog&level=%s&message=%s", AbstractTest.NUXEO_URL, level, 354 URIUtils.quoteURIPathComponent(message, true))); 355 } 356 357}