/*
* Copyright 2013-2017 Erudika. https://erudika.com
*
* 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.
*
* For issues and patches go to: https://github.com/erudika
*/
package com.erudika.para.rest;
import static com.erudika.para.Para.getCustomResourceHandlers;
import static com.erudika.para.Para.getDAO;
import static com.erudika.para.Para.getVersion;
import static com.erudika.para.Para.setup;
import com.erudika.para.core.App;
import com.erudika.para.core.utils.CoreUtils;
import com.erudika.para.core.ParaObject;
import com.erudika.para.core.utils.ParaObjectUtils;
import com.erudika.para.core.User;
import static com.erudika.para.rest.RestUtils.getBatchCreateResponse;
import static com.erudika.para.rest.RestUtils.getBatchDeleteResponse;
import static com.erudika.para.rest.RestUtils.getBatchReadResponse;
import static com.erudika.para.rest.RestUtils.getBatchUpdateResponse;
import static com.erudika.para.rest.RestUtils.getCreateResponse;
import static com.erudika.para.rest.RestUtils.getDeleteResponse;
import static com.erudika.para.rest.RestUtils.getEntity;
import static com.erudika.para.rest.RestUtils.getOverwriteResponse;
import static com.erudika.para.rest.RestUtils.getPrincipalApp;
import static com.erudika.para.rest.RestUtils.getReadResponse;
import static com.erudika.para.rest.RestUtils.getStatusResponse;
import static com.erudika.para.rest.RestUtils.getUpdateResponse;
import static com.erudika.para.rest.RestUtils.pathParam;
import static com.erudika.para.rest.RestUtils.queryParam;
import static com.erudika.para.rest.RestUtils.queryParams;
import com.erudika.para.security.SecurityUtils;
import com.erudika.para.utils.Config;
import com.erudika.para.utils.HumanTime;
import com.erudika.para.utils.Pager;
import com.erudika.para.utils.Utils;
import com.erudika.para.utils.filters.FieldFilter;
import com.erudika.para.validation.Constraint;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.glassfish.jersey.process.Inflector;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is the main REST API configuration class which defines all endpoints for all resources
* and the way API request will be handled. This is API version 1.0.
* @author Alex Bogdanovski [alex@erudika.com]
*/
public final class Api1 extends ResourceConfig {
/**
* {@value #PATH}.
*/
public static final String PATH = "/v1/";
private static final Logger logger = LoggerFactory.getLogger(Api1.class);
private static final String JSON = MediaType.APPLICATION_JSON;
private static final String GET = HttpMethod.GET;
private static final String PUT = HttpMethod.PUT;
private static final String POST = HttpMethod.POST;
private static final String DELETE = HttpMethod.DELETE;
private static final String PATCH = "PATCH";
/**
* Initializes all of the API resources.
*/
public Api1() {
if (!Config.API_ENABLED) {
return;
}
setApplicationName(Config.APP_NAME_NS);
register(GenericExceptionMapper.class);
register(new JacksonJsonProvider(ParaObjectUtils.getJsonMapper()));
register(FieldFilter.class);
// print logo
Resource.Builder logo = Resource.builder("/");
logo.addMethod(GET).produces(JSON).handledBy(introHandler());
registerResources(logo.build());
// core objects CRUD API
registerCrudApi("{type}", typeCrudHandler(), linksHandler());
// search API
Resource.Builder searchRes = Resource.builder("search/{querytype}");
searchRes.addMethod(GET).produces(JSON).handledBy(searchHandler(null, null));
registerResources(searchRes.build());
// first time setup
Resource.Builder setupRes = Resource.builder("_setup");
setupRes.addMethod(GET).produces(JSON).handledBy(setupHandler());
setupRes.addChildResource("{appid}").addMethod(GET).produces(JSON).handledBy(setupHandler());
registerResources(setupRes.build());
// reset API keys
Resource.Builder keysRes = Resource.builder("_newkeys");
keysRes.addMethod(POST).produces(JSON).handledBy(keysHandler());
registerResources(keysRes.build());
// user-defined types
Resource.Builder typesRes = Resource.builder("_types");
typesRes.addMethod(GET).produces(JSON).handledBy(listTypesHandler());
registerResources(typesRes.build());
// current user/app object
Resource.Builder meRes = Resource.builder("_me");
meRes.addMethod(GET).produces(JSON).handledBy(meHandler());
registerResources(meRes.build());
// getValidationConstraints by id
Resource.Builder idRes = Resource.builder("_id/{id}");
idRes.addMethod(GET).produces(JSON).handledBy(readIdHandler());
registerResources(idRes.build());
// validation
Resource.Builder valRes = Resource.builder("_constraints");
valRes.addMethod(GET).produces(JSON).handledBy(getConstrHandler(null));
valRes.addChildResource("{type}").addMethod(GET).produces(JSON).handledBy(getConstrHandler(null));
valRes.addChildResource("{type}/{field}/{cname}").addMethod(PUT).produces(JSON).handledBy(addConstrHandler(null));
valRes.addChildResource("{type}/{field}/{cname}").addMethod(DELETE).produces(JSON).handledBy(removeConstrHandler(null));
registerResources(valRes.build());
// permissions
Resource.Builder permRes = Resource.builder("_permissions");
permRes.addMethod(GET).produces(JSON).handledBy(getPermitHandler(null));
permRes.addChildResource("{subjectid}").addMethod(GET).produces(JSON).handledBy(getPermitHandler(null));
permRes.addChildResource("{subjectid}/{type}/{method}").addMethod(GET).produces(JSON).handledBy(checkPermitHandler(null));
permRes.addChildResource("{subjectid}/{type}").addMethod(PUT).produces(JSON).handledBy(grantPermitHandler(null));
permRes.addChildResource("{subjectid}/{type}").addMethod(DELETE).produces(JSON).handledBy(revokePermitHandler(null));
permRes.addChildResource("{subjectid}").addMethod(DELETE).produces(JSON).handledBy(revokePermitHandler(null));
registerResources(permRes.build());
// app settings
Resource.Builder appSettingsRes = Resource.builder("_settings");
appSettingsRes.addMethod(GET).produces(JSON).handledBy(appSettingsHandler(null));
appSettingsRes.addMethod(PUT).produces(JSON).handledBy(appSettingsHandler(null));
appSettingsRes.addChildResource("{key}").addMethod(GET).produces(JSON).handledBy(appSettingsHandler(null));
appSettingsRes.addChildResource("{key}").addMethod(PUT).produces(JSON).handledBy(appSettingsHandler(null));
appSettingsRes.addChildResource("{key}").addMethod(DELETE).produces(JSON).handledBy(appSettingsHandler(null));
registerResources(appSettingsRes.build());
// util functions API
Resource.Builder utilsRes = Resource.builder("utils/{method}");
utilsRes.addMethod(GET).produces(JSON).handledBy(utilsHandler());
registerResources(utilsRes.build());
// register custom resources
for (final CustomResourceHandler handler : getCustomResourceHandlers()) {
Resource.Builder custom = Resource.builder(handler.getRelativePath());
custom.addMethod(GET).produces(JSON).handledBy(new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
return handler.handleGet(ctx);
}
});
custom.addMethod(POST).produces(JSON).consumes(JSON).
handledBy(new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
return handler.handlePost(ctx);
}
});
custom.addMethod(PATCH).produces(JSON).consumes(JSON).
handledBy(new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
return handler.handlePatch(ctx);
}
});
custom.addMethod(PUT).produces(JSON).consumes(JSON).
handledBy(new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
return handler.handlePut(ctx);
}
});
custom.addMethod(DELETE).produces(JSON).handledBy(new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
return handler.handleDelete(ctx);
}
});
registerResources(custom.build());
}
}
private void registerCrudApi(String path, Inflector<ContainerRequestContext, Response> handler,
Inflector<ContainerRequestContext, Response> linksHandler) {
Resource.Builder core = Resource.builder(path);
// list endpoints (both do the same thing)
core.addMethod(GET).produces(JSON).handledBy(handler);
core.addChildResource("search/{querytype}").addMethod(GET).produces(JSON).handledBy(handler);
// CRUD endpoints (non-batch)
core.addMethod(POST).produces(JSON).consumes(JSON).handledBy(handler);
core.addChildResource("{id}").addMethod(GET).produces(JSON).handledBy(handler);
core.addChildResource("{id}").addMethod(PUT).produces(JSON).consumes(JSON).handledBy(handler);
core.addChildResource("{id}").addMethod(PATCH).produces(JSON).consumes(JSON).handledBy(handler);
core.addChildResource("{id}").addMethod(DELETE).produces(JSON).handledBy(handler);
// links CRUD endpoints
core.addChildResource("{id}/links/{type2}/{id2}").addMethod(GET).produces(JSON).handledBy(linksHandler);
core.addChildResource("{id}/links/{type2}").addMethod(GET).produces(JSON).handledBy(linksHandler);
core.addChildResource("{id}/links/{id2}").addMethod(POST).produces(JSON).handledBy(linksHandler);
core.addChildResource("{id}/links/{id2}").addMethod(PUT).produces(JSON).handledBy(linksHandler);
core.addChildResource("{id}/links/{type2}/{id2}").addMethod(DELETE).produces(JSON).handledBy(linksHandler);
core.addChildResource("{id}/links").addMethod(DELETE).produces(JSON).handledBy(linksHandler);
// CRUD endpoints (batch)
Resource.Builder batch = Resource.builder("_batch");
batch.addMethod(POST).produces(JSON).consumes(JSON).handledBy(batchCreateHandler(null));
batch.addMethod(GET).produces(JSON).handledBy(batchReadHandler(null));
batch.addMethod(PUT).produces(JSON).consumes(JSON).handledBy(batchCreateHandler(null));
batch.addMethod(PATCH).produces(JSON).consumes(JSON).handledBy(batchUpdateHandler(null));
batch.addMethod(DELETE).produces(JSON).handledBy(batchDeleteHandler(null));
registerResources(core.build());
registerResources(batch.build());
}
private Inflector<ContainerRequestContext, Response> utilsHandler() {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
MultivaluedMap<String, String> params = ctx.getUriInfo().getQueryParameters();
String method = pathParam("method", ctx);
method = StringUtils.isBlank(method) ? params.getFirst("method") : method;
if ("newid".equals(method)) {
return Response.ok(Utils.getNewId(), MediaType.TEXT_PLAIN_TYPE).build();
} else if ("timestamp".equals(method)) {
return Response.ok(Utils.timestamp(), MediaType.TEXT_PLAIN_TYPE).build();
} else if ("formatdate".equals(method)) {
String format = params.getFirst("format");
String locale = params.getFirst("locale");
Locale loc = Utils.getLocale(locale);
return Response.ok(Utils.formatDate(format, loc), MediaType.TEXT_PLAIN_TYPE).build();
} else if ("formatmessage".equals(method)) {
String msg = params.getFirst("message");
Object[] paramz = params.get("fields").toArray();
return Response.ok(Utils.formatMessage(msg, paramz), MediaType.TEXT_PLAIN_TYPE).build();
} else if ("nospaces".equals(method)) {
String str = params.getFirst("string");
String repl = params.getFirst("replacement");
return Response.ok(Utils.noSpaces(str, repl), MediaType.TEXT_PLAIN_TYPE).build();
} else if ("nosymbols".equals(method)) {
String str = params.getFirst("string");
return Response.ok(Utils.stripAndTrim(str), MediaType.TEXT_PLAIN_TYPE).build();
} else if ("md2html".equals(method)) {
String md = params.getFirst("md");
return Response.ok(Utils.markdownToHtml(md), MediaType.TEXT_HTML).build();
} else if ("timeago".equals(method)) {
String d = params.getFirst("delta");
long delta = NumberUtils.toLong(d, 1);
return Response.ok(HumanTime.approximately(delta), MediaType.TEXT_PLAIN_TYPE).build();
}
return getStatusResponse(Response.Status.BAD_REQUEST, "Unknown method: " +
((method == null) ? "empty" : method));
}
};
}
private Inflector<ContainerRequestContext, Response> introHandler() {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
Map<String, String> info = new TreeMap<String, String>();
info.put("info", "Para - a backend for busy developers.");
if (Config.getConfigBoolean("print_version", true)) {
info.put("version", StringUtils.replace(getVersion(), "-SNAPSHOT", ""));
}
return Response.ok(info).build();
}
};
}
private Inflector<ContainerRequestContext, Response> typeCrudHandler() {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
String typePlural = pathParam(Config._TYPE, ctx);
App app = getPrincipalApp();
if (app != null && !StringUtils.isBlank(typePlural)) {
String type = ParaObjectUtils.getAllTypes(app).get(typePlural);
if (type == null) {
type = typePlural;
}
return crudHandler(app, type).apply(ctx);
}
return getStatusResponse(Response.Status.NOT_FOUND, "App not found.");
}
};
}
/**
* @param app {@link App}
* @param type a type
* @return response
*/
public static Inflector<ContainerRequestContext, Response> crudHandler(final App app, final String type) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
String id = pathParam(Config._ID, ctx);
if (StringUtils.isBlank(id)) {
if (GET.equals(ctx.getMethod())) {
return searchHandler(app, type).apply(ctx);
} else if (POST.equals(ctx.getMethod())) {
return createHandler(app, type).apply(ctx);
} else if (ctx.getUriInfo().getPath().contains("search")) {
return searchHandler(app, type).apply(ctx);
}
} else {
if (GET.equals(ctx.getMethod())) {
return readHandler(app, type).apply(ctx);
} else if (PUT.equals(ctx.getMethod())) {
return overwriteHandler(app, type).apply(ctx);
} else if (PATCH.equals(ctx.getMethod())) {
return updateHandler(app, type).apply(ctx);
} else if (DELETE.equals(ctx.getMethod())) {
return deleteHandler(app, type).apply(ctx);
}
}
return getStatusResponse(Response.Status.NOT_FOUND, "Type '" + type + "' not found.");
}
};
}
/**
* @return response
*/
public static Inflector<ContainerRequestContext, Response> linksHandler() {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
MultivaluedMap<String, String> params = ctx.getUriInfo().getQueryParameters();
MultivaluedMap<String, String> pathp = ctx.getUriInfo().getPathParameters();
String id = pathp.getFirst(Config._ID);
String type = pathp.getFirst(Config._TYPE);
String id2 = pathp.getFirst("id2");
String type2 = pathp.getFirst("type2");
App app = getPrincipalApp();
if (app == null) {
return getStatusResponse(Response.Status.BAD_REQUEST);
}
String typeSingular = (type == null) ? null : ParaObjectUtils.getAllTypes(app).get(type);
type = (typeSingular == null) ? type : typeSingular;
id2 = StringUtils.isBlank(id2) ? params.getFirst(Config._ID) : id2;
type2 = StringUtils.isBlank(type2) ? params.getFirst(Config._TYPE) : type2;
ParaObject pobj = ParaObjectUtils.toObject(type);
pobj.setId(id);
pobj = getDAO().read(app.getAppIdentifier(), pobj.getId());
Pager pager = new Pager();
pager.setPage(NumberUtils.toLong(params.getFirst("page"), 0));
pager.setSortby(params.getFirst("sort"));
pager.setDesc(Boolean.parseBoolean(params.containsKey("desc") ? params.getFirst("desc") : "true"));
pager.setLimit(NumberUtils.toInt(params.getFirst("limit"), pager.getLimit()));
String childrenOnly = params.getFirst("childrenonly");
if (pobj != null) {
if (POST.equals(ctx.getMethod()) || PUT.equals(ctx.getMethod())) {
return RestUtils.createLinksHandler(pobj, id2);
} else if (GET.equals(ctx.getMethod())) {
return RestUtils.readLinksHandler(pobj, id2, type2, params, pager, childrenOnly != null);
} else if (DELETE.equals(ctx.getMethod())) {
return RestUtils.deleteLinksHandler(pobj, id2, type2, childrenOnly != null);
}
}
return getStatusResponse(Response.Status.NOT_FOUND, "Object not found: " + id);
}
};
}
/**
* @return response
*/
public static Inflector<ContainerRequestContext, Response> meHandler() {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
if (GET.equals(ctx.getMethod())) {
User user = SecurityUtils.getAuthenticatedUser();
App app = SecurityUtils.getAuthenticatedApp();
if (user != null) {
return Response.ok(user).build();
} else if (app != null) {
return Response.ok(app).build();
}
}
return Response.status(Response.Status.UNAUTHORIZED).build();
}
};
}
/**
* @return response
*/
public static Inflector<ContainerRequestContext, Response> readIdHandler() {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = getPrincipalApp();
String id = pathParam(Config._ID, ctx);
if (app != null) {
return getReadResponse(app, getDAO().read(app.getAppIdentifier(), id));
}
return getStatusResponse(Response.Status.NOT_FOUND, "App not found.");
}
};
}
/**
* @param a {@link App}
* @return response
*/
public static Inflector<ContainerRequestContext, Response> getConstrHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
String type = pathParam(Config._TYPE, ctx);
if (app != null) {
if (type != null) {
return Response.ok(app.getAllValidationConstraints(type)).build();
} else {
return Response.ok(app.getAllValidationConstraints()).build();
}
}
return getStatusResponse(Response.Status.NOT_FOUND, "App not found.");
}
};
}
/**
* @param a {@link App}
* @return response
*/
@SuppressWarnings("unchecked")
public static Inflector<ContainerRequestContext, Response> addConstrHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
String type = pathParam(Config._TYPE, ctx);
String field = pathParam("field", ctx);
String cname = pathParam("cname", ctx);
if (app != null) {
Response payloadRes = getEntity(ctx.getEntityStream(), Map.class);
if (payloadRes.getStatusInfo() == Response.Status.OK) {
Map<String, Object> payload = (Map<String, Object>) payloadRes.getEntity();
if (app.addValidationConstraint(type, field, Constraint.build(cname, payload))) {
app.update();
}
}
return Response.ok(app.getAllValidationConstraints(type)).build();
}
return getStatusResponse(Response.Status.NOT_FOUND, "App not found.");
}
};
}
/**
* @param a {@link App}
* @return response
*/
public static Inflector<ContainerRequestContext, Response> removeConstrHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
String type = pathParam(Config._TYPE, ctx);
String field = pathParam("field", ctx);
String cname = pathParam("cname", ctx);
if (app != null) {
if (app.removeValidationConstraint(type, field, cname)) {
app.update();
}
return Response.ok(app.getAllValidationConstraints(type)).build();
}
return getStatusResponse(Response.Status.NOT_FOUND, "App not found.");
}
};
}
/**
* @param a {@link App}
* @return response
*/
public static Inflector<ContainerRequestContext, Response> getPermitHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
String subjectid = pathParam("subjectid", ctx);
if (app != null) {
if (subjectid != null) {
return Response.ok(app.getAllResourcePermissions(subjectid)).build();
} else {
return Response.ok(app.getAllResourcePermissions()).build();
}
}
return getStatusResponse(Response.Status.NOT_FOUND, "App not found.");
}
};
}
/**
* @param a {@link App}
* @return response
*/
public static Inflector<ContainerRequestContext, Response> checkPermitHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
String subjectid = pathParam("subjectid", ctx);
String resourcePath = pathParam(Config._TYPE, ctx);
String httpMethod = pathParam("method", ctx);
if (app != null) {
return Response.ok(app.isAllowedTo(subjectid, resourcePath, httpMethod),
MediaType.TEXT_PLAIN_TYPE).build();
}
return getStatusResponse(Response.Status.NOT_FOUND, "App not found.");
}
};
}
/**
* @param a {@link App}
* @return response
*/
@SuppressWarnings("unchecked")
public static Inflector<ContainerRequestContext, Response> grantPermitHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
String subjectid = pathParam("subjectid", ctx);
String resourcePath = pathParam(Config._TYPE, ctx);
if (app != null) {
Response resp = getEntity(ctx.getEntityStream(), List.class);
if (resp.getStatusInfo() == Response.Status.OK) {
List<String> permission = (List<String>) resp.getEntity();
Set<App.AllowedMethods> set = new HashSet<App.AllowedMethods>(permission.size());
for (String perm : permission) {
if (!StringUtils.isBlank(perm)) {
App.AllowedMethods method = App.AllowedMethods.fromString(perm);
if (method != null) {
set.add(method);
}
}
}
if (!set.isEmpty()) {
if (app.grantResourcePermission(subjectid, resourcePath, EnumSet.copyOf(set))) {
app.update();
}
return Response.ok(app.getAllResourcePermissions(subjectid)).build();
} else {
return getStatusResponse(Response.Status.BAD_REQUEST, "No allowed methods specified.");
}
} else {
return resp;
}
}
return getStatusResponse(Response.Status.NOT_FOUND, "App not found.");
}
};
}
/**
* @param a {@link App}
* @return response
*/
public static Inflector<ContainerRequestContext, Response> revokePermitHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
String subjectid = pathParam("subjectid", ctx);
String type = pathParam(Config._TYPE, ctx);
if (app != null) {
boolean revoked;
if (type != null) {
revoked = app.revokeResourcePermission(subjectid, type);
} else {
revoked = app.revokeAllResourcePermissions(subjectid);
}
if (revoked) {
app.update();
}
return Response.ok(app.getAllResourcePermissions(subjectid)).build();
}
return getStatusResponse(Response.Status.NOT_FOUND, "App not found.");
}
};
}
/**
* @param a {@link App}
* @return response
*/
@SuppressWarnings("unchecked")
public static Inflector<ContainerRequestContext, Response> appSettingsHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
String key = pathParam("key", ctx);
if (app != null) {
if (PUT.equals(ctx.getMethod())) {
Response resp = getEntity(ctx.getEntityStream(), Map.class);
if (resp.getStatusInfo() == Response.Status.OK) {
Map<String, Object> setting = (Map<String, Object>) resp.getEntity();
if (!StringUtils.isBlank(key) && setting.containsKey("value")) {
app.addSetting(key, setting.get("value"));
} else {
app.setSettings(setting);
}
app.update();
return Response.ok().build();
} else {
return getStatusResponse(Response.Status.BAD_REQUEST);
}
} else if (DELETE.equals(ctx.getMethod())) {
app.removeSetting(key);
app.update();
return Response.ok().build();
} else {
if (StringUtils.isBlank(key)) {
return Response.ok(app.getSettings()).build();
} else {
return Response.ok(Collections.singletonMap("value", app.getSetting(key))).build();
}
}
}
return getStatusResponse(Response.Status.FORBIDDEN, "Not allowed.");
}
};
}
private Inflector<ContainerRequestContext, Response> listTypesHandler() {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = getPrincipalApp();
if (app != null) {
return Response.ok(ParaObjectUtils.getAllTypes(app)).build();
}
return getStatusResponse(Response.Status.NOT_FOUND, "App not found.");
}
};
}
private Inflector<ContainerRequestContext, Response> keysHandler() {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = SecurityUtils.getAuthenticatedApp();
if (app != null) {
app.resetSecret();
CoreUtils.getInstance().overwrite(app);
Map<String, String> creds = app.getCredentials();
creds.put("info", "Save the secret key! It is showed only once!");
return Response.ok(creds).build();
}
return getStatusResponse(Response.Status.UNAUTHORIZED, "Not an app.");
}
};
}
private Inflector<ContainerRequestContext, Response> setupHandler() {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
String appid = pathParam(Config._APPID, ctx);
if (!StringUtils.isBlank(appid)) {
App app = SecurityUtils.getAuthenticatedApp();
if (app != null && app.isRootApp()) {
boolean shared = "true".equals(queryParam("shared", ctx));
return Response.ok(setup(appid, queryParam("name", ctx), shared)).build();
} else {
return getStatusResponse(Response.Status.FORBIDDEN,
"Only root app can create and initialize other apps.");
}
}
return Response.ok(setup()).build();
}
};
}
/**
* @param a {@link App}
* @param type a type
* @return response
*/
public static Inflector<ContainerRequestContext, Response> createHandler(final App a, final String type) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
return getCreateResponse(app, type, ctx.getEntityStream());
}
};
}
/**
* @param a {@link App}
* @param type a type
* @return response
*/
public static Inflector<ContainerRequestContext, Response> readHandler(final App a, final String type) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
ParaObject obj = ParaObjectUtils.toObject(type);
obj.setId(pathParam(Config._ID, ctx));
if (app.getId().equals(obj.getId())) {
return getReadResponse(app, app);
}
return getReadResponse(app, getDAO().read(app.getAppIdentifier(), obj.getId()));
}
};
}
/**
* @param a {@link App}
* @param type a type
* @return response
*/
public static Inflector<ContainerRequestContext, Response> updateHandler(final App a, final String type) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
ParaObject obj = ParaObjectUtils.toObject(type);
obj.setType(type);
obj.setId(pathParam(Config._ID, ctx));
// partial update - equivalent to PATCH method
return getUpdateResponse(app, getDAO().read(app.getAppIdentifier(), obj.getId()),
ctx.getEntityStream());
}
};
}
/**
* @param a {@link App}
* @param type a type
* @return response
*/
public static Inflector<ContainerRequestContext, Response> overwriteHandler(final App a, final String type) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
// full update - equivalent to PUT method
App app = (a != null) ? a : getPrincipalApp();
return getOverwriteResponse(app, pathParam(Config._ID, ctx), type, ctx.getEntityStream());
}
};
}
/**
* @param a {@link App}
* @param type a type
* @return response
*/
public static Inflector<ContainerRequestContext, Response> deleteHandler(final App a, final String type) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
ParaObject obj = ParaObjectUtils.toObject(type);
obj.setType(type);
obj.setId(pathParam(Config._ID, ctx));
return getDeleteResponse(app, obj);
}
};
}
/**
* @param a {@link App}
* @return response
*/
public static Inflector<ContainerRequestContext, Response> batchCreateHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
return getBatchCreateResponse(app, ctx.getEntityStream());
}
};
}
/**
* @param a {@link App}
* @return response
*/
public static Inflector<ContainerRequestContext, Response> batchReadHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
return getBatchReadResponse(app, queryParams("ids", ctx));
}
};
}
/**
* @param a {@link App}
* @return response
*/
@SuppressWarnings("unchecked")
public static Inflector<ContainerRequestContext, Response> batchUpdateHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
Response entityRes = getEntity(ctx.getEntityStream(), List.class);
if (entityRes.getStatusInfo() == Response.Status.OK) {
List<Map<String, Object>> newProps = (List<Map<String, Object>>) entityRes.getEntity();
ArrayList<String> ids = new ArrayList<String>(newProps.size());
for (Map<String, Object> props : newProps) {
ids.add((String) props.get(Config._ID));
}
return getBatchUpdateResponse(app, getDAO().readAll(app.getAppIdentifier(), ids, true), newProps);
} else {
return entityRes;
}
}
};
}
/**
* @param a {@link App}
* @return response
*/
public static Inflector<ContainerRequestContext, Response> batchDeleteHandler(final App a) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app = (a != null) ? a : getPrincipalApp();
return getBatchDeleteResponse(app, queryParams("ids", ctx));
}
};
}
/**
* @param app {@link App}
* @param type a type
* @return response
*/
public static Inflector<ContainerRequestContext, Response> searchHandler(final App app, final String type) {
return new Inflector<ContainerRequestContext, Response>() {
public Response apply(ContainerRequestContext ctx) {
App app1 = (app == null) ? getPrincipalApp() : app;
MultivaluedMap<String, String> params = ctx.getUriInfo().getQueryParameters();
String queryType = pathParam("querytype", ctx);
return Response.ok(RestUtils.buildQueryAndSearch(app1, queryType, params, type)).build();
}
};
}
}