/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012-2015 ForgeRock AS. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://forgerock.org/license/CDDLv1.0.html
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at http://forgerock.org/license/CDDLv1.0.html
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*/
package org.forgerock.openidm.script;
import java.lang.IllegalArgumentException;
import java.lang.NoSuchMethodException;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.forgerock.script.scope.Function;import org.forgerock.script.scope.FunctionFactory;import org.forgerock.script.scope.Parameter;import org.forgerock.services.context.Context;
import org.forgerock.json.JsonValue;
import org.forgerock.json.resource.ActionRequest;
import org.forgerock.json.resource.ActionResponse;
import org.forgerock.json.resource.BadRequestException;
import org.forgerock.json.resource.ConnectionFactory;
import org.forgerock.json.resource.CreateRequest;
import org.forgerock.json.resource.DeleteRequest;
import org.forgerock.json.resource.NotFoundException;
import org.forgerock.json.resource.PatchOperation;
import org.forgerock.json.resource.PatchRequest;
import org.forgerock.json.resource.QueryFilters;
import org.forgerock.json.resource.QueryRequest;
import org.forgerock.json.resource.QueryResourceHandler;
import org.forgerock.json.resource.QueryResponse;
import org.forgerock.json.resource.ReadRequest;
import org.forgerock.json.resource.Request;
import org.forgerock.json.resource.Requests;
import org.forgerock.json.resource.ResourceResponse;
import org.forgerock.json.resource.ResourceException;
import org.forgerock.json.resource.UpdateRequest;
import org.forgerock.util.Factory;
import org.forgerock.util.LazyMap;
/**
* Exposes a function that can be provided to a script to invoke.
*
* @author Laszlo Hordos
*/
public final class ResourceFunctions {
private ResourceFunctions() {
}
public static Function<JsonValue> newCreateFunction(ConnectionFactory connectionFactory) {
return new CreateFunction(connectionFactory);
}
/**
* <pre>
* create(String resourceContainer, String newResourceId, Map content[, Map params][, List fieldFilter][, Map context])
* </pre>
*/
private static final class CreateFunction extends AbstractFunction {
/** Serializable class a version number. */
static final long serialVersionUID = 2L;
private CreateFunction(ConnectionFactory connectionFactory) {
super(connectionFactory);
}
@Override
public JsonValue call(Parameter scope, Function<?> callback, Object... arguments)
throws ResourceException, NoSuchMethodException {
String resourceContainer = null;
String newResourceId = null;
JsonValue content = null;
JsonValue params = new JsonValue(null);
List<Object> fieldFilter = null;
Context context = null;
if (arguments.length < 3) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("create",
arguments));
}
for (int i = 0; i < arguments.length; i++) {
Object value = arguments[i];
switch (i) {
case 0:
if (value instanceof String) {
resourceContainer = (String) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"create", arguments));
}
break;
case 1:
if (value instanceof String) {
newResourceId = (String) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"create", arguments));
}
break;
case 2:
if (value instanceof Map) {
content = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isMap()) {
content = (JsonValue) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"create", arguments));
}
break;
case 3:
if (value instanceof Map) {
params = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isMap()) {
params = (JsonValue) value;
} else if (null != value && arguments.length > 4) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"create", arguments));
}
break;
case 4:
if (value instanceof List) {
fieldFilter = (List<Object>) value;
break;
} else if (value instanceof JsonValue && ((JsonValue) value).isList()) {
fieldFilter = ((JsonValue) value).asList();
break;
} else if (null != value && arguments.length > 5) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"create", arguments));
}
case 5:
if (value instanceof Context) {
context = (Context) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"create", arguments));
}
break;
default: // TODO log unused arguments
}
}
return create(scope, resourceContainer, newResourceId, content, params, fieldFilter, context,
callback).getContent();
}
public ResourceResponse create(final Parameter scope, String resourceContainer,
String newResourceId, JsonValue content, JsonValue params, List<Object> fieldFilter,
Context context, final Function<?> callback) throws ResourceException {
CreateRequest cr =
Requests.newCreateRequest(resourceContainer, newResourceId, new JsonValue(
content));
// add fieldFilter
cr.addField(fetchFields(fieldFilter));
for (String name : params.keys()) {
setAdditionalParameter(cr, name, params.get(name));
}
return connectionFactory.getConnection().create(scope.getContext(context), cr);
}
}
public static Function<JsonValue> newReadFunction(ConnectionFactory connectionFactory) {
return new ReadFunction(connectionFactory);
}
/**
* <pre>
* read(String resourceName[, Map params][, List fieldFilter][,Map context])
* </pre>
*/
private static final class ReadFunction extends AbstractFunction {
/** Serializable class a version number. */
static final long serialVersionUID = 2L;
private ReadFunction(ConnectionFactory connectionFactory) {
super(connectionFactory);
}
@Override
public JsonValue call(final Parameter scope, final Function<?> callback,
Object... arguments) throws ResourceException, NoSuchMethodException {
String resourceName = null;
List<Object> fieldFilter = null;
JsonValue params = new JsonValue(null);
Context context = null;
if (arguments.length < 1) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("read",
arguments));
}
for (int i = 0; i < arguments.length; i++) {
Object value = arguments[i];
switch (i) {
case 0:
if (value instanceof String) {
resourceName = (String) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"read", arguments));
}
break;
case 1:
if (value instanceof Map) {
params = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isMap()) {
params = (JsonValue) value;
} else if (null != value && arguments.length > 2) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"read", arguments));
}
break;
case 2:
if (value instanceof List) {
fieldFilter = (List<Object>) value;
break;
} else if (value instanceof JsonValue && ((JsonValue) value).isList()) {
fieldFilter = ((JsonValue) value).asList();
break;
} else if (null != value && arguments.length > 3) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"read", arguments));
}
case 3:
if (value instanceof Context) {
context = (Context) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"read", arguments));
}
break;
default: // TODO log unused arguments
}
}
JsonValue result = null;
try {
result = read(scope, resourceName, params, fieldFilter, context, callback).getContent();
} catch (NotFoundException e) {
// indicates no such record without throwing exception
return null;
}
return result;
}
public ResourceResponse read(final Parameter scope, String resourceName, JsonValue params,
List<Object> fieldFilter, Context context, final Function<?> callback)
throws ResourceException {
ReadRequest rr = Requests.newReadRequest(resourceName);
// add fieldFilter
rr.addField(fetchFields(fieldFilter));
for (String name : params.keys()) {
setAdditionalParameter(rr, name, params.get(name));
}
return connectionFactory.getConnection().read(scope.getContext(context), rr);
}
}
public static Function<JsonValue> newUpdateFunction(ConnectionFactory connectionFactory) {
return new UpdateFunction(connectionFactory);
}
/**
* <pre>
* update(String resourceName, String revision, Map content [, Map params][, List fieldFilter][,Map context])
* </pre>
*/
private static final class UpdateFunction extends AbstractFunction {
/** Serializable class a version number. */
static final long serialVersionUID = 2L;
private UpdateFunction(ConnectionFactory connectionFactory) {
super(connectionFactory);
}
@Override
public JsonValue call(final Parameter scope, final Function<?> callback,
final Object... arguments) throws ResourceException, NoSuchMethodException {
String resourceName = null;
String revision = null;
JsonValue content = null;
JsonValue params = new JsonValue(null);
List<Object> fieldFilter = null;
Context context = null;
if (arguments.length < 3) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("update",
arguments));
}
for (int i = 0; i < arguments.length; i++) {
Object value = arguments[i];
switch (i) {
case 0:
if (value instanceof String) {
resourceName = (String) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"update", arguments));
}
break;
case 1:
if (value instanceof String) {
revision = (String) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"update", arguments));
}
break;
case 2:
if (value instanceof Map) {
content = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isMap()) {
content = (JsonValue) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"update", arguments));
}
break;
case 3:
if (value instanceof Map) {
params = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isMap()) {
params = (JsonValue) value;
} else if (null != value && arguments.length > 4) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"update", arguments));
}
break;
case 4:
if (value instanceof List) {
fieldFilter = (List<Object>) value;
break;
} else if (value instanceof JsonValue && ((JsonValue) value).isList()) {
fieldFilter = ((JsonValue) value).asList();
break;
} else if (null != value && arguments.length > 5) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"update", arguments));
}
case 5:
if (value instanceof Context) {
context = (Context) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"update", arguments));
}
break;
default: // TODO log unused arguments
}
}
return update(scope, resourceName, revision, content, params, fieldFilter, context, callback)
.getContent();
}
private final ResourceResponse update(final Parameter scope, String resourceName, String revision,
JsonValue content, JsonValue params, List<Object> fieldFilter, Context context,
final Function<?> callback) throws ResourceException {
UpdateRequest ur = Requests.newUpdateRequest(resourceName, content);
// add fieldFilter
ur.addField(fetchFields(fieldFilter));
// set revision
ur.setRevision(revision);
// set additional parameters
for (String name : params.keys()) {
setAdditionalParameter(ur, name, params.get(name));
}
return connectionFactory.getConnection().update(scope.getContext(context), ur);
}
}
public static Function<JsonValue> newPatchFunction(ConnectionFactory connectionFactory) {
return new PatchFunction(connectionFactory);
}
/**
* <pre>
* patch(String resourceName, String revision, Map patch[, Map params][, List fieldFilter][,Map context])
* </pre>
*/
private static final class PatchFunction extends AbstractFunction {
/** Serializable class a version number. */
static final long serialVersionUID = 2L;
private PatchFunction(ConnectionFactory connectionFactory) {
super(connectionFactory);
}
@Override
public JsonValue call(Parameter scope, Function<?> callback, Object... arguments)
throws ResourceException, NoSuchMethodException {
String resourceName = null;
String revision = null;
JsonValue patch = null;
JsonValue params = new JsonValue(null);
List<Object> fieldFilter = null;
Context context = null;
if (arguments.length < 3) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("patch",
arguments));
}
for (int i = 0; i < arguments.length; i++) {
Object value = arguments[i];
switch (i) {
case 0:
if (value instanceof String) {
resourceName = (String) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"patch", arguments));
}
break;
case 1:
if (value instanceof String) {
revision = (String) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"patch", arguments));
}
break;
case 2:
if (value instanceof List) {
patch = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isList()) {
patch = (JsonValue) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"patch", arguments));
}
break;
case 3:
if (value instanceof Map) {
params = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isMap()) {
params = (JsonValue) value;
} else if (null != value && arguments.length > 4) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"patch", arguments));
}
break;
case 4:
if (value instanceof List) {
fieldFilter = (List<Object>) value;
break;
} else if (value instanceof JsonValue && ((JsonValue) value).isList()) {
fieldFilter = ((JsonValue) value).asList();
break;
} else if (null != value && arguments.length > 5) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"patch", arguments));
}
case 5:
if (value instanceof Context) {
context = (Context) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"patch", arguments));
}
break;
default: // TODO log unused arguments
}
}
return patch(scope, resourceName, revision, patch, params, fieldFilter, context, callback).getContent();
}
private final ResourceResponse patch(Parameter scope, String resourceName, String revision,
JsonValue patch, JsonValue params, List<Object> fieldFilter, Context context,
final Function<?> callback) throws ResourceException {
// create the request
PatchRequest pr = Requests.newPatchRequest(resourceName);
// add operations
List<PatchOperation> ops = PatchOperation.valueOfList(patch);
pr.addPatchOperation(ops.toArray(new PatchOperation[ops.size()]));
// add fieldFilter
pr.addField(fetchFields(fieldFilter));
// set revision
pr.setRevision(revision);
// set additional params
for (String name : params.keys()) {
setAdditionalParameter(pr, name, params.get(name));
}
return connectionFactory.getConnection().patch(scope.getContext(context), pr);
}
}
public static Function<JsonValue> newQueryFunction(ConnectionFactory connectionFactory) {
return new QueryFunction(connectionFactory);
}
/**
* <pre>
* query(String resourceContainer, Map params [, List fieldFilter][,Map context])
* </pre>
*/
private static final class QueryFunction extends AbstractFunction {
/** Serializable class a version number. */
static final long serialVersionUID = 2L;
private QueryFunction(ConnectionFactory connectionFactory) {
super(connectionFactory);
}
@Override
public JsonValue call(Parameter scope, final Function<?> callback, Object... arguments)
throws ResourceException, NoSuchMethodException {
String resourceContainer = null;
JsonValue params = new JsonValue(null);
List<Object> fieldFilter = null;
Context context = null;
if (arguments.length < 2) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("query",
arguments));
}
for (int i = 0; i < arguments.length; i++) {
Object value = arguments[i];
switch (i) {
case 0:
if (value instanceof String) {
resourceContainer = (String) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"query", arguments));
}
break;
case 1:
if (value instanceof Map) {
params = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isMap()) {
params = (JsonValue) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"query", arguments));
}
break;
case 2:
if (value instanceof List) {
fieldFilter = (List<Object>) value;
break;
} else if (value instanceof JsonValue && ((JsonValue) value).isList()) {
fieldFilter = ((JsonValue) value).asList();
break;
} else if (null != value && arguments.length > 3) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"query", arguments));
}
case 3:
if (value instanceof Context) {
context = (Context) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"query", arguments));
}
break;
default: // TODO log unused arguments
}
}
// warning: if you dont use poll or peek and only iterator()
// (+.remove()) it will leak memory.
LinkedList<Object> results =
null != callback ? null : new LinkedList<Object>();
QueryResponse queryResponse =
query(scope, resourceContainer, params, fieldFilter, context, results, callback);
JsonValue result = new JsonValue(new LinkedHashMap<String, Object>(3));
if (null != queryResponse) {
result.put("pagedResultsCookie", queryResponse.getPagedResultsCookie());
result.put("totalPagedResults", queryResponse.getTotalPagedResults());
}
if (null != results) {
result.put("result", results);
}
return result;
}
private final QueryResponse query(final Parameter scope, String resourceContainer,
JsonValue params, List<Object> fieldFilter, Context context,
final Collection<Object> results, final Function<?> callback)
throws ResourceException {
if (params.isDefined("_queryId") ^ params.isDefined("_queryExpression")
^ params.isDefined("_queryFilter")) {
QueryRequest qr = Requests.newQueryRequest(resourceContainer);
// add fieldFilter
qr.addField(fetchFields(fieldFilter));
for (String name : params.keys()) {
if (name.equalsIgnoreCase("_fields")
&& (null == fieldFilter || fieldFilter.isEmpty())) {
JsonValue fields = params.get(name);
if (fields.isString()) {
try {
qr.addField(fields.asString().split(","));
} catch (final IllegalArgumentException e) {
// FIXME: i18n.
throw new BadRequestException(
"The value '"
+ fields
+ "' for parameter '"
+ name
+ "' could not be parsed as a comma separated list of JSON pointers");
}
} else if (fields.isList()) {
qr.addField(fields.asList().toArray(new String[fields.size()]));
}
} else if (name.equalsIgnoreCase("_sortKeys")) {
JsonValue sortKey = params.get(name);
if (sortKey.isString()) {
try {
qr.addSortKey(sortKey.asString().split(","));
} catch (final IllegalArgumentException e) {
// FIXME: i18n.
throw new BadRequestException("The value '" + sortKey
+ "' for parameter '" + name
+ "' could not be parsed as a comma "
+ "separated list of sort keys");
}
} else if (sortKey.isList()) {
qr.addSortKey(sortKey.asList().toArray(new String[sortKey.size()]));
}
} else if (name.equalsIgnoreCase("_queryId")) {
qr.setQueryId(params.get(name).required().asString());
} else if (name.equalsIgnoreCase("_queryExpression")) {
qr.setQueryExpression(params.get(name).required().asString());
} else if (name.equalsIgnoreCase("_pagedResultsCookie")) {
qr.setPagedResultsCookie(params.get(name).required().asString());
} else if (name.equalsIgnoreCase("_pagedResultsOffset")) {
qr.setPagedResultsOffset(params.get(name).required().asInteger());
} else if (name.equalsIgnoreCase("_pageSize")) {
qr.setPageSize(params.get(name).required().asInteger());
} else if (name.equalsIgnoreCase("_queryFilter")) {
final String s = params.get(name).required().asString();
try {
qr.setQueryFilter(QueryFilters.parse(s));
} catch (final IllegalArgumentException e) {
// FIXME: i18n.
throw new BadRequestException("The value '" + s + "' for parameter '"
+ name + "' could not be parsed as a valid query filter");
}
} else {
setAdditionalParameter(qr, name, params.get(name));
}
}
return connectionFactory.getConnection().query(scope.getContext(context), qr,
new QueryResourceHandler() {
@Override
public boolean handleResource(ResourceResponse resource) {
if (null != callback) {
try {
callback.call(scope, null, resource.getContent());
} catch (ResourceException e) {
// TODO log
return false;
} catch (NoSuchMethodException e) {
// TODO log
return false;
}
} else {
results.add(resource.getContent().getObject());
}
return true;
}
});
} else {
throw new BadRequestException(
"Only one of [_queryId, _queryExpression, _queryFilter] is supported; multiple detected");
}
}
}
public static Function<JsonValue> newDeleteFunction(ConnectionFactory connectionFactory) {
return new DeleteFunction(connectionFactory);
}
/**
* <pre>
* delete(String resourceName, String revision [, Map params][, List fieldFilter][,Map context])
* </pre>
*/
private static final class DeleteFunction extends AbstractFunction {
/** Serializable class a version number. */
static final long serialVersionUID = 2L;
private DeleteFunction(ConnectionFactory connectionFactory) {
super(connectionFactory);
}
@Override
public JsonValue call(Parameter scope, Function<?> callback, Object... arguments)
throws ResourceException, NoSuchMethodException {
String resourceName = null;
String revision = null;
JsonValue params = new JsonValue(null);
List<Object> fieldFilter = null;
Context context = null;
if (arguments.length < 2) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("delete",
arguments));
}
for (int i = 0; i < arguments.length; i++) {
Object value = arguments[i];
switch (i) {
case 0:
if (value instanceof String) {
resourceName = (String) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"delete", arguments));
}
break;
case 1:
if (value instanceof String) {
revision = (String) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"delete", arguments));
}
break;
case 2:
if (value instanceof Map) {
params = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isMap()) {
params = (JsonValue) value;
} else if (null != value && arguments.length > 4) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"patch", arguments));
}
break;
case 3:
if (value instanceof List) {
fieldFilter = (List<Object>) value;
break;
} else if (value instanceof JsonValue && ((JsonValue) value).isList()) {
fieldFilter = ((JsonValue) value).asList();
break;
} else if (null != value && arguments.length > 4) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"delete", arguments));
}
case 4:
if (value instanceof Context) {
context = (Context) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"delete", arguments));
}
break;
default: // TODO log unused arguments
}
}
return delete(scope, resourceName, revision, params, fieldFilter, context, callback)
.getContent();
}
private ResourceResponse delete(Parameter scope, String resourceName, String revision, JsonValue params,
List<Object> fieldFilter, Context context, final Function<?> callback)
throws ResourceException {
DeleteRequest dr = Requests.newDeleteRequest(resourceName);
// add fieldFilter
dr.addField(fetchFields(fieldFilter));
// set revision
dr.setRevision(revision);
// set additional parameters
for (String name : params.keys()) {
setAdditionalParameter(dr, name, params.get(name));
}
return connectionFactory.getConnection().delete(scope.getContext(context), dr);
}
}
public static Function<JsonValue> newActionFunction(ConnectionFactory connectionFactory) {
return new ActionFunction(connectionFactory);
}
/**
* <pre>
* action(String resourceName, [String actionId,] Map content, Map params [, List fieldFilter][,Map context])
* </pre>
*/
private static final class ActionFunction extends AbstractFunction {
/** Serializable class a version number. */
static final long serialVersionUID = 2L;
private ActionFunction(ConnectionFactory connectionFactory) {
super(connectionFactory);
}
@Override
public JsonValue call(Parameter scope, Function<?> callback, Object... arguments)
throws ResourceException, NoSuchMethodException {
String resourceName = null;
String actionId = null;
JsonValue content = null;
JsonValue params = new JsonValue(null);
List<Object> fieldFilter = null;
Context context = null;
if (arguments.length < 3) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("action",
arguments));
}
int pointer = 0;
for (int i = 0; i < arguments.length; i++) {
Object value = arguments[i];
switch (pointer) {
case 0:
if (value instanceof String) {
resourceName = (String) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"action", arguments));
}
break;
case 1:
if (value instanceof String) {
actionId = (String) value;
break;
} else if (null == value) {
break;
} else if (!(value instanceof Map) && (value instanceof JsonValue && !((JsonValue) value).isMap())) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"action", arguments));
} else {
// shift the param pointer
pointer++;
}
case 2:
if (value instanceof Map) {
content = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isMap()) {
content = (JsonValue) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"action", arguments));
}
break;
case 3:
if (value instanceof Map) {
params = new JsonValue(value);
} else if (value instanceof JsonValue && ((JsonValue) value).isMap()) {
params = (JsonValue) value;
} else {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"action", arguments));
}
break;
case 4:
if (value instanceof List) {
fieldFilter = (List<Object>) value;
break;
} else if (value instanceof JsonValue && ((JsonValue) value).isList()) {
fieldFilter = ((JsonValue) value).asList();
break;
} else if (null != value && arguments.length > 5) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"action", arguments));
}
case 5:
if (value instanceof Context) {
context = (Context) value;
} else if (null != value) {
throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(
"action", arguments));
}
break;
default: // TODO log unused arguments
}
pointer++;
}
return action(scope, resourceName, actionId, content, params, fieldFilter, context,
callback).getJsonContent();
}
public ActionResponse action(final Parameter scope, String resourceName, String actionId,
JsonValue content, JsonValue params, List<Object> fieldFilter, Context context,
final Function<?> callback) throws ResourceException {
ActionRequest ar =
Requests.newActionRequest(resourceName,
null != actionId ? actionId : params.get("_action").required().asString());
// add fieldFilter
ar.addField(fetchFields(fieldFilter));
// set additional parameters
for (String name : params.keys()) {
setAdditionalParameter(ar, name, params.get(name));
}
// set content
ar.setContent(content);
return connectionFactory.getConnection().action(scope.getContext(context), ar);
}
}
private static abstract class AbstractFunction implements Function<JsonValue> {
/** Serializable class a version number. */
static final long serialVersionUID = 2L;
final ConnectionFactory connectionFactory;
AbstractFunction(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
protected String[] fetchFields(List<Object> fields) {
if (null != fields && !fields.isEmpty()) {
int idx = 0;
String[] checkedFields = new String[fields.size()];
for (Object o : fields) {
if (o instanceof String) {
checkedFields[idx++] = (String) o;
}
}
return Arrays.copyOfRange(checkedFields, 0, idx);
}
return new String[0];
}
protected void setAdditionalParameter(Request request, String name, JsonValue value) throws BadRequestException {
if (value.isNull()) {
// ignore null values
} else if (value.isString()) {
request.setAdditionalParameter(name, value.asString());
} else if (value.isNumber()) {
request.setAdditionalParameter(name, String.valueOf(value.asNumber()));
} else if (value.isBoolean()) {
request.setAdditionalParameter(name, String.valueOf(value.asBoolean()));
} else {
throw new BadRequestException("The value '" + String.valueOf(value.getObject())
+ "' for additional parameter '" + name
+ "' is not of expected type String");
}
}
}
/*
*
* action(String endPoint[, String id], String type, Map params, Map
* content[, List fieldFilter][,Map context])
*
* create(String endPoint[, String id], Map content[, List fieldFilter][,Map
* context])
*
* delete(String endPoint, String id[, String rev][, List fieldFilter][,Map
* context])
*
* patch(String endPoint[, String id], Map content [, String rev][, List
* fieldFilter][,Map context])
*
* query(String endPoint[, Map params][, String filter][, List
* fieldFilter][,Map context])
*
* read(String endPoint[, String id][, List fieldFilter][,Map context])
*
* update(String endPoint[, String id], Map content [, String rev][, List
* fieldFilter][,Map context])
*
* @return
*/
public static Map<String, Function<JsonValue>> resourceFunctions(final ConnectionFactory connectionFactory) {
return new LazyMap<>(
new Factory<Map<String, Function<JsonValue>>>() {
@Override
public Map<String, Function<JsonValue>> newInstance() {
Map<String, Function<JsonValue>> functions = new HashMap<String, Function<JsonValue>>(7);
functions.put("create", ResourceFunctions.newCreateFunction(connectionFactory));
functions.put("read", ResourceFunctions.newReadFunction(connectionFactory));
functions.put("update", ResourceFunctions.newUpdateFunction(connectionFactory));
functions.put("patch", ResourceFunctions.newPatchFunction(connectionFactory));
functions.put("query", ResourceFunctions.newQueryFunction(connectionFactory));
functions.put("delete", ResourceFunctions.newDeleteFunction(connectionFactory));
functions.put("action", ResourceFunctions.newActionFunction(connectionFactory));
return functions;
}
});
}
}