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

Support for TM1SessionID or cookiejar in RESTService #31

Closed
ajmyers opened this issue Oct 29, 2017 · 6 comments
Closed

Support for TM1SessionID or cookiejar in RESTService #31

ajmyers opened this issue Oct 29, 2017 · 6 comments

Comments

@ajmyers
Copy link
Contributor

ajmyers commented Oct 29, 2017

It would be useful if TM1py supported initializing a RESTService with an additional TM1SessionId or session cookiejar parameter to bypass the authentication process on the TM1 server. An LDAP authentication request can take 10-20s for us (or can be delayed if there are waits on the TM1 server), so caching the session is a necessity for us.

@MariusWirtz
Copy link
Collaborator

Yes. That would be a nice feature.

I think the most intuitive way to do it is through an optional string argument (session_id) in the TM1Service / RESTService Constructor.
Shouldn't be more than a few extra lines of code in the RESTService constructor.

How come you have a sessionid at hand before initializing TM1py?

@ajmyers
Copy link
Contributor Author

ajmyers commented Oct 30, 2017

Currently, i'm grabbing the session cookie from RESTService._s.

For the time being, I'm doing an ugly patch of RESTService._get_cookies to add in basic persistent session support via exporting/importing to/from a temp file, however a supported mechanism to export the session and reinitialize TM1Service or RESTService with that session would be ideal.

def _get_cookies(self):
   """ perform a simple GET request (Ask for the TM1 Version) to start a session
   """
    url = '{}/api/v1/Configuration/ProductVersion/$value'.format(self._base_url)

    auth_string = self._headers['Authorization'].split(" ")[1]
    auth = b64decode(auth_string).decode('ascii').split(":")
    if 'CAMNamespace' in self._headers['Authorization']:
        session_name = '{}_{}_{}'.format(self._base_url, auth[0], auth[2])
    else:
        session_name = '{}_{}'.format(self._base_url, auth[0])

    cookie_path = tempfile.gettempdir()
    cookie_file = os.path.join(cookie_path, urlsafe_b64encode(session_name.encode()).decode() + ".tm1_session")

    try:
        with open(cookie_file, 'rb') as f:
            cookies = pickle.load(f)
            requests.utils.add_dict_to_cookiejar(self._s.cookies, cookies)
            h = self._headers.copy()
            h.pop('Authorization')
            response = self._s.get(url=url, headers=h, data='', verify=self._verify)
            self.verify_response(response)
            self._version = response.text
            return
    except TM1pyException as e:
        print('Bad cookie, generating new session')
    except Exception as e:
        print('No cookie or some other error, generating new session')
        pass

    response = self._s.get(url=url, headers=self._headers, data='', verify=self._verify)
    self.verify_response(response)
    self._version = response.text

    with open(cookie_file, 'wb') as f:
        pickle.dump(requests.utils.dict_from_cookiejar(self._s.cookies), f)

@MariusWirtz
Copy link
Collaborator

Thanks for the sample code! I like the use of pickle.

I am thinking about serializing the full TM1Service instance into a pickle file.
I would like to do it by introducing two new functions to the TM1Service:

  • one that serializes the TM1Service instance (including the session cookie) into a pickle file.
  • and another one that de-serializes the TM1Service from a pickle file. I think we could build the second one as a classmethod (independent constructor) of the TM1Service.
    So codewise it could look like:
tm1 = TM1Service.from_file(path_to_tm1service_pickle)
print(tm1.cubes.get("plan_BudgetPlan"))

I think it would serve the purpose and it should be easy and intuitive.
What do you think?

@ajmyers
Copy link
Contributor Author

ajmyers commented Oct 30, 2017

Yes, that sounds perfect!

@MariusWirtz
Copy link
Collaborator

Hi,

I pushed the new release to Github and PyPI.
Now we can dump the tm1service instance to a file and reuse the session later.


import time
from TM1py.Services import TM1Service

# Connect to TM1
tm1 = TM1Service(address='localhost', port=12354, user='admin', password='apple', ssl=True)
print(tm1.server.get_server_name())
# Save TM1Service instance to file
tm1.save_to_file('tm1_connection')

time.sleep(7)

# Restore TM1Service instance from file
tm1 = TM1Service.restore_from_file('tm1_connection')
print(tm1.server.get_server_name())

# Logout
tm1.logout()

@ajmyers
Copy link
Contributor Author

ajmyers commented Nov 9, 2017

@MariusWirtz This is awesome, thank you!

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

No branches or pull requests

2 participants