Giter VIP home page Giter VIP logo

jamf-pro-sdk-python's People

Contributors

brysontyrrell avatar honestpuck avatar maczack avatar motionbug avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

jamf-pro-sdk-python's Issues

[Feature Request] Add a model for the classic API category record

In a similar fashion to other models such as classic.computer_groups there is a need for a model for classic.categories

Proposal

>>> categories = client.classic_api.list_all_categories()
>>> len(categories)
4
>>> type(categories[0])
<class 'jamf_pro_sdk.models.classic.categories.CategoriesItem'>
>>> for c in categories:
...   print(c.name)
...
Enrolment
Applications
Security
Tools

Additional Details

I actually wanted to add a model for policies but thought I would start with a simpler model, so categories.

(This project is a stretch for my python skills. We will see how I go.)

[Bug] branch main is not passing make test

Steps to Reproduce

$ git clone [email protected]:macadmins/jamf-pro-sdk-python.git
$ cd Jamf-pro-sdk-python.git
$ make test

Expected Result

I would expect a clean test.


pytest
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --cov-report=html:htmlcov --cov-report=term-missing
  inifile: /Users/tonyw/Library/CloudStorage/Dropbox/work/jamf-pro-sdk-python/pyproject.toml
  rootdir: /Users/tonyw/Library/CloudStorage/Dropbox/work/jamf-pro-sdk-python

make: *** [test] Error 4

System Information

macOS 14.0, Python 3.10.5, latest SDK

[Feedback] Handling Jamf Pro Versioning in the SDK

During the JNUC presentation a question was raised about SDK compatibility with different versions of Jamf Pro as APIs are added, deprecated, and removed. This proposal outlines an approach to alerting developers to when they are using an API that may not be compatible with the version of Jamf Pro they've connected a client to, or if they are using an API that has been deprecated.

Current

Currently all API changes have to be referenced from release notes for each Jamf Pro version. The SDK does not include any mechanisms for alerting developers.

Proposed

The SDK should contain metadata about the API methods that have been added that track the version they were added (minimum), if they are deprecated (a true/false flag), and the version they were removed (maximum).

Client Jamf Pro Version

The SDK would need to know the version of Jamf Pro during client init. This could be provided statically, automatically retrieved from the Pro API jamf-pro-version endpoint, or ignored.

As a part of this proposal the default behavior on client init would include an authenticated call to GET jamf-pro-version. This behavior may not be desirable as it requires authentication which means the client is making two network calls immediately.

Passing the version string on init would bypass this. Choosing to ignore the client version through an argument would then disable ALL of the warning system.

This new option should be added to the client config.

from jamf_pro_sdk import JamfProClient, BasicAuthProvider, SessionConfig

config = SessionConfig()
config.server_version = "10.50"
client.server_version = None  # <-- Default, triggers call to jamf-pro-version API
client.ignore_server_version = True  # <-- Default is False, setting True will disable all Jamf Pro version warnings

client = JamfProClient(
    server="jamf.my.org",
    credentials=BasicAuthProvider("oscar", "j@mf1234!"),
    config=config
)

API Warnings

If the client is instantiated with a version then all endpoints would need to emit WARNINGS when a method is being used that:

  1. Has a minimum version ABOVE the client's Jamf Pro version.
  2. Has been flagged as deprecated and should be migrated off of.
  3. Has a maximum version LOWER than the client's Jamf Pro version.

This will be handled using Python's warnings module. Each unique warning will only appear once. The loggers can be configured to capture warning messages so developers will know when a particular client is making requests that meet one of the conditions above.

import logging
import warnings

from jamf_pro_sdk import logger_quick_setup

logger_quick_setup()  # Updated to include capturing warnings 
# logging.captureWarnings(True)
# warnings_logger = logging.getLogger("py.warnings")
# warnings_logger.addHandler(handler)

warnings.warn("This API is deprecated as of version 10.50 and will be removed in a future.")
# 2023-10-05 10:30:04,746 py.warnings WARNING MainThread <stdin>:1: UserWarning: This API is deprecated as of Jamf Pro 10.50 and will be removed in a future version.

When a deprecation flag is set there should be included a message for which API method the developer should migrate to using (if one exists). This migration message should carry forward for case 3.

The SDK will NOT throw version exceptions as a part of this warnings system. The client will return 404 errors if the API does not exist and the warning message can be captured in logs if the developer follows guidance in the documentation.

[Bug] send_mdm_command_preview broken?

When I run the send_mdm_command_preview with a UUID and a command, I get a pydantic.error_wrappers.ValidationError

Steps to Reproduce

from jamf_pro_sdk import JamfProClient
from jamf_pro_sdk.clients import auth
from jamf_pro_sdk.models.pro.mdm import LogOutUserCommand

client = JamfProClient(
    server="*.jamfcloud.com",
    credentials=auth.ApiClientCredentialsProvider(
        client_id="",
        client_secret=""
    )
)

logout_user = client.pro_api.send_mdm_command_preview(
    management_ids=[""],
    command=LogOutUserCommand
)

All empty quotes have valid values.

Expected Result

The MDM command is sent successfully.

Actual Result

Traceback (most recent call last):
  File "/Users/jasonausmus/repos/wayspring-jamf/auth.py", line 14, in <module>
    logout_user = client.pro_api.send_mdm_command_preview(
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jasonausmus/.local/share/virtualenvs/wayspring-jamf-_868w2GM/lib/python3.11/site-packages/jamf_pro_sdk/clients/pro_api/__init__.py", line 232, in send_mdm_command_preview
    data = SendMdmCommand(
           ^^^^^^^^^^^^^^^
  File "pydantic/main.py", line 341, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 2 validation errors for SendMdmCommand
commandData
  Discriminator 'commandType' is missing in value (type=value_error.discriminated_union.missing_discriminator; discriminator_key=commandType)
commandData
  value is not a valid dict (type=type_error.dict)

I'm not sure what I'm doing wrong here and I can't find any documented examples of using this method.

System Information

[Feedback] delete and update methods should return something on success

Current

At the moment the delete and update methods for each endpoint are returning None regardless of success. They should return something

Proposed

I propose that api_request method return a Bool and the methods in question return the <object>_id on success for those two methods in each endpoint

    def delete_computer_by_id(self, computer: ComputerId) -> int:
        """Delete a single computer record using the ID.

        :param computer: A computer ID or supported Classic API model.
        :type computer: Union[int, ClassicComputer, ClassicComputersItem]

        """
        computer_id = ClassicApi._parse_id(computer)
       if self.api_request(method="delete", resource_path=f"computers/id/{computer_id}"):
           return computer_id
       else:
           return -1
     

System Information

0.4a1 Release

[Feature Request] Option to parse XML from Classic API endpoints

Currently, JSON responses are requested from the Classic API and can be parsed with the .json() method:

response = api.classic_api_request("GET", "accounts/userid/257")
user_info = response.json()

However, there's no equivalent option for parsing XML responses if the headers are overridden to request XML:

response = api.classic_api_request("GET", "accounts/userid/257", override_headers={"Accept": "application/xml"})
user_info = response.xml()  # this doesn't exist

Some orgs may benefit from the ability to choose between XML and JSON responses. One use case would be working around Jamf product issues that affect the accuracy of information returned via one format โ€” like PI104345: /accounts endpoint giving different results when searching by userid vs. username.

Proposal

The SDK could provide an .xml() method similar to the existing .json() method that converts XML responses to dictionary form, possibly leveraging a module like xmltodict to do the work behind the scenes.

Alternatively, the response could contain a .data attribute that provides the relevant information in structured data (lists or dicts) regardless of what serialization format was requested โ€” a parsed equivalent to .text. This attribute could replace both .json() and .xml() functionalities.

Thanks for considering.

[Bug] Bizarre 409 error when updating a static group using a string value for the computer ID.

Steps to Reproduce

client.classic_api.update_static_computer_group_membership_by_id(1, computers_to_add=["1"])

Expected Result

The computer is added. The underlying code should be casting the string value to an integer. The Pydantic model exports the correct XML document if the provided value is a string or an integer.

Actual Result

Debug logging to the console:

[ERROR] HTTPError: 409 Client Error:  for url: https//XXXXXXXXXX/JSSResource/computergroups/id/1
Error: Unable to match computer

I think this is an encoding issue with the XML and Jamf is throwing an incorrect error.

When looking up a computer using the Pro API that returns the id as a string value. Passing this directly to the static group update call surfaced the error.

System Information

jamf-pro-sdk 0.3a1
Jamf Pro 10.48.2

[Bug] Can't call the ApiClientCredentialsProvider class on import

Simple script:

from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider

url="https://test.jamfcloud.com"
client_id="<redacted>"
client_secret="<redacted>"

client = JamfProClient(
    server="snyk.jamfcloud.com",
    credentials=ApiClientCredentialsProvider(client_id, client_secret)
)

all_computers = client.pro_api.get_computer_inventory_v1()
print(all_computers)

This results in: cannot import name 'ApiClientCredentialsProvider' from 'jamf_pro_sdk' (/opt/homebrew/lib/python3.11/site-packages/jamf_pro_sdk/init.py)

Installed:
jamf-pro-sdk 0.4a1

[Bug] ApiClientCredentialsProvider -> AccessToken() -> Pedantic -> Scope

Feel free to tell me to go figure it out for myself but I though I'd check in with you first in case you were just still working on this part. It's still alpha, after all.

I'm getting an error trying ApiClientCredentialsProvider. from jamf_pro_sdk import ApiClientCredentialsProvider didn't work but it does if I add it to the from .clients.auth import list in ./jamf_pro_sdk/__init__.py or if I import it by path... from jamf_pro_sdk.clients.auth import ApiClientCredentialsProvider

I tried using it and get an error where pydantic is expecting a scope that doesn't yet exist.

  File "/Users/admin/PycharmProjects/Jamf Client API/bryson.py", line 32, in <module>
    credentials=ApiClientCredentialsProvider(client_id=get_env_value("client_id"),
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/admin/PycharmProjects/Jamf Client API/venv/lib/python3.11/site-packages/jamf_pro_sdk/clients/auth.py", line 155, in __init__
    super().__init__()
  File "/Users/admin/PycharmProjects/Jamf Client API/venv/lib/python3.11/site-packages/jamf_pro_sdk/clients/auth.py", line 41, in __init__
    self._access_token = AccessToken()
                         ^^^^^^^^^^^^^
  File "/Users/admin/PycharmProjects/Jamf Client API/venv/lib/python3.11/site-packages/jamf_pro_sdk/models/__init__.py", line 9, in __init__
    super().__init__(**kwargs)
  File "/Users/admin/PycharmProjects/Jamf Client API/venv/lib/python3.11/site-packages/pydantic/main.py", line 164, in __init__
    __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
pydantic_core._pydantic_core.ValidationError: 1 validation error for AccessToken
scope
  Field required [type=missing, input_value={}, input_type=dict]

This is what I was trying...

import logging
import os
from jamf_pro_sdk import JamfProClient
from jamf_pro_sdk.clients.auth import ApiClientCredentialsProvider
from jamf_pro_sdk.helpers import logger_quick_setup

logger_quick_setup(level=logging.DEBUG)

def get_env_value(variable_name):
    """
    :param variable_name:
    :return: value

    Throws env error if 1) env var does not exist or 2) it exists but has no value
    """
    value = os.getenv(variable_name)
    if not value:
        raise EnvironmentError(f"get_env_value: The {variable_name} variable exists but does not have a value")
    return value


credentials=ApiClientCredentialsProvider(client_id=get_env_value("client_id"),
                                         client_secret= get_env_value("client_secret"))

client = JamfProClient(
    server=get_env_value("server"),
    credentials=credentials
)

System Information

Python 3.11
Sonoma 14.1
Jamf Pro SDK 0.4a1

[Feedback] src/jamf_pro_sdk/clients/pro_api/__init__.py may get unwieldy

Current

The current behaviour is to put all the functions for all the pro endpoints into the __init__.py file. I realise this allows calls like

ifrom jamf_pro_sdk import JamfProClient, BasicAuthProvider

client = JamfProClient(
    server="dummy.jamfcloud.com",
    credentials=BasicAuthProvider("username", "password")
)

all_computers = client.pro_api.get_computer_inventory_v1()

but when the number of endpoints gets high this file will grow to be difficult to deal with.

Proposed

I would propose that in the directory there could be a file computer_v1 which contains (among others) the function get_inventory then the call would be:

from jamf_pro_sdk import JamfProClient, BasicAuthProvider

client = JamfProClient(
    server="dummy.jamfcloud.com",
    credentials=BasicAuthProvider("username", "password")
)

all_computers = client.pro_api.computer_v1.get_inventory()

System Information

SDK version 4.0a

[Bug] ClassicAdvancedComputerSearch error when scope includes 'member of'

Attempting to get a list of computers missing a Recovery lock password, target report was:
"is not" Recovery Lock Enabled = Enabled
"is" Recovery Lock Enabled = arm64
"member of" Computer Group = All Managed Clients

workaround is to change the "member of" operator to "is" Managed = Managed

Steps to Reproduce

from jamf_pro_sdk import JamfProClient

reportdata = client.classic_api.get_advanced_computer_search_by_id(reportwithmemberof)

Expected Result

Output of report computers

Actual Result

Traceback (most recent call last):
File "/usr/local/bin/Scripts/./JAMF_Script.py", line 35, in
reportdata = client.classic_api.get_advanced_computer_search_by_id(98)
File "/home/username/.local/lib/python3.10/site-packages/jamf_pro_sdk/clients/classic_api.py", line 490, in get_advanced_computer_search_by_id
return ClassicAdvancedComputerSearch(**resp.json()["advanced_computer_search"])
File "/home/username/.local/lib/python3.10/site-packages/jamf_pro_sdk/models/init.py", line 9, in init
super().init(**kwargs)
File "/home/username/.local/lib/python3.10/site-packages/pydantic/main.py", line 175, in init
self.pydantic_validator.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 1 validation error for ClassicAdvancedComputerSearch
criteria.2.search_type
Input should be 'is', 'is not', 'like', 'not like', 'has', 'does not have', 'matches regex', 'does not match regex', 'before (yyyy-mm-dd)', 'after (yyyy-mm-dd)', 'more than x days ago', 'less than x days ago', 'current', 'not current', 'greater than', 'less than', 'greater than or equal' or 'less than or equal' [type=enum, input_value='member of', input_type=str]
For further information visit https://errors.pydantic.dev/2.7/v/enum

System Information

Ubuntu 22.04.4 LTS
Name: jamf-pro-sdk
Version: 0.6a1
Name: pydantic
Version: 2.7.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.