use of com.linkedin.restli.server.Key in project rest.li by linkedin.
the class ArgumentUtils method parseCompoundKey.
/**
* Parse {@link CompoundKey} from its String representation.
*
* @param urlString {@link CompoundKey} string representation
* @param keys {@link CompoundKey} constituent keys' classes keyed on their names
* @param errorMessageBuilder {@link StringBuilder} to build error message if necessary
* @param simpleKeyDelimiterPattern delimiter of constituent keys in the compound key
* @param keyValueDelimiterPattern delimiter of key and value in a constituent key
* @return {@link CompoundKey} parsed from the input string
*/
public static CompoundKey parseCompoundKey(final String urlString, final Collection<Key> keys, final StringBuilder errorMessageBuilder, final Pattern simpleKeyDelimiterPattern, final Pattern keyValueDelimiterPattern) throws RoutingException {
String[] simpleKeys = simpleKeyDelimiterPattern.split(urlString.trim());
CompoundKey compoundKey = new CompoundKey();
for (String simpleKey : simpleKeys) {
String[] nameValuePair = keyValueDelimiterPattern.split(simpleKey.trim());
if (simpleKey.trim().length() == 0 || nameValuePair.length != 2) {
errorMessageBuilder.append("Bad key format '");
errorMessageBuilder.append(urlString);
errorMessageBuilder.append("'");
return null;
}
// Simple key names and values are URL-encoded prior to being included in the URL on
// the client, to prevent collision with any of the delimiter characters (bulk,
// compound key and simple key-value). So, must decode them
String name;
try {
name = URLDecoder.decode(nameValuePair[0], RestConstants.DEFAULT_CHARSET_NAME);
} catch (UnsupportedEncodingException e) {
//should not happen, since we are using "UTF-8" as the encoding
throw new RestLiInternalException(e);
}
// Key is not found in the set defined for the resource
Key currentKey = getKeyWithName(keys, name);
if (currentKey == null) {
errorMessageBuilder.append("Unknown key part named '");
errorMessageBuilder.append(name);
errorMessageBuilder.append("'");
return null;
}
String decodedStringValue;
try {
decodedStringValue = URLDecoder.decode(nameValuePair[1], RestConstants.DEFAULT_CHARSET_NAME);
} catch (UnsupportedEncodingException e) {
//should not happen, since we are using "UTF-8" as the encoding
throw new RestLiInternalException(e);
}
compoundKey.append(name, convertSimpleValue(decodedStringValue, currentKey.getDataSchema(), currentKey.getType()));
}
return compoundKey;
}
use of com.linkedin.restli.server.Key in project rest.li by linkedin.
the class RestLiAnnotationReader method checkIfKeyIsValid.
private static void checkIfKeyIsValid(String paramName, final Class<?> paramType, ResourceModel model) {
ResourceModel nextModel = model.getParentResourceModel();
while (nextModel != null) {
Set<Key> keys = nextModel.getKeys();
for (Key key : keys) {
if (key.getName().equals(paramName)) {
return;
}
}
nextModel = nextModel.getParentResourceModel();
}
throw new ResourceConfigException("Parameter " + paramName + " not found in path keys of class " + model.getResourceClass());
}
use of com.linkedin.restli.server.Key in project rest.li by linkedin.
the class RestLiAnnotationReader method processCollection.
@SuppressWarnings("unchecked")
private static ResourceModel processCollection(final Class<? extends KeyValueResource<?, ?>> collectionResourceClass, ResourceModel parentResourceModel) {
Class<?> keyClass;
Class<? extends RecordTemplate> keyKeyClass = null;
Class<? extends RecordTemplate> keyParamsClass = null;
Class<? extends RecordTemplate> valueClass;
Class<?> complexKeyResourceBase = null;
// type V and the resource key type is ComplexResourceKey<K,P>
if (ComplexKeyResource.class.isAssignableFrom(collectionResourceClass)) {
complexKeyResourceBase = ComplexKeyResource.class;
} else if (ComplexKeyResourceAsync.class.isAssignableFrom(collectionResourceClass)) {
complexKeyResourceBase = ComplexKeyResourceAsync.class;
} else if (ComplexKeyResourceTask.class.isAssignableFrom(collectionResourceClass)) {
complexKeyResourceBase = ComplexKeyResourceTask.class;
} else if (ComplexKeyResourcePromise.class.isAssignableFrom(collectionResourceClass)) {
complexKeyResourceBase = ComplexKeyResourcePromise.class;
}
if (complexKeyResourceBase != null) {
List<Class<?>> kvParams;
if (complexKeyResourceBase.equals(ComplexKeyResource.class)) {
kvParams = ReflectionUtils.getTypeArguments(ComplexKeyResource.class, (Class<? extends ComplexKeyResource<?, ?, ?>>) collectionResourceClass);
} else if (complexKeyResourceBase.equals(ComplexKeyResourceAsync.class)) {
kvParams = ReflectionUtils.getTypeArguments(ComplexKeyResourceAsync.class, (Class<? extends ComplexKeyResourceAsync<?, ?, ?>>) collectionResourceClass);
} else if (complexKeyResourceBase.equals(ComplexKeyResourceTask.class)) {
kvParams = ReflectionUtils.getTypeArguments(ComplexKeyResourceTask.class, (Class<? extends ComplexKeyResourceTask<?, ?, ?>>) collectionResourceClass);
} else {
kvParams = ReflectionUtils.getTypeArguments(ComplexKeyResourcePromise.class, (Class<? extends ComplexKeyResourcePromise<?, ?, ?>>) collectionResourceClass);
}
keyClass = ComplexResourceKey.class;
keyKeyClass = kvParams.get(0).asSubclass(RecordTemplate.class);
keyParamsClass = kvParams.get(1).asSubclass(RecordTemplate.class);
valueClass = kvParams.get(2).asSubclass(RecordTemplate.class);
} else // Otherwise, it's a KeyValueResource, whose parameters are resource key and resource
// value
{
List<Type> actualTypeArguments = ReflectionUtils.getTypeArgumentsParametrized(KeyValueResource.class, collectionResourceClass);
keyClass = ReflectionUtils.getClass(actualTypeArguments.get(0));
if (RecordTemplate.class.isAssignableFrom(keyClass)) {
// ComplexResourceKey
throw new ResourceConfigException("Class '" + collectionResourceClass.getName() + "' should implement 'ComplexKeyResource' as a complex key '" + keyClass.getName() + "' is being used.");
} else if (TyperefInfo.class.isAssignableFrom(keyClass)) {
throw new ResourceConfigException("Typeref '" + keyClass.getName() + "' cannot be key type for class '" + collectionResourceClass.getName() + "'.");
}
if (keyClass.equals(ComplexResourceKey.class)) {
@SuppressWarnings("unchecked") Type[] typeArguments = ((ParameterizedType) actualTypeArguments.get(0)).getActualTypeArguments();
keyKeyClass = ReflectionUtils.getClass(typeArguments[0]).asSubclass(RecordTemplate.class);
keyParamsClass = ReflectionUtils.getClass(typeArguments[1]).asSubclass(RecordTemplate.class);
}
valueClass = ReflectionUtils.getClass(actualTypeArguments.get(1)).asSubclass(RecordTemplate.class);
}
ResourceType resourceType = getResourceType(collectionResourceClass);
RestLiAnnotationData annotationData;
if (collectionResourceClass.isAnnotationPresent(RestLiCollection.class)) {
annotationData = new RestLiAnnotationData(collectionResourceClass.getAnnotation(RestLiCollection.class));
} else if (collectionResourceClass.isAnnotationPresent(RestLiAssociation.class)) {
annotationData = new RestLiAnnotationData(collectionResourceClass.getAnnotation(RestLiAssociation.class));
} else {
throw new ResourceConfigException("No valid annotation on resource class '" + collectionResourceClass.getName() + "'");
}
String name = annotationData.name();
String namespace = annotationData.namespace();
String keyName;
if (annotationData.keyName() == null) {
keyName = name + "Id";
} else {
keyName = annotationData.keyName();
}
Key primaryKey = buildKey(name, keyName, keyClass, annotationData.typerefInfoClass());
Set<Key> keys = new HashSet<Key>();
if (annotationData.keys() == null) {
keys.add(primaryKey);
} else {
keys.addAll(buildKeys(name, annotationData.keys()));
}
Class<?> parentResourceClass = annotationData.parent().equals(RestAnnotations.ROOT.class) ? null : annotationData.parent();
ResourceModel collectionModel = new ResourceModel(primaryKey, keyKeyClass, keyParamsClass, keys, valueClass, collectionResourceClass, parentResourceClass, name, resourceType, namespace);
collectionModel.setParentResourceModel(parentResourceModel);
addResourceMethods(collectionResourceClass, collectionModel);
log.info("Processed collection resource '" + collectionResourceClass.getName() + "'");
return collectionModel;
}
use of com.linkedin.restli.server.Key in project rest.li by linkedin.
the class ArgumentBuilder method parseEntityStringKey.
/**
* Parses the provided string key value and returns its corresponding typed key instance. This method should only be
* used to parse keys which appear in the request body.
*
* @param stringKey Key string from the entity body
* @param routingResult {@link RoutingResult} instance for the current request
* @param version {@link ProtocolVersion} instance of the current request
* @return An instance of key's corresponding type
*/
static Object parseEntityStringKey(final String stringKey, final RoutingResult routingResult, final ProtocolVersion version) {
ResourceModel resourceModel = routingResult.getResourceMethod().getResourceModel();
ResourceContext resourceContext = routingResult.getContext();
try {
Key primaryKey = resourceModel.getPrimaryKey();
String altKeyName = resourceContext.getParameter(RestConstants.ALT_KEY_PARAM);
if (altKeyName != null) {
return ArgumentUtils.translateFromAlternativeKey(ArgumentUtils.parseAlternativeKey(stringKey, altKeyName, resourceModel, version), altKeyName, resourceModel);
} else if (ComplexResourceKey.class.equals(primaryKey.getType())) {
return ComplexResourceKey.parseString(stringKey, resourceModel.getKeyKeyClass(), resourceModel.getKeyParamsClass(), version);
} else if (CompoundKey.class.equals(primaryKey.getType())) {
return ArgumentUtils.parseCompoundKey(stringKey, resourceModel.getKeys(), version);
} else {
// The conversion of simple keys doesn't include URL decoding as the current version of Rest.li clients don't
// encode simple keys which appear in the request body for BATCH UPDATE and BATCH PATCH requests.
Key key = resourceModel.getPrimaryKey();
return ArgumentUtils.convertSimpleValue(stringKey, key.getDataSchema(), key.getType());
}
} catch (InvalidAlternativeKeyException | AlternativeKeyCoercerException | PathSegment.PathSegmentSyntaxException | IllegalArgumentException e) {
throw new RoutingException(String.format("Invalid key: '%s'", stringKey), HttpStatus.S_400_BAD_REQUEST.getCode());
}
}
use of com.linkedin.restli.server.Key in project rest.li by linkedin.
the class TestBatchPatchArgumentBuilder method argumentData.
@DataProvider(name = "argumentData")
private Object[][] argumentData() {
Map<String, Object> aMap1 = new HashMap<>();
aMap1.put("a", "someString");
Map<String, Object> setMap1 = new HashMap<>();
setMap1.put("$set", new DataMap(aMap1));
Map<String, Object> patchMap1 = new HashMap<>();
patchMap1.put("patch", new DataMap(setMap1));
PatchRequest<MyComplexKey> patch1 = new PatchRequest<>(new DataMap(patchMap1));
Map<String, Object> aMap2 = new HashMap<>();
aMap2.put("a", "someOtherString");
Map<String, Object> setMap2 = new HashMap<>();
setMap2.put("$set", new DataMap(aMap2));
Map<String, Object> data2 = new HashMap<>();
data2.put("patch", new DataMap(setMap2));
PatchRequest<MyComplexKey> patch2 = new PatchRequest<>(new DataMap(data2));
@SuppressWarnings("rawtypes") PatchRequest[] patches = new PatchRequest[] { patch1, patch2 };
Object[] simpleKeys = new Object[] { "simple", "(s:pe%cial)" };
Object[] compoundKeys = new Object[] { new CompoundKey().append("string1", "apples").append("string2", "oranges"), new CompoundKey().append("string1", "simple").append("string2", "(s:pe%cial)") };
Object[] complexResourceKeys = new Object[] { new ComplexResourceKey<>(new MyComplexKey().setA("simple").setB(111L), new EmptyRecord()), new ComplexResourceKey<>(new MyComplexKey().setA("(s:pe%cial)").setB(222L), new EmptyRecord()) };
return new Object[][] { { AllProtocolVersions.RESTLI_PROTOCOL_1_0_0.getProtocolVersion(), new Key("stringKey", String.class, new StringDataSchema()), null, "{\"entities\":{\"simple\":{\"patch\":{\"$set\":{\"a\":\"someString\"}}}," + "\"(s:pe%cial)\":{\"patch\":{\"$set\":{\"a\":\"someOtherString\"}}}}}", simpleKeys, patches }, { AllProtocolVersions.RESTLI_PROTOCOL_2_0_0.getProtocolVersion(), new Key("stringKey", String.class, new StringDataSchema()), null, "{\"entities\":{\"simple\":{\"patch\":{\"$set\":{\"a\":\"someString\"}}}," + "\"(s:pe%cial)\":{\"patch\":{\"$set\":{\"a\":\"someOtherString\"}}}}}", simpleKeys, patches }, { AllProtocolVersions.RESTLI_PROTOCOL_1_0_0.getProtocolVersion(), new Key("compoundKey", CompoundKey.class, null), new Key[] { new Key("string1", String.class), new Key("string2", String.class) }, "{\"entities\":{\"string1=apples&string2=oranges\":{\"patch\":{\"$set\":{\"a\":\"someString\"}}}," + "\"string1=simple&string2=(s:pe%25cial)\":{\"patch\":{\"$set\":{\"a\":\"someOtherString\"}}}}}", compoundKeys, patches }, { AllProtocolVersions.RESTLI_PROTOCOL_2_0_0.getProtocolVersion(), new Key("compoundKey", CompoundKey.class, null), new Key[] { new Key("string1", String.class), new Key("string2", String.class) }, "{\"entities\":{\"(string1:apples,string2:oranges)\":{\"patch\":{\"$set\":{\"a\":\"someString\"}}}," + "\"(string1:simple,string2:%28s%3Ape%25cial%29)\":{\"patch\":{\"$set\":{\"a\":\"someOtherString\"}}}}}", compoundKeys, patches }, { AllProtocolVersions.RESTLI_PROTOCOL_1_0_0.getProtocolVersion(), new Key("complexKey", ComplexResourceKey.class, null), null, "{\"entities\":{\"a=simple&b=111\":{\"patch\":{\"$set\":{\"a\":\"someString\"}}}," + "\"a=(s:pe%25cial)&b=222\":{\"patch\":{\"$set\":{\"a\":\"someOtherString\"}}}}}", complexResourceKeys, patches }, { AllProtocolVersions.RESTLI_PROTOCOL_2_0_0.getProtocolVersion(), new Key("complexKey", ComplexResourceKey.class, null), null, "{\"entities\":{\"($params:(),a:%28s%3Ape%25cial%29,b:222)\":{\"patch\":{\"$set\":{\"a\":\"someOtherString\"}}}," + "\"($params:(),a:simple,b:111)\":{\"patch\":{\"$set\":{\"a\":\"someString\"}}}}}", complexResourceKeys, patches } };
}
Aggregations