Announcement

Collapse
No announcement yet.

API Question: CRUD List - select with where clause params not recognized / Python

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • API Question: CRUD List - select with where clause params not recognized / Python

    I'm trying to query EspoCRM for any leads that might have a particular phone number. I am pretty sure my code is written correctly when I compare it to the PHP API example as a template (https://github.com/espocrm/documenta...arch-params.md)

    No matter what values I pass or format I try for the params or where clause, they do not seem to be recognized. When I run this, I just get a list of all Leads in my CRM. I expect that my where and select clauses should be honored.

    At the end of the day, when I'm done with this function, it will return the id of the lead that contains the phone number. I'd like to do as much of that processing inside the EspoCRM server by passing it the correct search parameters. I hope I don't have to resort to parsing the whole lead list in my code every time I run this function.

    Is there something wrong with what I am doing?

    My code:

    Code:
    #!/usr/bin/python3
    
    import espoapi as crm
    
    endpoint = "https://myendpoint"
    client = crm.EspoAPI(endpoint, "my-api-key")
    
    def check_if_lead_exists_by_number_in_crm(phone: str):[INDENT]where = { 'value': {[/INDENT][INDENT=3]'type': 'contains',
    'attribute': 'phoneNumber',
    'value': phone[/INDENT][INDENT=2]}[/INDENT][INDENT]
    }
    params = {[/INDENT][INDENT=2]'select': 'id',
    'where': where,[/INDENT][INDENT]}
    
    a = "Lead"
    r = client.request('GET', a, params )
    total = r.get("total")
    list = r.get("list")
    print("Total: %s" % total)
    for record in list:[/INDENT][INDENT=2]print("Record: %s" % record)[/INDENT]
    Last edited by mbaysek; 04-04-2020, 02:31 AM.

  • #2
    I believe that the 'type' in the 'where' clause should be 'equals' instead of 'contains'

    Comment


    • #3
      "Where" should be an array.

      How it would look in JSON:
      Code:
      [
          {
                "type": "equals",
                "attribute": "phoneNumber",
                "value": "SOME NUMBER"
           }
      ]

      Comment


      • #4
        yuri I have tried that, as well. Currently, when trying the format that you specified above, I am still getting a dump of all records in my Lead database.

        Here's my current try at setting the params. :

        Code:
        phone = "555-555-1212"   
        params = {
                "select": "id",
                "where": [
                        {
                            "type": "equals",
                            "attribute": "phoneNumber",
                            "value": phone,
                        }
                    ],
            }
        As a check to see if the clause was being parsed at all, I set the type to "foo", and also set the attribute to "foo", and it doesn't even throw any error.

        For debugging, inside the EspoAPI class, I have put some printouts that show the values in params:

        Code:
            def request(self, method, action, params=None):
                if params is None:
                    params = {}
        
                print("--------------------------------")
                print("method %s" % method)
                print("action %s" % action)
                print("params: %s" % params)
                print("self %s" % self)
                print("--------------------------------")[INDENT]
        # snip ...[/INDENT]


        printout:
        Code:
        --------------------------------
        method GET
        action Lead
        params: {'select': 'id', 'where': [{'type': 'equals', 'attribute': 'phoneNumber', 'value': '555-555-1212'}]}
        self <espoapi.EspoAPI object at 0x7f259090b400>
        --------------------------------
        Is there something else I must do in order to get the API to recognize my params?

        Comment


        • #5
          It looks correct. Try to make the same request through EspoCRM UI and check what params are sent. You can print $params variable to compare what is sent: https://github.com/espocrm/espocrm/b...ecord.php#L132


          Maybe the api client class for python has an issue. It was contributed and I did not test it. I'm not a python developer.

          Try to change in the client:

          from: kwargs['data'] = params

          to: kwargs['params'] = params

          Please let me know if if works so I will fix the class.
          Last edited by yuri; 04-18-2020, 07:39 AM.

          Comment


          • #6
            I am not a Python developer either but I recently wrote an API in python and found python's "request" module very easy to use instead of using the contributed wrap class.

            Here's a good post detailing how to do that: https://forum.espocrm.com/forum/deve...as-a-parameter

            Comment


            • #7
              Well, I fell into the rabbit hole of python and was surprised how things that are trivial in javascript and PHP are real hurdles in python.

              I fixed the class. Here is the updated class:


              Code:
              import requests
              import urllib
              
              class EspoAPIError(Exception):
                  """An exception class for the client"""
              
              def http_build_query(data):
                  parents = list()
                  pairs = dict()
              
                  def renderKey(parents):
                      depth, outStr = 0, ''
                      for x in parents:
                          s = "[%s]" if depth > 0 or isinstance(x, int) else "%s"
                          outStr += s % str(x)
                          depth += 1
                      return outStr
              
                  def r_urlencode(data):
                      if isinstance(data, list) or isinstance(data, tuple):
                          for i in range(len(data)):
                              parents.append(i)
                              r_urlencode(data[i])
                              parents.pop()
                      elif isinstance(data, dict):
                          for key, value in data.items():
                              parents.append(key)
                              r_urlencode(value)
                              parents.pop()
                      else:
                          pairs[renderKey(parents)] = str(data)
              
                      return pairs
                  return urllib.parse.urlencode(r_urlencode(data))
              
              class EspoAPI:
              
                  url_path = '/api/v1/'
              
                  def __init__(self, url, username, password):
                      self.url = url
                      self.username = username
                      self.password = password
                      self.status_code = None
              
                  def request(self, method, action, params=None):
                      if params is None:
                          params = {}
              
                      headers = {
                      }
              
                      kwargs = {
                          'url': self.normalize_url(action),
                          'auth': (self.username, self.password),
                          'headers': headers,
                      }
              
                      if method in ['POST', 'PATCH', 'PUT']:
                          kwargs['json'] = params
                      else:
                          kwargs['url'] = kwargs['url'] + '?' + http_build_query(params)
              
                      response = requests.request(method, **kwargs)
              
                      self.status_code = response.status_code
              
                      if self.status_code != 200:
                          reason = self.parse_reason(response.headers)
                          raise EspoAPIError('Wrong request, status code is {response.status_code}, reason is {reason}')
              
                      data = response.content
                      if not data:
                          raise EspoAPIError('Wrong request, content response is empty')
              
                      return response.json()
              
                  def normalize_url(self, action):
                      return self.url + self.url_path + action
              
                  @staticmethod
                  def parse_reason(headers):
                      if 'X-Status-Reason' not in headers:
                          return 'Unknown Error'
              
                      return headers['X-Status-Reason']
              Last edited by yuri; 04-18-2020, 09:34 AM.

              Comment


              • #8
                yuri The new API class code you sent above has worked. Now that you mention it (in your code,) it makes sense that you'd have to build the GET url query in order to make a request.get call.

                I appreciate the time you put into this. Thank you.

                Comment


                • #9
                  Maybe the api client class for python has an issue. It was contributed and I did not test it. I'm not a python developer.

                  Try to change in the client:

                  from: kwargs['data'] = params

                  to: kwargs['params'] = params

                  This will maybe fix the issue.

                  Also, still, there is an issue then upgrade pip, python version. There are lots of new APIs now available that will help you to resolve your issue.

                  Python requests get() method accepts the config object in which you can set the type, header, etc. It is the best API out there that can help you.

                  If you are using the Django framework then crud in Django makes it very easy and you can also create an API very fast.

                  I hope this helps.

                  Comment

                  Working...
                  X