Batch download of contact address records via REST? - rest

Using Acumatica ERP 2018R (V18.112.0019) which is the latest version as of this question.
I've been trying to find a way to download contact address records in batch -- not one at a time, and it seems that everything I try, I get an error from the optimizer about a BQL delegate.
I've tried using the Default/17.200.001 endpoint to go after the Contact entity and use the $expand=Address parameter.
Often suggested as a workaround is to create a Generic Inquiry, so I've also tried extending the endpoint and get the results from a GI, but no matter how basic I make that GI, I get the same BQL delegate error. For the DAC, I've tried using PX.Objects.CR.Address, PX.Objects.CR.ContactExtAddress, PX.Objects.AR.ARAddress, and went so far as to return just the AddressID field. No other tables, relations, parameters, conditions, etc. Just a very basic GI to try and get to an address. Even if the GI worked, there's issues with being able to page the results (I get a method not allowed error when trying to do a Put to a GI, but that could just be me doing it wrong).
Looking that differences between the old endpoint Contract versions, it looks like V1 was more forgiving with sub-optimal queries than V3? Maybe the REST API needs to have a URL parameter or a way to specify to allow a non-optimized query to run? Or if it has that feature, I can't seem to locate it.
Any insight or examples that anyone could add would be greatly appreciated. I can't imagine having to download contacts and/or contact address records one at a time - that would be a ridiculous number of round trips/requests and would be much slower and more of a resource hog than a single non-optimized query.

I think I was able to solve my own problem by expanding the Contacts in the following manner:
Customer?$expand=ShippingContact/Address,MainContact/Address,Contacts,Contacts/Contact/Address

You should be able to do PUT to a Generic Inquiry entity, and in that way you should not get a delegate-related optimization error.

Related

REST API Get single latest resource

I'm designing a REST api and interested if anyone can help with best practice in the following scenario.
I have...
GET Customers/{customerId}/Orders - to get all customer orders
GET Customers/{customerId}/Orders/{orderId} - to get a particular order
I need to provide the ability to get a customers most recent order. What is best practice in this scenario? Simply get all and sort by date or provide a specific method?
I need to provide the ability to get a customers most recent order.
Of course you could provide query parameters to filter, sort and slice the orders collection, but why not making it simpler and give the latest order if the client needs it?
You could use something like (returning a representation of a single order):
GET /customers/{customerId}/orders/latest
The above URL will map an order that will change over the time and it's perfectly fine.
Say there is also a case where you need last 5 orders. How would your route(s) look like?
The above approach focus on the ability to get a customers most recent order requirement. If returning the last 5 orders requirement eventually comes up after some time, I would probably introduce another mapping such as /recent that returns a representation of a collection with the recent orders and accepts a query parameter that indicates the amount of orders to be returned (5 would be the default value if the parameter is omitted).
The /latest mapping would still be valid and would return a representation of the very latest order only.
Providing query parameters to filter, sort and slice the orders collection is still a valid approach.
The key is: If you know the client who will consume the API, target it to their needs. Otherwise, make it more generic. And when modifying the API, be careful with breaking changes and versioning the API is also welcome.
I think there is no need for another route.
Pass something like &order=-created_at&limit=1 in your get request
Or &order=created_at&orderby=DESC&limit=1 (note I'm not sure about naming your params so maybe you could use &count=1 instead of &limit=1, ditto order params)
I think it also depends whether you are using pagination or not on that route, so perhaps additional params are required
Customers/{customerId}/Orders?order=-created_at&limit=1
The Github API for the similar use case is using latest, to fetch the single resource which is latest.
https://docs.github.com/en/rest/reference/repos#get-the-latest-release
So to fetch a single resource which is latest you can use.
GET /customers/{customerId}/orders/latest
However would like to know what community think about this.
IMO the resource/latest gives an impression that the response will be a list of resource sorted by latest to oldest.

REST url proper format

my REST API format:
http://example.com/api/v1.0/products - get all products
http://example.com/api/v1.0/products/3 - get product with id=3
Also, the products can be orginized into a product groups.
What is a proper way to get all product groups according to REST best practices:
http://example.com/api/v1.0/products/groups
or
http://example.com/api/v1.0/productgroups
...
another option ?
I can't agree with Rishabh Soni because http://example.com/api/v1.0/products/groups may lead to ambiguity.
I would put my money on http://example.com/api/v1.0/productgroups or even better http://example.com/api/v1.0/product_groups (better readability).
I've had similar discussion here: Updating RESTful resources against aggregate roots only
Question: About the thing of /products/features or /product-features,
is there any consensus on this? Do you know any good source to ensure
that it's not just a matter of taste?
Answer: I think this is misleading. I would expect to get all features
in all products rather than get all possible features. But, to be
honest, it’s hard to find any source talking directly about this
problem, but there is a bunch of articles where people don’t try to
create nested resources like /products/features, but do this
separately.
So, we can't be sure http://example.com/api/v1.0/products/groups will return all possible groups or just all groups that are connected with all existing products (what about a group that has not been connected with the product yet?).
To avoid this ambiguity, you can add some annotation in documentation. But you can just prepare http://example.com/api/v1.0/product_groups and all is clear.
If you are developing Rest API for your clients than you should not rely on id's. Instead build a meaningful abbreviation and map them to actual id on server side.
If that is not possible, instead of using
http://example.com/api/v1.0/products/3 you can use http://example.com/api/v1.0/products?product_id=3 and then you can provide "product_id" description in the documentation. basically telling the client ways to use product_id.
In short a url must be meaningful and follow a pattern.The variable part must be send by in the url query(part after ? or POST payload)
With this, method to querying the server is also important. If client is trying to get something to the server he should use "GET" http request, similar POST http request if it is uploading new info and "PUT" request if it is updating or creating a new resource.
So by this analogy http://example.com/api/v1.0/products/groups is more appropriate as it is following a pattern(groups in product) while productgroups is more like a keyword with no pattern.
A directory like pattern is more easier to understand. Like in file systems(C:\Program Files\WinRAR), every part gets us to more generalized target.
You can also customize this for specific group- http://example.com/api/v1.0/products/groups?id=3

Cannot use folder-name field to filter resources in HP QC REST API

I don't expect to get an answer on this but if anyone can help it would be greatly appreciated.
We're using HP Application Lifecycle Management 12.2 (ALM or QC/QualityCenter as it's called). I'm trying to retrieve test resources from the project using the REST api but I suspect I've come across a defect in the API.
I'm trying to filter the response by using a query, and any other query works fine but when I try to use the 'folder-name' field it fails with the following message:
Failed to set params in the parametrized query
It doesn't matter what format I try to escape the query with, I've tried the following:
/resources/?query={folder-name[%27folder%27]}
/resources/?query={folder-name['folder']}
/resources/?query=%7bfolder-name%5b%27folder%27%5d%7d
I've also reproduced this on an 11.52 QC instance so it's not just our environment. I've double checked the customization collection using the ?can-filter=true param and folder-name is supposed to be filterable.
I'm currently using the parent-id for filtering instead which works but it requires me to do another request to get the parent-id first which I would like to refrain from.
Any suggestions, patches?
Thanks
As I wrote in the comments, the error is reproducable here, and I agree that it is strange. The folder-name exists as a field in resource, and should be filterable. It seems to be a bug.
But there is another approach that I offer as a substitute solution. The resource entity offer a relation directly to the folder entity, and you can get the exact result you were looking for, by using a cross join with that table:
/resources/?query={resource-folder.name['folder']}
This is probably too late to help you with your work, but it might help others.

Marklogic REST API search for latest document version

We need to restrict a MarkLogic search to the latest version of managed documents, using Marklogic's REST api. We're using MarkLogic 6.
Using straight xquery, you can use dls:documents-query() as an additional-query option (see
Is there any way to restrict marklogic search on specific version of the document).
But the REST api requires XML, not arbitrary xquery. You can turn ordinary cts queries into XML easily enough (execute <some-element>{cts:word-query("hello world")}</some-element> in QConsole).
If I try that with dls:documents-query() I get this:
<cts:properties-query xmlns:cts="http://marklogic.com/cts">
<cts:registered-query>
<cts:id>17524193535823153377</cts:id>
</cts:registered-query>
</cts:properties-query>
Apart from being less than totally transparent... how safe is that number? We'll need to put it in our query options, so it's not something we can regenerate every time we need it. I've looked on two different installations here and the the number's the same, but is it guaranteed to be the same, and will it ever change? On, for example, a MarkLogic upgrade?
Also, assuming the number is safe, will the registered-query always be there? The documentation says that registered queries may be cleared by the system at various times, but it's talking about user-defined registered queries, and I'm not sure how much of that applies to internal queries.
Is this even the right approach? If we can't do this we can always set up collections and restrict the search that way, but we'd rather use dls:documents-query if possible.
The number is a registered query id, and is deterministic. That is, it will be the same every time the query is registered. That behavior has been invariant across a couple of major releases, but is not guaranteed. And as you already know, the server can unregister a query at any time. If that happens, any query using that id will throw an XDMP-UNREGISTERED error. So it's best to regenerate the query when you need it, perhaps by calling dls:documents-query again. It's safest to do this in the same request as the subsequent search.
So I'd suggest extending the REST API with your own version of the search endpoint. Your new endpoint could add dls:documents-query to the input query. That way the registered query would be generated in the same request with the subsequent search. For ML6, http://docs.marklogic.com/6.0/guide/rest-dev/extensions explains how to do this.
The call to dls:documents-query() makes sure the query is actually registered (on the fly if necessary), but that won't work from REST api. You could extend the REST api with a custom extension as suggested by Mike, but you could also use the following:
cts:properties-query(
cts:and-not-query(
cts:element-value-query(
xs:QName("dls:latest"),
"true",
(),
0
),
cts:element-query(
xs:QName("dls:version-id"),
cts:and-query(())
)
)
)
That is the query that is registered by dls:documents-query(). Might not be future proof though, so check at each upgrade. You can find the definition of the function in /Modules/MarkLogic/dls.xqy
HTH!

What is a good strategy for adding additional information in a GET query over REST?

Given that we provide a restful api that serves book entities listening at
/books
And a client can get a book at the usual
GET /books/{id}
Suppose that we want to begin offering discounts on books to only our most vigilant buyers. These buyers would be given a discount code, and that code will reduce the price of the book.
Thus, a generic response may be
GET /books/4
{"id":4, "price":"24.95"}
Where a response to a query with a discount code may be
GET /books/4
{"id":4, "price":"24.95", "yourPrice":"19.95"}
The back-end processing we can get figured out, but what is the best practice for a client submitting a discount code over a restful api?
Certain books will be eligible for discounts while others will not. Discounts will not be broad (20% off everything), but instead will map to a specific price for that particular code (or client/code combo).
We've considered:
kludging the url
GET /codes/{someCode}/books/{id}
Adding the code in a header value
Using a query string
GET /books?code=myCode
anything else?
EDIT: Our goal is not to implement single-use codes. Instead, these discount codes could be used some fixed number of times for some fixed set of books.
I like using query variables. I just looked at the RESTful Web Services book, my main reference in this area, and they say:
Use query variables only to suggest
arguments being plugged into an
algorithm... If two URIs differ only
in their query variables, it implies
they're the different sets of inputs
into the same underlying algorithm.
It seems to me your discount codes are inputs to a discounting algorithm.
Charles
If you're going to be submitting anything that's not idempotent, I would suggest using POST instead of GET. You wouldn't want a client to be able to use their code more than once.
Anything you add in the URL or header values are open to be intercepted, and possibly allowing other users to 'fake' their discount ID. 1 approach would be to introduce a new POST call, that will allow the ID to be encrypted with simple HTTPS. The POSTed data could be as simple as the discountID or customerID.
Added - Sorry Michael, you already said that :)
You can register the code in a table so when the user retrieves that book automatically returns that book with the proper discount, for example:
The user can add some code
POST /register/{code}
This will add an entry to a table {user} - {code} so when the user retrieves by
GET /books/{id}
will use that entry to apply the discount. I'm guessing that you already have some relation between {code}-{book} so wont get into that.