odataolingo

Can we create navigation properties upto 3 segments in olingo odata


I have a requirement where we need to send 3 entity sets in the URI

e.g., {serviceRoot}/Entity1('ID')/Entity2('ID')/Entity3

I have gone through the olingo git repo and could only find for 2 segments(for 3 segments a Not Implemented Exception is thrown.

I want to understand if the URL({serviceRoot}/Entity1('ID')/Entity2('ID')/Entity3) is valid?

Olingo Version used is 4.5


Solution

  • From a OData Sepcifiion point of view its perfectly fine to have 3 level or even n level 'navigation properties', the first segment of the URL is the Entity or Entity Set, and then you will have to create Navigation Properties, which are bound to(read return) and entity set or entity.

    The reason you are getting an Not Implemented Exception exception is if you read through the code of readEntityCollection you will find the below segment

    if (segmentCount == 2){ //navigation: e.g. DemoService.svc/Categories(3)/Products
    

    Here its checking if the segment is the 3rd part and treats it as navigation property but if you go a level deeper it just has an else condition it throws the exception.

    To achieve your purpose you will have to implement this logic in your code to go one(or even more) levels deeper

    Below is the complete code for reference

    public void readEntityCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException, SerializerException {

    EdmEntitySet responseEdmEntitySet = null; // for building ContextURL
    EntityCollection responseEntityCollection = null; // for the response body
    
    // 1st retrieve the requested EntitySet from the uriInfo
    List<UriResource> resourceParts = uriInfo.getUriResourceParts();
    int segmentCount = resourceParts.size();
    
    UriResource uriResource = resourceParts.get(0); // the first segment is the EntitySet
    if (! (uriResource instanceof UriResourceEntitySet)) {
        throw new ODataApplicationException("Only EntitySet is supported", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(),Locale.ROOT);
    }
    
    UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) uriResource;
    EdmEntitySet startEdmEntitySet = uriResourceEntitySet.getEntitySet();
    
    if(segmentCount == 1){ // this is the case for: DemoService/DemoService.svc/Categories
        responseEdmEntitySet = startEdmEntitySet; // first (and only) entitySet
    
        // 2nd: fetch the data from backend for this requested EntitySetName
        responseEntityCollection = storage.readEntitySetData(startEdmEntitySet);
    }else if (segmentCount == 2){ //navigation: e.g. DemoService.svc/Categories(3)/Products
        UriResource lastSegment = resourceParts.get(1); // don't support more complex URIs
        if(lastSegment instanceof UriResourceNavigation){
            UriResourceNavigation uriResourceNavigation = (UriResourceNavigation)lastSegment;
            EdmNavigationProperty edmNavigationProperty = uriResourceNavigation.getProperty();
            EdmEntityType targetEntityType = edmNavigationProperty.getType();
            responseEdmEntitySet = Util.getNavigationTargetEntitySet(startEdmEntitySet, edmNavigationProperty);
    
            // 2nd: fetch the data from backend
            // first fetch the entity where the first segment of the URI points to
            // e.g. Categories(3)/Products first find the single entity: Category(3)
            List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
            Entity sourceEntity = storage.readEntityData(startEdmEntitySet, keyPredicates);
            // error handling for e.g.  DemoService.svc/Categories(99)/Products
            if(sourceEntity == null) {
                throw new ODataApplicationException("Entity not found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
            }
            // then fetch the entity collection where the entity navigates to
            responseEntityCollection = storage.getRelatedEntityCollection(sourceEntity, targetEntityType);
        }
    }else{ // this would be the case for e.g. Products(1)/Category/Products
        throw new ODataApplicationException("Not supported", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(),Locale.ROOT);
    }
        // 3rd: create and configure a serializer
        ContextURL contextUrl = ContextURL.with().entitySet(responseEdmEntitySet).build();
        final String id = request.getRawBaseUri() + "/" + responseEdmEntitySet.getName();
        EntityCollectionSerializerOptions opts = EntityCollectionSerializerOptions.with().contextURL(contextUrl).id(id).build();
        EdmEntityType edmEntityType = responseEdmEntitySet.getEntityType();
    
        ODataSerializer serializer = odata.createSerializer(responseFormat);
        SerializerResult serializerResult = serializer.entityCollection(this.srvMetadata, edmEntityType, responseEntityCollection, opts);
    
        // 4th: configure the response object: set the body, headers and status code
        response.setContent(serializerResult.getContent());
        response.setStatusCode(HttpStatusCode.OK.getStatusCode());
        response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());   
    }