@Override @SuppressWarnings("unchecked") public PartialRestResponse buildResponse( RoutingResult routingResult, RestLiResponseData responseData) { List<CreateCollectionResponseEnvelope.CollectionCreateResponseItem> collectionCreateResponses = responseData.getCreateCollectionResponseEnvelope().getCreateResponses(); List<CreateIdStatus<Object>> formattedResponses = new ArrayList<CreateIdStatus<Object>>(collectionCreateResponses.size()); // Iterate through the responses and generate the ErrorResponse with the appropriate override // for exceptions. // Otherwise, add the result as is. for (CreateCollectionResponseEnvelope.CollectionCreateResponseItem response : collectionCreateResponses) { if (response.isErrorResponse()) { RestLiServiceException exception = response.getException(); formattedResponses.add( new CreateIdStatus<Object>( exception.getStatus().getCode(), response.getId(), _errorResponseBuilder.buildErrorResponse(exception), ProtocolVersionUtil.extractProtocolVersion(responseData.getHeaders()))); } else { if (response.getRecord() instanceof CreateIdEntityStatus) { CreateIdEntityStatus<?, ?> currentStatus = (CreateIdEntityStatus<?, ?>) response.getRecord(); final ResourceContext resourceContext = routingResult.getContext(); DataMap entityData = currentStatus.getEntity() != null ? currentStatus.getEntity().data() : null; final DataMap data = RestUtils.projectFields( entityData, resourceContext.getProjectionMode(), resourceContext.getProjectionMask()); CreateIdEntityStatus<?, ?> projectedStatus = new CreateIdEntityStatus( currentStatus.getStatus(), currentStatus.getKey(), new AnyRecord(data), currentStatus.getError(), ProtocolVersionUtil.extractProtocolVersion(resourceContext.getRequestHeaders())); formattedResponses.add((CreateIdStatus<Object>) projectedStatus); } else { formattedResponses.add((CreateIdStatus<Object>) response.getRecord()); } } } PartialRestResponse.Builder builder = new PartialRestResponse.Builder(); BatchCreateIdResponse<Object> batchCreateIdResponse = new BatchCreateIdResponse<Object>(formattedResponses); return builder .headers(responseData.getHeaders()) .cookies(responseData.getCookies()) .entity(batchCreateIdResponse) .build(); }
private RestResponseBuilder encodeResult( RestResponseBuilder builder, DataMap dataMap, String acceptTypes) { String bestType = RestUtils.pickBestEncoding(acceptTypes); if (RestConstants.HEADER_VALUE_APPLICATION_PSON.equalsIgnoreCase(bestType)) { builder.setHeader( RestConstants.HEADER_CONTENT_TYPE, RestConstants.HEADER_VALUE_APPLICATION_PSON); builder.setEntity(DataMapUtils.mapToPsonBytes(dataMap)); } else if (RestConstants.HEADER_VALUE_APPLICATION_JSON.equalsIgnoreCase(bestType)) { builder.setHeader( RestConstants.HEADER_CONTENT_TYPE, RestConstants.HEADER_VALUE_APPLICATION_JSON); builder.setEntity(DataMapUtils.mapToBytes(dataMap, _permissiveEncoding)); } else { throw new RoutingException( "No acceptable types can be returned", HttpStatus.S_406_NOT_ACCEPTABLE.getCode()); } return builder; }
/** * Invokes the method with the specified callback and arguments built from the request. * * @param invocableMethod {@link RoutingResult} * @param request {@link RestRequest} * @param callback {@link RestLiCallback} * @param isDebugMode whether the invocation will be done as part of a debug request. * @param filterContext {@link FilterRequestContextInternal} */ public void invoke( final RoutingResult invocableMethod, final RestRequest request, final RequestExecutionCallback<Object> callback, final boolean isDebugMode, final FilterRequestContextInternal filterContext) { RequestExecutionReportBuilder requestExecutionReportBuilder = null; if (isDebugMode) { requestExecutionReportBuilder = new RequestExecutionReportBuilder(); } // Fast fail if the request headers are invalid. try { RestUtils.validateRequestHeadersAndUpdateResourceContext( request.getHeaders(), (ServerResourceContext) invocableMethod.getContext()); } catch (RestLiServiceException e) { callback.onError(e, getRequestExecutionReport(requestExecutionReportBuilder)); return; } // Request headers are valid. Proceed with the invocation of the filters and eventually the // resource. ResourceMethodDescriptor resourceMethodDescriptor = invocableMethod.getResourceMethod(); RestLiArgumentBuilder adapter = _methodAdapterRegistry.getArgumentBuilder(resourceMethodDescriptor.getType()); if (adapter == null) { throw new IllegalArgumentException( "Unsupported method type: " + resourceMethodDescriptor.getType()); } RestLiRequestData requestData = adapter.extractRequestData(invocableMethod, request); filterContext.setRequestData(requestData); // Kick off the request filter iterator, which finally would invoke the resource. RestLiRequestFilterChainCallback restLiRequestFilterChainCallback = new RestLiRequestFilterChainCallbackImpl( invocableMethod, adapter, callback, requestExecutionReportBuilder); new RestLiRequestFilterChain(_requestFilters, restLiRequestFilterChainCallback) .onRequest(filterContext); }
/** * This method recursively removes all values from a RecordTemplate that do not match some field * in the schema via an all positive projection generated from the schema unless whitelisted by * the override parameter. * * <p>RecordTemplates with readonly datamaps should not invoke this. An exception will be thrown * if this is done. * * @param recordTemplate represents the RecordTemplate that will be trimmed. * @param override represents the MaskTree that defines how fields outside the schema should be * preserved. * @param failOnMismatch true if an exception should be thrown if there is a data-schema mismatch. */ public static void trimRecordTemplate( RecordTemplate recordTemplate, MaskTree override, final boolean failOnMismatch) { if (recordTemplate.schema() == null) { return; } DataMap overrideResults = RestUtils.projectFields(recordTemplate.data(), ProjectionMode.AUTOMATIC, override); Builder.create(recordTemplate.data(), recordTemplate.schema(), IterationOrder.PRE_ORDER) .filterBy( new Predicate() { @Override public boolean evaluate(DataElement element) { if (failOnMismatch && element.getSchema() == null) { throw new IllegalArgumentException(); } return element.getSchema() == null; } }) .remove(); CheckedUtil.putAllWithoutChecking(recordTemplate.data(), overrideResults); }
/** * Build arguments for resource method invocation. Combines various types of arguments into a * single array. * * @param positionalArguments pass-through arguments coming from {@link RestLiArgumentBuilder} * @param resourceMethod the resource method * @param context {@link ResourceContext} * @param template {@link DynamicRecordTemplate} * @return array of method argument for method invocation. */ @SuppressWarnings("deprecation") public static Object[] buildArgs( final Object[] positionalArguments, final ResourceMethodDescriptor resourceMethod, final ResourceContext context, final DynamicRecordTemplate template) { List<Parameter<?>> parameters = resourceMethod.getParameters(); Object[] arguments = Arrays.copyOf(positionalArguments, parameters.size()); fixUpComplexKeySingletonArraysInArguments(arguments); for (int i = positionalArguments.length; i < parameters.size(); ++i) { Parameter<?> param = parameters.get(i); try { if (param.getParamType() == Parameter.ParamType.KEY || param.getParamType() == Parameter.ParamType.ASSOC_KEY_PARAM) { Object value = context.getPathKeys().get(param.getName()); if (value != null) { arguments[i] = value; continue; } } else if (param.getParamType() == Parameter.ParamType.CALLBACK) { continue; } else if (param.getParamType() == Parameter.ParamType.PARSEQ_CONTEXT_PARAM || param.getParamType() == Parameter.ParamType.PARSEQ_CONTEXT) { continue; // don't know what to fill in yet } else if (param.getParamType() == Parameter.ParamType.HEADER) { HeaderParam headerParam = param.getAnnotations().get(HeaderParam.class); String value = context.getRequestHeaders().get(headerParam.value()); arguments[i] = value; continue; } // Since we have multiple different types of MaskTrees that can be passed into resource // methods, // we must evaluate based on the param type (annotation used) else if (param.getParamType() == Parameter.ParamType.PROJECTION || param.getParamType() == Parameter.ParamType.PROJECTION_PARAM) { arguments[i] = context.getProjectionMask(); continue; } else if (param.getParamType() == Parameter.ParamType.METADATA_PROJECTION_PARAM) { arguments[i] = context.getMetadataProjectionMask(); continue; } else if (param.getParamType() == Parameter.ParamType.PAGING_PROJECTION_PARAM) { arguments[i] = context.getPagingProjectionMask(); continue; } else if (param.getParamType() == Parameter.ParamType.CONTEXT || param.getParamType() == Parameter.ParamType.PAGING_CONTEXT_PARAM) { PagingContext ctx = RestUtils.getPagingContext(context, (PagingContext) param.getDefaultValue()); arguments[i] = ctx; continue; } else if (param.getParamType() == Parameter.ParamType.PATH_KEYS || param.getParamType() == Parameter.ParamType.PATH_KEYS_PARAM) { arguments[i] = context.getPathKeys(); continue; } else if (param.getParamType() == Parameter.ParamType.RESOURCE_CONTEXT || param.getParamType() == Parameter.ParamType.RESOURCE_CONTEXT_PARAM) { arguments[i] = context; continue; } else if (param.getParamType() == Parameter.ParamType.VALIDATOR_PARAM) { RestLiDataValidator validator = new RestLiDataValidator( resourceMethod.getResourceModel().getResourceClass().getAnnotations(), resourceMethod.getResourceModel().getValueClass(), resourceMethod.getMethodType()); arguments[i] = validator; continue; } else if (param.getParamType() == Parameter.ParamType.POST) { // handle action parameters if (template != null) { DataMap data = template.data(); if (data.containsKey(param.getName())) { arguments[i] = template.getValue(param); continue; } } } else if (param.getParamType() == Parameter.ParamType.QUERY) { Object value; if (DataTemplate.class.isAssignableFrom(param.getType())) { value = buildDataTemplateArgument(context, param); } else { value = buildRegularArgument(context, param); } if (value != null) { arguments[i] = value; continue; } } else if (param.getParamType() == Parameter.ParamType.BATCH || param.getParamType() == Parameter.ParamType.RESOURCE_KEY) { // should not come to this routine since it should be handled by passing in // positionalArguments throw new RoutingException( "Parameter '" + param.getName() + "' should be passed in as a positional argument", HttpStatus.S_400_BAD_REQUEST.getCode()); } else { // unknown param type throw new RoutingException( "Parameter '" + param.getName() + "' has an unknown parameter type '" + param.getParamType().name() + "'", HttpStatus.S_400_BAD_REQUEST.getCode()); } } catch (TemplateRuntimeException e) { throw new RoutingException( "Parameter '" + param.getName() + "' is invalid", HttpStatus.S_400_BAD_REQUEST.getCode()); } try { // Handling null-valued parameters not provided in resource context or entity body // check if it is optional parameter if (param.isOptional() && param.hasDefaultValue()) { arguments[i] = param.getDefaultValue(); } else if (param.isOptional() && !param.getType().isPrimitive()) { // optional primitive parameter must have default value or provided arguments[i] = null; } else { throw new RoutingException( "Parameter '" + param.getName() + "' is required", HttpStatus.S_400_BAD_REQUEST.getCode()); } } catch (ResourceConfigException e) { // Parameter default value format exception should result in server error code 500. throw new RestLiServiceException( HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Parameter '" + param.getName() + "' default value is invalid", e); } } return arguments; }