use of com.linkedin.data.DataMap in project rest.li by linkedin.
the class RestLiAnnotationReader method addFinderResourceMethod.
private static void addFinderResourceMethod(final ResourceModel model, final Method method) {
Finder finderAnno = method.getAnnotation(Finder.class);
if (finderAnno == null) {
return;
}
String queryType = finderAnno.value();
List<Parameter<?>> queryParameters = getParameters(model, method, ResourceMethod.FINDER);
if (queryType != null) {
Class<? extends RecordTemplate> metadataType = null;
final Class<?> returnClass = getLogicalReturnClass(method);
if (CollectionResult.class.isAssignableFrom(returnClass)) {
final List<Class<?>> typeArguments = ReflectionUtils.getTypeArguments(CollectionResult.class, returnClass.asSubclass(CollectionResult.class));
final Class<?> metadataClass;
if (typeArguments == null || typeArguments.get(1) == null) {
// the return type may leave metadata type as parameterized and specify in runtime
metadataClass = ((Class<?>) ((ParameterizedType) getLogicalReturnType(method)).getActualTypeArguments()[1]);
} else {
metadataClass = typeArguments.get(1);
}
if (!metadataClass.equals(NoMetadata.class)) {
metadataType = metadataClass.asSubclass(RecordTemplate.class);
}
}
DataMap annotationsMap = ResourceModelAnnotation.getAnnotationsMap(method.getAnnotations());
addDeprecatedAnnotation(annotationsMap, method);
ResourceMethodDescriptor finderMethodDescriptor = ResourceMethodDescriptor.createForFinder(method, queryParameters, queryType, metadataType, getInterfaceType(method), annotationsMap);
validateFinderMethod(finderMethodDescriptor, model);
if (!Modifier.isPublic(method.getModifiers())) {
throw new ResourceConfigException(String.format("Resource '%s' contains non-public finder method '%s'.", model.getName(), method.getName()));
}
model.addResourceMethodDescriptor(finderMethodDescriptor);
}
}
use of com.linkedin.data.DataMap in project rest.li by linkedin.
the class ActionArgumentBuilder method extractRequestData.
@Override
public RestLiRequestData extractRequestData(RoutingResult routingResult, RestRequest request) {
ResourceMethodDescriptor resourceMethodDescriptor = routingResult.getResourceMethod();
final DataMap data;
if (request.getEntity() == null || request.getEntity().length() == 0) {
data = new DataMap();
} else {
data = DataMapUtils.readMap(request);
}
DynamicRecordTemplate template = new DynamicRecordTemplate(data, resourceMethodDescriptor.getRequestDataSchema());
ValidationResult result = ValidateDataAgainstSchema.validate(data, template.schema(), new ValidationOptions(RequiredMode.IGNORE, CoercionMode.NORMAL));
if (!result.isValid()) {
throw new RoutingException("Parameters of method '" + resourceMethodDescriptor.getActionName() + "' failed validation with error '" + result.getMessages() + "'", HttpStatus.S_400_BAD_REQUEST.getCode());
}
return new RestLiRequestDataImpl.Builder().entity(template).build();
}
use of com.linkedin.data.DataMap in project rest.li by linkedin.
the class RestLiRouter method parseBatchKeysParameter.
private void parseBatchKeysParameter(final ResourceModel resource, final ServerResourceContext context) {
Class<?> keyClass = resource.getKeyClass();
ProtocolVersion version = context.getRestliProtocolVersion();
final Set<Object> batchKeys;
try {
if (context.getParameters().containsKey(RestConstants.ALT_KEY_PARAM)) {
batchKeys = parseAlternativeBatchKeys(resource, context);
} else if (ComplexResourceKey.class.equals(keyClass)) {
// Parse all query parameters into a data map.
DataMap allParametersDataMap = context.getParameters();
// Get the batch request keys from the IDS list at the root of the map.
DataList batchIds = allParametersDataMap.getDataList(RestConstants.QUERY_BATCH_IDS_PARAM);
if (batchIds == null) {
batchKeys = null;
} else if (batchIds.isEmpty()) {
batchKeys = Collections.emptySet();
} else {
batchKeys = new HashSet<Object>();
// Validate the complex keys and put them into the context batch keys
for (Object complexKey : batchIds) {
if (!(complexKey instanceof DataMap)) {
log.warn("Invalid structure of key '" + complexKey.toString() + "', skipping key.");
context.getBatchKeyErrors().put(complexKey, new RestLiServiceException(HttpStatus.S_400_BAD_REQUEST));
continue;
}
batchKeys.add(ComplexResourceKey.buildFromDataMap((DataMap) complexKey, ComplexKeySpec.forClassesMaybeNull(resource.getKeyKeyClass(), resource.getKeyParamsClass())));
}
}
} else if (CompoundKey.class.equals(keyClass) && version.compareTo(AllProtocolVersions.RESTLI_PROTOCOL_2_0_0.getProtocolVersion()) >= 0) {
DataMap allParametersDataMap = context.getParameters();
// Get the batch request keys from the IDS list at the root of the map.
DataList batchIds = allParametersDataMap.getDataList(RestConstants.QUERY_BATCH_IDS_PARAM);
if (batchIds == null) {
batchKeys = null;
} else if (batchIds.isEmpty()) {
batchKeys = Collections.emptySet();
} else {
batchKeys = new HashSet<Object>();
// Validate the compound keys and put them into the contex batch keys
for (Object compoundKey : batchIds) {
if (!(compoundKey instanceof DataMap)) {
log.warn("Invalid structure of key '" + compoundKey.toString() + "', skipping key.");
context.getBatchKeyErrors().put(compoundKey.toString(), new RestLiServiceException(HttpStatus.S_400_BAD_REQUEST));
continue;
}
CompoundKey finalKey;
try {
finalKey = ArgumentUtils.dataMapToCompoundKey((DataMap) compoundKey, resource.getKeys());
} catch (IllegalArgumentException e) {
log.warn("Invalid structure of key '" + compoundKey.toString() + "', skipping key.");
context.getBatchKeyErrors().put(compoundKey.toString(), new RestLiServiceException(HttpStatus.S_400_BAD_REQUEST));
continue;
}
batchKeys.add(finalKey);
}
}
} else // collection batch get in v2, collection or association batch get in v1
if (context.hasParameter(RestConstants.QUERY_BATCH_IDS_PARAM)) {
batchKeys = new HashSet<Object>();
List<String> ids = context.getParameterValues(RestConstants.QUERY_BATCH_IDS_PARAM);
if (version.compareTo(AllProtocolVersions.RESTLI_PROTOCOL_2_0_0.getProtocolVersion()) >= 0) {
for (String id : ids) {
Key key = resource.getPrimaryKey();
Object value;
try {
// in v2, compound keys have already been converted and dealt with, so all we need to do here is convert simple values.
value = ArgumentUtils.convertSimpleValue(id, key.getDataSchema(), key.getType());
batchKeys.add(value);
} catch (NumberFormatException e) {
throw new RoutingException("NumberFormatException parsing batch key '" + id + "'", HttpStatus.S_400_BAD_REQUEST.getCode(), e);
} catch (IllegalArgumentException e) {
throw new RoutingException("IllegalArgumentException parsing batch key '" + id + "'", HttpStatus.S_400_BAD_REQUEST.getCode(), e);
}
}
} else {
for (String id : ids) {
try {
// in v1, compound keys have not been fully parsed or dealt with yet, so we need to take them into account.
Object value = parseKeyFromBatchV1(id, resource);
batchKeys.add(value);
} catch (NumberFormatException e) {
log.warn("Caught NumberFormatException parsing batch key '" + id + "', skipping key.");
context.getBatchKeyErrors().put(id, new RestLiServiceException(HttpStatus.S_400_BAD_REQUEST, null, e));
} catch (IllegalArgumentException e) {
log.warn("Caught IllegalArgumentException parsing batch key '" + id + "', skipping key.");
context.getBatchKeyErrors().put(id, new RestLiServiceException(HttpStatus.S_400_BAD_REQUEST, null, e));
} catch (PathSegmentSyntaxException e) {
log.warn("Caught IllegalArgumentException parsing batch key '" + id + "', skipping key.");
context.getBatchKeyErrors().put(id, new RestLiServiceException(HttpStatus.S_400_BAD_REQUEST, null, e));
}
}
}
} else {
batchKeys = null;
}
} catch (TemplateRuntimeException e) {
// thrown from DateTemplateUtil.coerceOutput
throw new RoutingException("Batch key parameter value is invalid", HttpStatus.S_400_BAD_REQUEST.getCode(), e);
}
context.getPathKeys().setBatchKeys(batchKeys);
}
use of com.linkedin.data.DataMap in project rest.li by linkedin.
the class ResourceModelEncoder method createParameters.
private ParameterSchemaArray createParameters(final ResourceMethodDescriptor resourceMethodDescriptor) {
ParameterSchemaArray parameterSchemaArray = new ParameterSchemaArray();
for (Parameter<?> param : resourceMethodDescriptor.getParameters()) {
//only custom parameters need to be specified in the IDL
if (!param.isCustom()) {
continue;
}
// assocKeys are listed outside the parameters list
if (param.getParamType() == Parameter.ParamType.KEY || param.getParamType() == Parameter.ParamType.ASSOC_KEY_PARAM) {
continue;
}
ParameterSchema paramSchema = new ParameterSchema();
paramSchema.setName(param.getName());
paramSchema.setType(buildDataSchemaType(param.getType(), param.getDataSchema()));
final Object defaultValueData = param.getDefaultValueData();
if (defaultValueData == null && param.isOptional()) {
paramSchema.setOptional(true);
} else if (defaultValueData != null) {
paramSchema.setDefault(defaultValueData.toString());
}
String paramDoc = _docsProvider.getParamDoc(resourceMethodDescriptor.getMethod(), param.getName());
if (paramDoc != null) {
paramSchema.setDoc(paramDoc);
}
final DataMap customAnnotation = param.getCustomAnnotationData();
if (!customAnnotation.isEmpty()) {
paramSchema.setAnnotations(new CustomAnnotationContentSchemaMap(customAnnotation));
}
parameterSchemaArray.add(paramSchema);
}
return parameterSchemaArray;
}
use of com.linkedin.data.DataMap in project rest.li by linkedin.
the class ResourceModelEncoder method loadOrBuildResourceSchema.
/**
* Checks if a matching .restspec.json file exists in the classpath for the given {@link ResourceModel}.
* If one is found it is loaded. If not, one is built from the {@link ResourceModel}.
*
* The .restspec.json is preferred because it contains the exact idl that was generated for the resource
* and also includees includes javadoc from the server class in the restspec.json.
*
* @param resourceModel provides the name and namespace of the schema to load or build
* @return the {@link ResourceSchema} for the given {@link ResourceModel}
*/
public ResourceSchema loadOrBuildResourceSchema(final ResourceModel resourceModel) {
StringBuilder resourceFilePath = new StringBuilder();
if (resourceModel.getNamespace() != null) {
resourceFilePath.append(resourceModel.getNamespace());
resourceFilePath.append(".");
}
resourceFilePath.append(resourceModel.getName());
resourceFilePath.append(RestConstants.RESOURCE_MODEL_FILENAME_EXTENSION);
try {
InputStream stream = this.getClass().getClassLoader().getResourceAsStream(resourceFilePath.toString());
if (stream == null) {
// restspec.json file not found, building one instead
return buildResourceSchema(resourceModel);
} else {
DataMap resourceSchemaDataMap = codec.bytesToMap(IOUtils.toByteArray(stream));
return new ResourceSchema(resourceSchemaDataMap);
}
} catch (IOException e) {
throw new RuntimeException("Failed to read " + resourceFilePath.toString() + " from classpath.", e);
}
}
Aggregations