Search in sources :

Example 1 with Key

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;
}
Also used : CompoundKey(com.linkedin.restli.common.CompoundKey) RestLiInternalException(com.linkedin.restli.internal.server.RestLiInternalException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) ComplexResourceKey(com.linkedin.restli.common.ComplexResourceKey) AlternativeKey(com.linkedin.restli.server.AlternativeKey) Key(com.linkedin.restli.server.Key) CompoundKey(com.linkedin.restli.common.CompoundKey)

Example 2 with Key

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());
}
Also used : ResourceConfigException(com.linkedin.restli.server.ResourceConfigException) AlternativeKey(com.linkedin.restli.server.annotations.AlternativeKey) ComplexResourceKey(com.linkedin.restli.common.ComplexResourceKey) Key(com.linkedin.restli.server.Key) AssocKey(com.linkedin.restli.server.annotations.AssocKey)

Example 3 with Key

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;
}
Also used : RestLiAssociation(com.linkedin.restli.server.annotations.RestLiAssociation) ComplexKeyResourceTask(com.linkedin.restli.server.resources.ComplexKeyResourceTask) ComplexKeyResource(com.linkedin.restli.server.resources.ComplexKeyResource) ParameterizedType(java.lang.reflect.ParameterizedType) RecordTemplate(com.linkedin.data.template.RecordTemplate) ComplexKeyResourcePromise(com.linkedin.restli.server.resources.ComplexKeyResourcePromise) HashSet(java.util.HashSet) ComplexKeyResourceAsync(com.linkedin.restli.server.resources.ComplexKeyResourceAsync) TyperefInfo(com.linkedin.data.template.TyperefInfo) HasTyperefInfo(com.linkedin.data.template.HasTyperefInfo) ParameterizedType(java.lang.reflect.ParameterizedType) InterfaceType(com.linkedin.restli.internal.server.model.ResourceMethodDescriptor.InterfaceType) Type(java.lang.reflect.Type) ResourceConfigException(com.linkedin.restli.server.ResourceConfigException) AlternativeKey(com.linkedin.restli.server.annotations.AlternativeKey) ComplexResourceKey(com.linkedin.restli.common.ComplexResourceKey) Key(com.linkedin.restli.server.Key) AssocKey(com.linkedin.restli.server.annotations.AssocKey)

Example 4 with Key

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());
    }
}
Also used : RoutingException(com.linkedin.restli.server.RoutingException) ResourceContext(com.linkedin.restli.server.ResourceContext) ServerResourceContext(com.linkedin.restli.internal.server.ServerResourceContext) InvalidAlternativeKeyException(com.linkedin.data.template.InvalidAlternativeKeyException) AlternativeKeyCoercerException(com.linkedin.restli.internal.server.util.AlternativeKeyCoercerException) ComplexResourceKey(com.linkedin.restli.common.ComplexResourceKey) ResourceModel(com.linkedin.restli.internal.server.model.ResourceModel) ComplexResourceKey(com.linkedin.restli.common.ComplexResourceKey) Key(com.linkedin.restli.server.Key) CompoundKey(com.linkedin.restli.common.CompoundKey)

Example 5 with Key

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 } };
}
Also used : EmptyRecord(com.linkedin.restli.common.EmptyRecord) MyComplexKey(com.linkedin.restli.common.test.MyComplexKey) HashMap(java.util.HashMap) CompoundKey(com.linkedin.restli.common.CompoundKey) PatchRequest(com.linkedin.restli.common.PatchRequest) BatchPatchRequest(com.linkedin.restli.server.BatchPatchRequest) DataMap(com.linkedin.data.DataMap) StringDataSchema(com.linkedin.data.schema.StringDataSchema) ComplexResourceKey(com.linkedin.restli.common.ComplexResourceKey) MyComplexKey(com.linkedin.restli.common.test.MyComplexKey) ComplexResourceKey(com.linkedin.restli.common.ComplexResourceKey) Key(com.linkedin.restli.server.Key) CompoundKey(com.linkedin.restli.common.CompoundKey) DataProvider(org.testng.annotations.DataProvider)

Aggregations

ComplexResourceKey (com.linkedin.restli.common.ComplexResourceKey)28 Key (com.linkedin.restli.server.Key)28 CompoundKey (com.linkedin.restli.common.CompoundKey)22 MyComplexKey (com.linkedin.restli.common.test.MyComplexKey)10 AlternativeKey (com.linkedin.restli.server.AlternativeKey)8 IntegerDataSchema (com.linkedin.data.schema.IntegerDataSchema)7 ResourceModel (com.linkedin.restli.internal.server.model.ResourceModel)7 DataProvider (org.testng.annotations.DataProvider)7 EmptyRecord (com.linkedin.restli.common.EmptyRecord)6 ServerResourceContext (com.linkedin.restli.internal.server.ServerResourceContext)6 HashSet (java.util.HashSet)6 Test (org.testng.annotations.Test)5 RecordTemplate (com.linkedin.data.template.RecordTemplate)4 RestRequest (com.linkedin.r2.message.rest.RestRequest)4 RoutingResult (com.linkedin.restli.internal.server.RoutingResult)4 Parameter (com.linkedin.restli.internal.server.model.Parameter)4 ResourceMethodDescriptor (com.linkedin.restli.internal.server.model.ResourceMethodDescriptor)4 ResourceConfigException (com.linkedin.restli.server.ResourceConfigException)4 RestLiRequestData (com.linkedin.restli.server.RestLiRequestData)4 RoutingException (com.linkedin.restli.server.RoutingException)4