Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

requests.post vs _tm1_rest.post #351

Closed
dltswatts opened this issue Sep 1, 2020 · 6 comments
Closed

requests.post vs _tm1_rest.post #351

dltswatts opened this issue Sep 1, 2020 · 6 comments
Labels

Comments

@dltswatts
Copy link

I have been using the Python requests function to upload one or more Tis to a server. I have a json file that contains my TIs. I am trying to utilize the TM1py REST functionality to do the same thing.

I bring in my json file like this:
cwd = os.getcwd()
with open(cwd+'/BP_Setup.json') as f:
pdata = json.load(f)

I then load in to my server like this:
headerload = {
'Authorization': "Basic YWRtaW46",
'Content-Type': "application/json",
'cache-control': "no-cache"
}
url = protocol + "://" + ip + ":" + str(tm1server) + "/api/v1/Processes"
requests.post(url, headers=headerload, json=pdata)

This works just fine. I then try to do the same with TM1py like this:

headerload = {
'Content-Type': "application/json",
'cache-control': "no-cache"
}

url = "/api/v1/Processes"
tm1._tm1_rest.POST(url, header=headerload, json=pdata)

(tm1 is the TM1Service logon)

And I get the following error message:
TM1pyRestException: Text: {"error":{"code":"278","message":"Unexpected value type encountered in the payload while processing "Process". Expecting either type "Object" or "Array" but was "Null"."}} Status Code: 400 Reason: Bad Request Headers: {'Content-Length': '158', 'Connection': 'keep-alive', 'Content-Encoding': 'gzip', 'Cache-Control': 'no-cache', 'Content-Type': 'application/json; charset=utf-8', 'OData-Version': '4.0'}

First and foremost, are my two statements equivalent?

Does requests.post(url, headers=headerload, json=pdata) = tm1._tm1_rest.POST(url, header=headerload, json=pdata)

The documentation makes me think it should be but if I'm going down the wrong road....

Thanks,
@dltswatts

@MariusWirtz
Copy link
Collaborator

Hi @dltswatts,

I don't think you need to work on the HTTP level.
In TM1py you can build an instance of the Process class from a json.
Then you can send the created process to a TM1 instance through the ProcessService.

here is a sample that illustrates the idea end to end:

from TM1py import Process
from TM1py.Services import TM1Service

ADDRESS = "localhost"
PORT = 12354
USER = "admin"
PWD = "apple"
SSL = True

with TM1Service(address=ADDRESS, port=PORT, user=USER, password=PWD, ssl=SSL) as tm1:
    process = tm1.processes.get("Bedrock.Dim.Clone")

    with open(r"C:\projects\out.json", "w", encoding="utf-8") as file:
        file.write(process.body)

    with open(r"C:\projects\out.json", "r", encoding="utf-8") as file:
        p = Process.from_json(file.read())
        p.name = "NewName"
        tm1.processes.create(p)

@dltswatts
Copy link
Author

Thank you Marius, I appreciate your time on this. Also, thank you for the example. I'm curious though what advantages your approach provides over the HTTP approach? Your example provides a nice example of writing out a process and then bringing it in as creating a new one. But if I have a file of more than one process, you need to then add language to iterate through them. So in terms of brevity it would seem the HTTP approach is more efficient but if there are other advantages with the TM1py approach you provided, I'd like to understand what they are. Thanks again for your insights.

Dean

@MariusWirtz
Copy link
Collaborator

Hi @dltswatts,

Yes. If you have a JSON file with multiple process definitions in it, or multiple JSON files you will need to loop through the process definitions. That shouldn't be complex though.

In a way, you could do everything with bare HTTP and not to use TM1py. In my opinion, it makes sense to use TM1py wherever possible. For the following reasons:

  • The code that uses TM1py building blocks is easier to read and understand than the bare HTTP requests

  • The code is well maintained and covers edge cases. If something changes in how the JSON needs to be sent to the TM1 server in the future, the create function will take care. Example: in TM1 10.2.2 the process JSON needs to be adjusted before it can be sent.

    if int(self.version[0:2]) < 11:

  • All TM1py functions have built-in features. The create method, like all other functions in TM1py, accepts a timeout keyword argument. So you can easily set a timeout for your create operation.

regarding your other question, is the pData object of type str or dict? the POST function expects it to be a str

@dltswatts
Copy link
Author

Thank you again Marius, your explanation is helpful. It has brought up the true difference of building for expediency vs building for sustainability. Thanks.

In my original question, the pdata is a dictionary (comes through as class 'list'). Are you saying the TM1py _tm1_rest function for POST expects a string? Is there an argument that can be provided to address a dictionary? Just wondering at this point.

Thanks again.

Dean

@MariusWirtz
Copy link
Collaborator

Hi @dltswatts,

thanks!

I think there is a small chance, that you can convert the list of dictionaries to a string and it just works 😅
tm1._tm1_rest.POST(url, header=headerload, json=str(pdata))

@dltswatts
Copy link
Author

Nah, tried this just for giggles and it threw the same error. Plus, it would really be leaving things up to chance wouldn't it? ;}

I'll go ahead and close out this question. This has been helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants