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

[Bug] Create/Update Smart Computer Group - Malformed Criteria XML #46

Closed
mhrono opened this issue Jul 23, 2024 · 2 comments · Fixed by #49
Closed

[Bug] Create/Update Smart Computer Group - Malformed Criteria XML #46

mhrono opened this issue Jul 23, 2024 · 2 comments · Fixed by #49
Assignees
Labels
bug Something isn't working

Comments

@mhrono
Copy link

mhrono commented Jul 23, 2024

When attempting to create a smart group, the API is returning Error: Name, and/or, search_type are required in criterion. According to the SDK models in the docs, the data I'm giving it is valid, but the XML conversion seems to be broken. See below.

Steps to Reproduce

from jamf_pro_sdk import JamfProClient, SessionConfig
from jamf_pro_sdk.models.classic import computer_groups
from jamf_pro_sdk.clients.auth import ApiClientCredentialsProvider

jamfClient = JamfProClient(
        server=jamfURL,
        credentials=ApiClientCredentialsProvider(jamfClientID, jamfClientSecret),
        session_config=SessionConfig(**{"timeout": 30, "max_retries": 3})
)

## example var to populate actual code
productVersion = 15

## get existing group data
updatedDevicesGroupData = jamfClient.classic_api_request("get", "computergroups/name/[jamf sdk test] macOS Version Current")

## get group ID etc if exists
if updatedDevicesGroupData.ok:
    groupID = updatedDevicesGroupData.json().get("computer_group").get("id")
    groupData = jamfClient.classic_api.get_computer_group_by_id(groupID)

    ## set new criterion value and update group
    groupData.criteria[0].value = productVersion
    jamfClient.classic_api.update_smart_computer_group_by_id(groupID, groupData)

## if the group doesn't exist, create it
else:

    groupCriteria = computer_groups.ClassicCriterion(
        name="Operating System Version",
        priority=0,
        and_or="and",
        search_type="greater than or equal",
        opening_paren=False,
        closing_paren=False,
        value=productVersion
    )

    newgroupData = computer_groups.ClassicComputerGroup(
        name="[jamf sdk test] macOS Version Current",
        is_smart=True,
        criteria=[groupCriteria]
    )

    jamfClient.classic_api.create_computer_group(newgroupData)

Expected Result

The smart computer group should either be updated or created with the correct criteria.

Actual Result

The same result is observed with both create and update operation attempts. The SDK appears to validate the input, but the XML generated and sent to jamf is incorrect. Specifically, the and_or and search_type values are being split into single-character None items, when they should be simple strings. The API response also references a missing Name criterion attribute, but I'm not sure what's causing that, as that key seems to be generated properly.

SDK generated XML:

<?xml version="1.0" encoding="UTF-8" ?>
<computer_group>
	<name>[jamf sdk test] macOS Version Current</name>
	<is_smart>true</is_smart>
	<criteria>
		<criterion>
			<name>Operating System Version</name>
			<priority>0</priority>
			<and_or><None>a</None><None>n</None><None>d</None></and_or>
			<search_type><None>g</None><None>r</None><None>e</None><None>a</None><None>t</None><None>e</None><None>r</None><None> </None><None>t</None><None>h</None><None>a</None><None>n</None><None> </None><None>o</None><None>r</None><None> </None><None>e</None><None>q</None><None>u</None><None>a</None><None>l</None></search_type>
			<value>15</value>
			<opening_paren>false</opening_paren>
			<closing_paren>false</closing_paren>
		</criterion>
	</criteria>
</computer_group>
>>> groupInfo = {
...             "name": "[jamf sdk test] macOS Version Current",
...             "is_smart": True,
...             "criteria": [
...                 {
...                     "name": "Operating System Version",
...                     "priority": 0,
...                     "and_or": "and",
...                     "search_type": "greater than or equal",
...                     "opening_paren": False,
...                     "closing_paren": False,
...                     "value": productVersion
...                 }
...             ]
...         }


>>> computer_groups.ClassicComputerGroup(**groupInfo)
ClassicComputerGroup(id=None, name='[jamf sdk test] macOS Version Current', is_smart=True, site=None, criteria=[ClassicCriterion(name='Operating System Version', priority=0, and_or=<ClassicCriterionAndOr.and_: 'and'>, search_type=<ClassicCriterionSearchType.greater_than_or_equal: 'greater than or equal'>, value='15', opening_paren=False, closing_paren=False)], computers=None)


>>> computer_groups.ClassicComputerGroup(**groupInfo).xml()
Array item:  {'name': 'Operating System Version', 'priority': 0, 'and_or': <ClassicCriterionAndOr.and_: 'and'>, 'search_type': <ClassicCriterionSearchType.greater_than_or_equal: 'greater than or equal'>, 'value': '15', 'opening_paren': False, 'closing_paren': False}
'<?xml version="1.0" encoding="UTF-8" ?><computer_group><name>[jamf sdk test] macOS Version Current</name><is_smart>true</is_smart><criteria><criterion><name>Operating System Version</name><priority>0</priority><and_or><None>a</None><None>n</None><None>d</None></and_or><search_type><None>g</None><None>r</None><None>e</None><None>a</None><None>t</None><None>e</None><None>r</None><None> </None><None>t</None><None>h</None><None>a</None><None>n</None><None> </None><None>o</None><None>r</None><None> </None><None>e</None><None>q</None><None>u</None><None>a</None><None>l</None></search_type><value>15</value><opening_paren>false</opening_paren><closing_paren>false</closing_paren></criterion></criteria></computer_group>'

System Information

macOS 14.5
Python 3.12.1
Jamf Pro 11.7.0
Jamf Python SDK 0.6a1

@mhrono mhrono added the bug Something isn't working label Jul 23, 2024
@brysontyrrell
Copy link
Collaborator

Researching.

One quick thing with your example:

## get existing group data
updatedDevicesGroupData = jamfClient.classic_api_request("get", "computergroups/name/[jamf sdk test] macOS Version Current")

## get group ID etc if exists
if updatedDevicesGroupData.ok:
    groupID = updatedDevicesGroupData.json().get("computer_group").get("id")
    groupData = jamfClient.classic_api.get_computer_group_by_id(groupID)

    ## set new criterion value and update group
    groupData.criteria[0].value = productVersion
    jamfClient.classic_api.update_smart_computer_group_by_id(groupID, groupData)

You're making two calls for the same object. You can init a model from the response of the first computergroups/name request:

updatedDevicesGroupData = jamfClient.classic_api_request("get", "computergroups/name/[jamf sdk test] macOS Version Current")
computer_groups.ClassicComputerGroup(**updatedDevicesGroupData.json()["computer_group"])

@mhrono
Copy link
Author

mhrono commented Jul 24, 2024

Ahh excellent, I hadn't considered I could just re-use the data I already fetched.

Anyway, I've tested with the branch containing the fix for this, and can confirm it now works as expected! There was one warning (which is easily worked around by typecasting the value as a string):

/Users/$me/Library/Python/3.12/lib/python/site-packages/pydantic/main.py:364: UserWarning: Pydantic serializer warnings:
  Expected `str` but got `int` - serialized value may not be as expected
  return self.__pydantic_serializer__.to_python(
Array item:  {'name': 'Operating System Version', 'priority': 0, 'and_or': 'and', 'search_type': 'greater than or equal', 'value': 15, 'opening_paren': False, 'closing_paren': False}```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants