How can I insert Parameters into a BAQ when using the Epicor Rest API (Python3)?

Hello everyone,

I have searched far and wide and tried a great deal of things, but was not able to get this working…

I am trying to build my own Rest library that pushes/pulls data from the Epicor Rest API. I am successfully running BO methods as well as BAQs, but I would like to make sure I can also run BAQs with params, this is the part I’m struggling with at the moment.

Here’s my python function (which works with BAQs that don’t have params):

#import http.client
#conn = http.client.HTTPSConnection(self.url)

def execute_baq(self, baq_name, params):
		headers = {"Authorization": "Bearer " + str(self.token)} #this part works
		self.conn.request("GET", self.epicor_env + self.api_v + "/BaqSvc/" + baq_name, params, headers)
		res = self.conn.getresponse()
		data = res.read()
		return data.decode("utf-8")

When I run the function, I’m pushing something along the lines of:

connection = EpicorConnection("username", "password", "env")
# I have also tried 
# params = {'first' : 'first', 'second': 'second'} ..., but there's a type error when using this as params.
params = "@full_name eq 'john doe' and @emp_id eq '1144' and @monday eq '7:00 - 17:30'"
print(connection.execute_baq("TestBAQ6", params))

I have applied these inside the BAQ in Epicor and it they work.


This code works with BAQs that have no params. Here I am getting no data returned.

{
"odata.metadata":"url/api/v1/BaqSvc/TestBAQ6/$metadata#Epicor.DynamicQuery.QueryResults","value":[
  ]

}

I have tried many variations of params. Without the @, with $, replaced “eq” with “=”,“==”, etc…

Does anyone have an idea on how the params need to be structured or passed into this http.client method?

Thank you!

You add them as a part of query string, after ?:
/BaqSvc/baqid?param1=value1&param2=value2
You can see this syntax in REST Help

2 Likes

I appreciate your response!

Is that the only way this can be done? I would have thought the method would take care of that.

The issue I have with adding the params manually to the url is that I will have to account for all the special character replacements.
For example, when the a param is passed “value with spaces”, the url is something like
/BaqSvc/baqid?param1=value%20with%20spaces&param2=value... and I have to account for all non-alphabet characters.
Another example, when I want param1 to be “M&Ms”, I will have to replace ‘&’ with something else. Unless there is some encoder for that…

Yes, but also no.

You can also use the dynamic query service directly, and pass your parameters in the Execution Parameters.

It worked!
I was able to do it with the following:

import urllib.parse

def execute_baq(self, baq_name):
	f= {'full_name': 'john doe', 'emp_id': '1144', 'monday': '7:00 - 17:30'}
	parameters = urllib.parse.urlencode(f)
	headers = {"Authorization": "Bearer " + str(self.token)}
	self.conn.request("GET", self.epicor_env + self.api_v + "/BaqSvc/" + baq_name + "?" + parameters, None, headers)
	res = self.conn.getresponse()
	data = res.read()
	return data.decode("utf-8")

Now to figure out how to pass >,<,>=,<= operators into f.
However, that should be an easier task.

Thank you for making me aware it needs to be passed into the url, not into the params/body field.

1 Like

Thank you for your response.

I was able to do it by passing it via the url.
I guess it won’t work by passing it through the body. I figured the params would have added to the url, but that’s not how the function works.

Thanks

If you don’t like passing them in the url, and you don’t want to use the dynamic query object via rest, you could make a function that handles it.

Do the work in C#, and make the function signature match something that suits you.

1 Like

It’s a good thought. I haven’t dealt with Epicor functions all too much, but eventually will need to anyway.
Do you think there is more value in creating my own function for this rather than passing the params through the url?
In terms of security, I don’t think there is anything to worry about when passing them through the url as I will have to encapsulate most of these methods, especially the ones with hard-coded credentials.

I really can’t answer that one without knowing a lot more information.

If the url situation isn’t working for you, I would first try learning how to call the dynamic
query service via rest. The knowledge will be helpful regardless.

I think @hmwillett has a post that covers it somewhere.

If you can’t find anything relevant, I could work something up when I have some time.

1 Like

The Big Bang Theory Reaction GIF

Yeah, no. You are obviously a good programmer. Strive to be better! Remove hard-coded credentials and learn to work with the token service. And like Kevin said, learn to use Functions. This will reduce the number of calls across the wire and help you decouple and encapsulate your logic.

YMMV, I’m just a cranky old man.

I am using the token service, as you can see in my above code.
However, we will have some screens being set up around the place which will GET data via BAQs. We wouldn’t mind running Epicor on all of them if that approach wouldn’t consume licenses. We don’t want to buy a license for every screen. Therefore, I figured I’d write a REST library that runs all these BAQs, refreshes them once in a while, and displays data.
I wouldn’t want the workers to have to enter credentials every morning, the data should just be displayed.

Is there still a different approach to take for this? Maybe an external script entering the credentials? I don’t plan on giving these credentials much power beyond running a few BAQs, but I haven’t tried setting up scopes for REST, not sure if it’s possible.

Maybe it’s better to use the API keys with limited scopes, I have much to learn on this approach however.

Don’t get me wrong, you’re doing very well here, and thinking the right way!

Consider using a Secrets Manager to manage the username and password and not keep them hardcoded. All the cloud providers have vaults and you can also use 3rd party vaults for on-prem solutions (like HashiCorp). PowerShell has a module that can pull creds from various vaults.

For me, my goal is to use a managed identity in Azure so no credentials are ever passed around. But for on-prem users, this is a harder sell to older (but younger than me :rofl: ) admins. Service Principals are a hard problem to solve, to be sure. Don’t take my comment as criticism.

1 Like

Great idea. Have you considered writing an API that uses one license with limited scope? That way, multiple clients can consume the Kinetic data in a much friendlier way where you can better shape the data for your client. I’m working on this myself for several systems, not just Kinetic. I’m hoping to get some caching in there too to reduce the load on the source systems.

2 Likes

I am not exactly sure how Epicor licenses are consumed. I’m open to hearing more about your approach!

I figured when using the Rest API, there would be one license consumed at max since I would be using the same credentials for every station. I was thinking something along the lines of having Raspberry Pi stations set up around the building and they would run this script every minute and then push some custom table to the screen. This is why I coded this in Python.
Does Epicor use a license per set of credentials used in the Rest API?
I’m open to all kinds of approaches to this. My goal is to have something reusable, reliable, low-maintenance, and that doesn’t require more Epicor licenses.

Alternatively, I could have some webapp that displays data based on a BAQ name. I could connect a chromecast to each screen and cast the tab to the chromecast. These could then be addressed from one computer. However, I’ve experienced these casts breaking up many times in a day, so that may not be ideal either.

To be clear, my goal isn’t licensing avoidance. Most cloud providers have a mix of per seat or consumption licensing. Epicor only has concurrent per seat licensing. This means if I buy 50 seats of that license type (Office, MES, Handheld, etc.) I can have a maximum of 50 sessions at one time. I can have 500 users but only 50 sessions.

There are also WebService licenses. IIRC, these are rate-limited in that you can call it every so often but when you exceed the number of licenses, it will add a progressively longer delay between each call. The first few WebService licenses are about the same as a full license but get significantly cheaper quickly as you buy more. Full licenses get cheaper with higher quantities, but the discount isn’t as big.

This is one of the cases where a consumption license makes sense. Pay for what you use. A consumption license is also be better for the Express/MT users. Now you can have just one code base and have smaller companies limited by transaction bundles. As they get bigger, they pay more. If times are bad, they pay less. If they get big enough, then buying seats makes more sense.

It’s best to stick to the business problem you’re trying to solve. If one gets too motivated by license reduction, you will have a poor user experience.

2 Likes

You’re right, it’s important to not cheap out if it reduces the user experience.
In our case, we have several people using up licenses just to read Dashboard data. This requires them to have a dedicated workstation (keyboard + mouse), an Epicor license, and takes time out of their day to log in/out to free up licenses.
Ideally, we would have a screen displaying the dashboard they need to see and them not having to log in and out.

In this case, I believe the Rest API solution makes a lot of sense. As we have multiple people working in these positions, there are several benefits for doing this:

  • Reduce # of consumed licenses (pay less)
  • Eliminate the need for user input in general (efficiency)…

I really appreciate the information you provided. I am definitely trying to do this the right way and not have lazy and compromising code. I will try to implement a Secrets Manager, despite our url not being accessible from outside the network. It is best practice and we can future proof it in case we go public.

Thank you!

Not only that, but I have seen people easily eat up any savings “rolling their own” (often inferior) solutions vs just purchasing a license to a service. :person_shrugging:

1 Like

Makes sense. “Don’t reinvent the wheel” is very applicable in these situations