22
33import logging
44import math
5+ import warnings
56from pathlib import Path
67from tempfile import TemporaryDirectory
78from typing import TYPE_CHECKING , Callable , Iterator , Union
@@ -134,9 +135,55 @@ def _upload_part(s3_client, multipart_upload: dict, part_number: int, file_uploa
134135 logger .debug (part_resp )
135136 return {"PartNumber" : part_number , "ETag" : part_resp ["ETag" ]}
136137
138+ def upload_package (self , file_path : Union [str , Path ]) -> None :
139+ """Upload a file and create the package object using the new package upload API.
140+
141+ This method replaces :meth:`upload_file` and does not require the ``aws`` extra dependency.
142+ It uses the ``POST /v1/packages/{id}/upload`` endpoint instead of the deprecated JCDS v1
143+ S3-based workflow.
144+
145+ A ``JCDS2FileExistsError`` is raised if any file of the same name exists and is associated
146+ to a package.
147+
148+ :param file_path: The path to the file to upload. Will raise ``FileNotFoundError`` if the
149+ path to the file's location does not exist.
150+ :type file_path: Union[str, Path]
151+ """
152+ if not isinstance (file_path , Path ):
153+ file_path = Path (file_path )
154+
155+ if not file_path .exists ():
156+ raise FileNotFoundError (f"File not found: { file_path } " )
157+
158+ packages = [
159+ self .classic_api_client .get_package_by_id (p )
160+ for p in self .classic_api_client .list_all_packages ()
161+ ]
162+
163+ for p in packages :
164+ if file_path .name == p .filename :
165+ raise JCDS2FileExistsError (
166+ f"The file '{ file_path .name } ' exists and is associated to package "
167+ f"({ p .id } ) '{ p .name } '"
168+ )
169+
170+ new_package = ClassicPackage (name = file_path .name , filename = file_path .name )
171+ new_pkg_id = self .classic_api_client .create_package (data = new_package )
172+ logger .debug ("Created package %s" , new_pkg_id )
173+
174+ try :
175+ self .pro_api_client .upload_package_v1 (package_id = new_pkg_id , file_path = file_path )
176+ except Exception as err :
177+ logger .exception (err )
178+ raise
179+
137180 def upload_file (self , file_path : Union [str , Path ]) -> None :
138181 """Upload a file to the JCDS and create the package object.
139182
183+ .. deprecated::
184+ The JCDS v1 API is deprecated by Jamf (2025-08-28). Use :meth:`upload_package` instead,
185+ which does not require the ``aws`` extra dependency.
186+
140187 If the file is less than 1 GiB in size the upload will be performed in a single request. If
141188 the file is greater than 1 GiB in size a multipart upload operation will be performed.
142189
@@ -151,6 +198,12 @@ def upload_file(self, file_path: Union[str, Path]) -> None:
151198 to the file's location does not exist.
152199 :type file_path: Union[str, Path]
153200 """
201+ warnings .warn (
202+ "upload_file() is deprecated. The JCDS v1 API was deprecated by Jamf on 2025-08-28. "
203+ "Use upload_package() instead, which does not require the 'aws' extra dependency." ,
204+ DeprecationWarning ,
205+ stacklevel = 2 ,
206+ )
154207 if not BOTO3_IS_INSTALLED :
155208 raise ImportError ("The 'aws' extra dependency is required." )
156209
0 commit comments