MattParr / python-atws

Autotask Web Services python module
MIT License
32 stars 10 forks source link

atws.wrapper.Wrapper.client.service.getEntityInfo(): suds.TypeNotFound: Type not found: 'UserAccessForCreate' #75

Closed dahifi closed 4 years ago

dahifi commented 4 years ago

I want to start building out some macros to auto-generate classes for each entity accessible through the API. The API docs point to getEntityInfo(), and I'm able to call it, but something in the parsing is failing.

If someone can confirm whether they get the same error using suds 0.7 I can rule that out.

Python 3.6.8:


at = atws.connect(username=AUTOTASK_USERNAME,
                  password=AUTOTASK_PASSWORD,
                  apiversion=1.6,
                  integrationcode=AUTOTASK_API_TOKEN)

at.client.service.getEntityInfo()

<suds.sax.document.Document object at 0x7f0d2dba16a0>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "suds/client.py", line 521, in __call__
    return client.invoke(args, kwargs)
  File "suds/client.py", line 581, in invoke
    result = self.send(soapenv)
  File "suds/client.py", line 621, in send
    original_soapenv=original_soapenv)
  File "suds/client.py", line 685, in process_reply
    self.method, replyroot)
  File "suds/bindings/binding.py", line 147, in get_reply
    return self.unmarshaller().process(nodes[0], resolved)
  File "suds/umx/typed.py", line 66, in process
    return Core.process(self, content)
  File "suds/umx/core.py", line 46, in process
    return self.append(content)
  File "suds/umx/core.py", line 61, in append
    self.append_children(content)
  File "suds/umx/core.py", line 138, in append_children
    cval = self.append(cont)
  File "suds/umx/core.py", line 61, in append
    self.append_children(content)
  File "suds/umx/core.py", line 138, in append_children
    cval = self.append(cont)
  File "suds/umx/core.py", line 59, in append
    self.start(content)
  File "suds/umx/typed.py", line 80, in start
    raise TypeNotFound(content.node.qname())
suds.TypeNotFound: Type not found: 'UserAccessForCreate'
dahifi commented 4 years ago

I can get a string of the types by calling print(at.client):

Suds ( https://fedorahosted.org/suds/ )  version: 0.6

Service ( ATWS ) tns="http://autotask.net/ATWS/v1_6/"
   Prefixes (1)
      ns0 = "http://autotask.net/ATWS/v1_6/"
   Ports (1):
      (ATWSSoap)
         Methods (14):
            CreateAttachment(Attachment attachment)
            DeleteAttachment(xs:long attachmentId)
            GetAttachment(xs:long attachmentId)
            GetFieldInfo(xs:string psObjectType)
            GetInvoiceMarkup(xs:int InvoiceId, xs:string Format)
            GetWsdlVersion()
            create(ArrayOfEntity Entities)
            delete(ArrayOfEntity Entities)
            getEntityInfo()
            getThresholdAndUsageInfo()
            getUDFInfo(xs:string psTable)
            getZoneInfo(xs:string UserName)
            query(xs:string sXML)
            update(ArrayOfEntity Entities)
         Types (162):
            ATWSError
            ATWSResponse
            ATWSZoneInfo
            Account
            [...]
            UserDefinedFieldDefinition
            UserDefinedFieldListItem
            WorkTypeModifier

Alternatively, at.client.sd[0].type:

[(<Complex:0x7f0d2d3d4240 name="ATWSError" />,
  <Complex:0x7f0d2d3d4240 name="ATWSError" />),
 (<Complex:0x7f0d2d4221d0 name="ATWSResponse" />,
  <Complex:0x7f0d2d4221d0 name="ATWSResponse" />),
 (<Complex:0x7f0d2d3f8278 name="ATWSZoneInfo" />,
  <Complex:0x7f0d2d3f8278 name="ATWSZoneInfo" />),
 (<Complex:0x7f0d2dcc6278 name="Account" />,
  <Complex:0x7f0d2dcc6278 name="Account" />),
[...]
 (<Complex:0x7f0d2d38c780 name="UserDefinedFieldListItem" />,
  <Complex:0x7f0d2d38c780 name="UserDefinedFieldListItem" />),
 (<Complex:0x7f0d2d46e278 name="WorkTypeModifier" />,
  <Complex:0x7f0d2d46e278 name="WorkTypeModifier" />)]

...and create a list of all available types via comprehension: ENTITIES = [at.client.sd[0].xlate(type[0]) for type in at.client.sd[0].types]

I'm going to use this to try and write a query parser along the lines of Django's queryset, so we can have something like Tickets.objects.get(ticket_number__eq=12345) or Account.object.get(acccount_name__contains='ABC')

Not quite sure I'm up to the task, but I just find the current implementation way to cumbersome.

dahifi commented 4 years ago

Back to the original error, I was able to do some debugging in suds. Here's the raw XML for the getEntityInfo():

<EntityInfo><Name>Account</Name><CanUpdate>true</CanUpdate><CanDelete>false</CanDelete><CanCreate>true</CanCreate><CanQuery>true</CanQuery><UserAccessForCreate>All</UserAccessForCreate><UserAccessForQuery>All</UserAccessForQuery><UserAccessForUpdate>All</UserAccessForUpdate><UserAccessForDelete>None</UserAccessForDelete><HasUserDefinedFields>true</HasUserDefinedFields></EntityInfo>

Looks like the issue is with All not being a type. I'll see if I can fix it using suds Doctor (https://stackoverflow.com/questions/3760427/problem-accessing-wsdl-service-with-python-suds-raises-typenotfound-arrayofint) and submit a PR.

MattParr commented 4 years ago

I think it's because the WSDL does not define an element UserAccessForCreate, or UserAccessForUpdate, or UserAccessForQuery or UserAccessForDelete but the service method GetEntityInfo returns those elements/attributes in the EntityInfo.

I think the quickest way to get to this would be using a message plugin and stripping those elements from a response to GetEntityInfo.

MattParr commented 4 years ago

in ee31710 on the getentityinfo branch i've made a plugin that fixes the responses for py3 I'm okay with this approach so I'll move it into a release once it works on py2

in the original example in this issue, you should just be able to call a service method straight from the wrapper eg:

at.getEntityInfo()

Tell me more about

building out some macros to auto-generate classes for each entity accessible through the API.

dahifi commented 4 years ago

I think I mentioned this Powershell module before: https://github.com/ecitsolutions/Autotask They have some sort of metaprogramming module that parses the entity list, then cycles through them, generating getters and setters as appropriate. Parameters are created based on the entityFieldInfo.

I had been doing some research into Python metaprogramming when I made this request, but I'm having to put this line of research on hold since I'm focused on some numerical analysis work in C++ for the next two months.

The code that they use to create the modules is in the https://github.com/ecitsolutions/Autotask/tree/master/Autotask/Private directory, if you're interested in taking a look.