Hello Sven and the olingo community! Needed your help again please :) As it stands, we are using the olingo library to generate odata services. We are sort of using the odata services as a proxy to invoke other services so its not really being used to perform CRUD operation on a backend database per se, but more so to simply relay the request(s) to underlying service(s). For example, we have an Account.svc which simply gets accounts from another disparate rpc service. We have Product.svc which gets products from another webservice. I realize this is not really what odata is meant to be used for but using odata to communicate with a ui layer (sap ui5) is a strict requirement for us. So now, since we have multiple services, we have not defined a singleprocessor as in the car/manufacturer example. Instead, we have defined separate processors which extend from OdataSingleProcessor i.e) AccountProcessor, ProductProcessor and have defined a delegator (also extends OdataSingleprocessor) which through reflection, and our own defined annotations, determines, which processor to use. We have also defined our own annotations to represent, simple/complex types which get mapped at runtime through the edmprovider instead of hard coding entity field simpl/complex types as shown in the sample app. (Unless there is something out of the box we can use??)
We now need to implement FunctionImport. The car/manufacturer example defines it in the edmprovider but doesn't provide a concrete implementation for us to use as an example. We would like to create such functionimports to be able to in turn call services that don't really tie into an EntityType. We also don't use JPA... can you illustrate how we might perform something like: http://myhost/MyService.svc/MyFunctionImport(parameter1....parameterN)? Thank you so much! Hanif -----Original Message----- From: Kobler-Morris, Sven [mailto:[email protected]] Sent: Thursday, December 19, 2013 5:00 AM To: [email protected] Subject: FW: How to implement a filter or query using Olingo API Hello Hanif, using the filter is the better option, because that is what the $filter system query parameter is made for (similar to the "where " clause in SQL statements). A ODATA client can request only entities which pass the filter expression. To avoid a manual parsing of the $filter parameter the odata library does the parsing and creates a filter tree which can be obtained from the UriInfo object supplied as input parameter "uriInfo" on the "readEntity" method. The filter tree can be used directly or with help of the Visitor pattern. Generally you can use the filter tree to create a query string which is understood by your data layer and pass this query string to the data layer (potentially insecure) or you can filter a entity set manually in your layer. I recommend to implement the interface "ExpressionVisitor" to either create the query string for the data layer or to the manual filtering. An potential solution for manual filtering would be to implement and ExpressionVisitor and use this visitor on each entity set/data record you want to filter. E.g. ... public class AccountFilter implements ExpressionVisitor ... use the methods: visitMember -> to read and validate the propertyName (from your sample only accountName is allowed) and return the column name visitLiteral -> to read the accountName value (e.g. "Account1") and return it visitBinary -> if the parameter is "eq" return whether property accountName on the current entityset/datarecord is equal to the literal or not ... As sample a rough flow would be: - read the entityset ( all records) - get the filter tree from uriInfo - for each record - if ( filtertree->accept(yourAccountFilter) != true) { delete record from entityset } - next record - serialize the remaining records The call sequence for $filter=AccountName eq 'Account1') would be 1. visitMember 2. visitLiteral 3. visitBinary (left operant is "AccountName" right opterand is "Account1") Consider also how your service should behave in case of more complex filters Like: $filter='Account1' eq AccountName $filter='Account1' eq AccountName or AccountName eq 'Acount2' However this is not the fastest solution. It is always faster to transform the filtertree into a query which can be used directly in your datalayer, but always consider security checks before using this to avoid script injection etc... regards, Sven ---- On 17.12.13 22:55, "Hanif Rajabali" <[email protected]> wrote: >Hello all, sorry if this is too rudimentary of a question but I have >setup an oData service with the help of the great tutorials posted on the >olingo site. The tutorials cover the Resource path but I'd like to now >perform a system or custom query. Below I've included my basic >$metadata document which has 2 simple entities: Account/Producer >(EntitySets: Accounts/Producers). As it stands I can perform an >operation like /Accounts(1) which is taken care of in my implementation >of readEntity() in my ODataSingleProcesser shown below. Now I'd like >to get say an account by 'AccountName'. So would it be system $filter >like: /Accounts?$filter=AccountName eq 'Account1' or would it be a >custom query like: /Accounts?AccountName='Account1' > > > >And, where would I implement it? Within readEntity? If so, could you >give me some guidance on the implementation? I saw the sample of the >visitor pattern to transform an odata query into a jdbc query but my >odata service is connecting to another disparate service: >this.accountService so I just need to basically parse the query and then >perform the corresponding service call based on the query name/value >pair. Should I leverage what was shown in the visitor example? If so, >how does that 'plugin' to my OdataSingleProcessor implementation? > > > >public class WPODataSingleProcessor extends ODataSingleProcessor { > >..... > >public ODataResponse readEntity(GetEntityUriInfo uriInfo, String >contentType) throws ODataException { > > logger.info("custom query options: " + >uriInfo.getCustomQueryOptions()); > > if (uriInfo.getNavigationSegments().size() >== 0) { > > EdmEntitySet entitySet = >uriInfo.getStartEntitySet(); > > WPEntitySet startEntitySet = >WPEntitySet.fromString(entitySet.getName()); > > > > int id; > > switch(startEntitySet) { > > case ACCOUNTS: > > id = >getKeyValue(uriInfo.getKeyPredicates().get(0)); > > Map<String, >Object> account = this.accountService.getAccount(id); > > > > if (account >!= null) { > > > URI serviceRoot = getContext().getPathInfo().getServiceRoot(); > > > ODataEntityProviderPropertiesBuilder propertiesBuilder = >EntityProviderWriteProperties.serviceRoot(serviceRoot); > > > > > return EntityProvider.writeEntry(contentType, entitySet, account, >propertiesBuilder.build()); > > } > > > > case PRODUCERS: > > id = >getKeyValue(uriInfo.getKeyPredicates().get(0)); > > Map<String, >Object> producer = this.producerService.getProducer(id); > > > > if (producer >!= null) { > > > URI serviceRoot = getContext().getPathInfo().getServiceRoot(); > > > ODataEntityProviderPropertiesBuilder propertiesBuilder = >EntityProviderWriteProperties.serviceRoot(serviceRoot); > > > > > return EntityProvider.writeEntry(contentType, entitySet, producer, >propertiesBuilder.build()); > > } > > > } > > > > throw new >ODataNotFoundException(ODataNotFoundException.ENTITY); > > > > } > > > > throw new ODataNotImplementedException(); > > } > > > >... > > > >} > > > > > >------------------- > >My $metadata: > >------------------- > ><edmx:Edmx xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx" >Version="1.0"> > ><edmx:DataServices >xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" >m:DataServiceVersion="1.0"> > ><Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm" >Namespace="com.sap.workplace.uiservice.odata"> > ><EntityType Name="Account"> > ><Key> > ><PropertyRef Name="Id"/> > ></Key> > ><Property Name="Id" Type="Edm.Int32" Nullable="false"/> > ><Property Name="AccountId" Type="Edm.String" Nullable="false"/> > ><Property Name="AccountType" Type="Edm.String" Nullable="false"/> > ><Property Name="AccountName" Type="Edm.String" Nullable="false"/> > ><Property Name="AccountNumber" Type="Edm.String" Nullable="false"/> > ><Property Name="Address" >Type="com.sap.workplace.uiservice.odata.Address"/> > ></EntityType> > ><EntityType Name="Producer"> > ><Key> > ><PropertyRef Name="Id"/> > ></Key> > ><Property Name="Id" Type="Edm.Int32" Nullable="false"/> > ><Property Name="ProducerName" Type="Edm.String" Nullable="false"/> > ><Property Name="ProducerId" Type="Edm.String" Nullable="false"/> > ><Property Name="FirstName" Type="Edm.String" Nullable="false" >MaxLength="100"/> > ><Property Name="LastName" Type="Edm.String" Nullable="false" >MaxLength="100"/> > ><Property Name="Address" >Type="com.sap.workplace.uiservice.odata.Address"/> > ></EntityType> > ><ComplexType Name="Address"> > ><Property Name="Address1" Type="Edm.String" Nullable="true"/> > ><Property Name="Address2" Type="Edm.String" Nullable="true"/> > ><Property Name="City" Type="Edm.String" Nullable="true"/> > ><Property Name="State" Type="Edm.String" Nullable="true"/> > ><Property Name="Country" Type="Edm.String" Nullable="true"/> > ><Property Name="Zip" Type="Edm.String" Nullable="true"/> > ></ComplexType> > ><EntityContainer Name="SAPWorkplaceEntityContainer" >m:IsDefaultEntityContainer="true"> > ><EntitySet Name="Accounts" >EntityType="com.sap.workplace.uiservice.odata.Account"/> > ><EntitySet Name="Producers" >EntityType="com.sap.workplace.uiservice.odata.Producer"/> > ></EntityContainer> > ></Schema> > ></edmx:DataServices> > ></edmx:Edmx> > > > > >
