/**
* Copyright (C) 2015 Orion Health (Orchestral Development Ltd)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xbdd.webapp.resource.feature;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import xbdd.webapp.factory.MongoDBAccessor;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.DuplicateKeyException;
@Path("/rest/admin")
public class AdminUtils {
private final MongoDBAccessor client;
@Inject
public AdminUtils(final MongoDBAccessor client,
@Context final HttpServletRequest req) {
this.client = client;
if (!req.isUserInRole("admin")) {
throw new WebApplicationException();
}
}
@DELETE
@Path("/delete/{product}")
@Produces("application/json")
public Response softDeleteEntireProduct(@PathParam("product") final String product,
@Context final HttpServletRequest req,
@Context final HttpServletResponse response) throws IOException {
final DB db = this.client.getDB("bdd");
final DBCollection collection = db.getCollection("summary");
final DBCollection targetCollection = db.getCollection("deletedSummary");
final BasicDBObject query = new BasicDBObject("coordinates.product",product);
final DBCursor cursor = collection.find(query);
DBObject doc;
while(cursor.hasNext()) {
doc = cursor.next();
//kill the old id
doc.removeField("_id");
try {
targetCollection.insert(doc);
} catch (Throwable e) {
return Response.status(500).build();
}
}
collection.remove(query);
return Response.ok().build();
}
@DELETE
@Path("/delete/{product}/{version}")
@Produces("application/json")
public Response softDeleteSingleVersion(@PathParam("product") final String product,
@PathParam("version") final String version,
@Context final HttpServletRequest req,
@Context final HttpServletResponse response) throws IOException {
final DB db = this.client.getDB("bdd");
final DBCollection collection = db.getCollection("summary");
final DBCollection targetCollection = db.getCollection("deletedSummary");
final Pattern productReg = java.util.regex.Pattern.compile("^"+product+"/"+version+"$");
final BasicDBObject query = new BasicDBObject("_id", productReg);
final DBCursor cursor = collection.find(query);
DBObject doc;
while(cursor.hasNext()) {
doc = cursor.next();
//kill the old id
doc.removeField("_id");
try {
targetCollection.insert(doc);
} catch (Throwable e) {
return Response.status(500).build();
}
}
collection.remove(query);
return Response.ok().build();
}
@DELETE
@Path("/delete/{product}/{version}/{build}")
@Produces("application/json")
public Response softDeleteSingleBuild(@PathParam("product") final String product,
@PathParam("build") final String build,
@PathParam("version") final String version,
@Context final HttpServletRequest req,
@Context final HttpServletResponse response) throws IOException {
final DB db = this.client.getDB("bdd");
final DBCollection collection = db.getCollection("summary");
final DBCollection targetCollection = db.getCollection("deletedSummary");
final Pattern productReg = java.util.regex.Pattern.compile("^"+product+"/"+version+"$");
final BasicDBObject query = new BasicDBObject("_id", productReg);
final DBCursor cursor = collection.find(query);
while (cursor.hasNext()) {
DBObject doc = cursor.next();
DBObject backupDoc = doc;
//Make sure the backup document only has the deleted build number
try {
final String[] singleBuild = {build};
backupDoc.put("builds", singleBuild);
targetCollection.insert(backupDoc);
} catch (DuplicateKeyException e) {
//The backup document already exists, possibly already deleted a build
//Lets add the deleted build to the existing document
targetCollection.update(new BasicDBObject("_id",backupDoc.get("_id")), new BasicDBObject("$push", new BasicDBObject("builds",build)));
} catch(Exception e) {
return Response.status(500).build();
}
//Remove the build number from the current document and push it back into the collection
try {
collection.update(new BasicDBObject("_id",doc.get("_id")), new BasicDBObject("$pull",new BasicDBObject("builds", build)));
} catch(Exception e) {
System.out.println(e);
return Response.status(500).build();
}
}
return Response.ok().build();
}
//Lets register the helper class that replaces the instances of the old product name with the new one
private DBObject renameDoc(String product, String newname, DBObject doc) {
doc.put("_id", ((String) doc.get("_id")).replaceAll(product+"/", newname+"/"));
if (doc.containsField("coordinates")) {
DBObject coordinates = (DBObject) doc.get("coordinates");
coordinates.put("product",newname);
doc.put("coordinates", coordinates);
}
return doc;
}
public static class Product {
public String name;
}
@PUT @Consumes(MediaType.APPLICATION_JSON)
@Path("/{product}")
@Produces("application/json")
public Response renameProduct(@PathParam("product") final String product,
final Product renameObject) {
final DB db = this.client.getDB("bdd");
final List<DBCollection> collections = Arrays.asList(db.getCollection("summary"), db.getCollection("features"), db.getCollection("reportStats"), db.getCollection("testingTips"));
final Pattern productReg = Pattern.compile("^"+product+"/");
final BasicDBObject query = new BasicDBObject("_id", productReg);
//Before anything lets check the new name isn't already in use
final BasicDBObject existsQuery = new BasicDBObject("_id", Pattern.compile("^"+renameObject.name+"/"));
final int summaryCount = db.getCollection("summary").find(existsQuery).count();
final int delCount = db.getCollection("deletedSummary").find(existsQuery).count();
if (delCount + summaryCount != 0) {
throw new WebApplicationException();
}
//We need to rename the product everywhere
//First up are all the collection with the product in the _id attribute
for (DBCollection collectioni : collections) {
DBCursor cursor = collectioni.find(query);
while(cursor.hasNext()) {
DBObject doc = cursor.next();
String id = (String) doc.get("_id");
doc = renameDoc(product, renameObject.name, doc);
collectioni.insert(doc);
collectioni.remove(new BasicDBObject("_id", id));
}
}
//Then we deal with the environments collection where only the coordinates.product is set
final DBCollection[] noIDCollections = {db.getCollection("environments"),db.getCollection("deletedSummary")};
final BasicDBObject enviroQuery = new BasicDBObject("coordinates.product", product);
for (int i=0;i<noIDCollections.length;i++) {
final DBCursor enviroCursor = noIDCollections[i].find(enviroQuery);
while (enviroCursor.hasNext()) {
DBObject doc = enviroCursor.next();
DBObject coordinates = (DBObject) doc.get("coordinates");
coordinates.put("product",renameObject.name);
DBObject updateDoc = new BasicDBObject("$set", new BasicDBObject("coordinates", coordinates));
noIDCollections[i].update(new BasicDBObject("_id", doc.get("_id")), updateDoc);
}
}
//Then we correct the name in any users favourites object
final DBCollection userCollection = db.getCollection("users");
final BasicDBObject favouriteQuery = new BasicDBObject("favourites."+product, new BasicDBObject("$exists",true));
final DBCursor users = userCollection.find(favouriteQuery);
while(users.hasNext()) {
DBObject doc = users.next();
DBObject favs = (DBObject) doc.get("favourites");
favs.put(renameObject.name, favs.get(product));
BasicDBObject updateDoc = new BasicDBObject("$set",new BasicDBObject("favourites",favs));
updateDoc.put("$unset", new BasicDBObject(product, ""));
userCollection.update(new BasicDBObject("_id", doc.get("_id")), updateDoc);
}
return Response.ok().build();
}
}