axm 0.1.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b9cf51eb8d1e42d2ee6112d4bf1bc8d38408a56581f0ed0fcaafcb4358692a7
4
- data.tar.gz: 48a58959848c13ff47d37dd9eb9cd104291bd66e422fc00ba8b3bfbdd3eea3b7
3
+ metadata.gz: 0d2f95c8bc2de0f7a2c7ea620d2f301980eb6c5a21243bc8ee878b0ae4ebb351
4
+ data.tar.gz: 59c698eebe7dbe7baf4b1378ca5a66b0045334a2d69c3441b0300ca5dc3de6a4
5
5
  SHA512:
6
- metadata.gz: bd809519957cc388dff7f4c62ce718de7f0c3990c85056a24964de019b2c77338db9078c56b488a583e289ecde0378f40e965682bcd11beaa329bbab625dccbb
7
- data.tar.gz: 8d4db509e738fe69a17265e718f8371a7c8c2d1e9a1ad7ca3c8471a5eebec62262c5d7f071c5040614fefc1568b75b47f59cd1bc331dae063b9f4ae5073812d1
6
+ metadata.gz: 8e3fc06e2e51475d49a6d5612700046e9003967200d1234ca3aa52d4ead5f08827d8f4eb432a512af127cb01920befab0a3f4dd9f354a6705a4f3ca8d1d5d781
7
+ data.tar.gz: dac8b555c797f8b773de5c58cc0de9b35128df4251a5cfbe9c3260160c11f5b2178e010f3fe9d8126a63ad144c0db71e9abd452b020488bb6440bb239e1875fe
data/CHANGELOG.md CHANGED
@@ -1,4 +1,14 @@
1
- ## [Unreleased]
1
+ ## 1.0.0 - 2025-11-08
2
+
3
+ ### Added
4
+
5
+ - List all MDM servers in an organisation
6
+ - Show all device IDs assigned to a specific MDM server
7
+ - Show which MDM server a device is assigned to
8
+ - Show the ID of the MDM server a device is assigned to
9
+ - Show information about the MDM server a device is assigned to
10
+ - Assign and unassign devices to MDM servers
11
+ - Show AppleCare coverage information for devices
2
12
 
3
13
  ## 0.1.2 - 2025-07-14
4
14
 
@@ -0,0 +1,31 @@
1
+ module Axm
2
+ class Client
3
+ module MdmServers
4
+ # Retrieves a list of MDM servers associated with the organization.
5
+ #
6
+ # @param options [Hash] Optional query parameters to filter fields or paginate results.
7
+ # - fields: (Array) Array of fields to include in the response.
8
+ # - limit: (Integer) Maximum number of devices to return per page (default: 100, maximum: 1000).
9
+ # @return [Array<Hash>] An array of MDM servers.
10
+ #
11
+ # See: https://developer.apple.com/documentation/applebusinessmanagerapi/get-mdm-servers
12
+ # See: https://developer.apple.com/documentation/appleschoolmanagerapi/get-mdm-servers
13
+ def list_mdm_servers(options = {})
14
+ get("v1/mdmServers", options)
15
+ end
16
+
17
+ # Retrieves a list of IDs for the devices assigned to a specific MDM server.
18
+ #
19
+ # @param mdm_server_id [String] The unique identifier of the MDM server.
20
+ # @param options [Hash] Optional query parameters to paginate results or increase number of returned results.
21
+ # - limit: (Integer) Maximum number of devices to return per page (default: 100, maximum: 1000).
22
+ # @return [Array<Hash>] An array of device IDs assigned to the specified MDM server.
23
+ #
24
+ # See: https://developer.apple.com/documentation/applebusinessmanagerapi/get-all-device-ids-for-a-mdmserver
25
+ # See: https://developer.apple.com/documentation/appleschoolmanagerapi/get-all-device-ids-for-a-mdmserver
26
+ def devices_assigned_to_mdm_server(mdm_server_id, options = {})
27
+ get("v1/mdmServers/#{mdm_server_id}/relationships/devices", options)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,74 @@
1
+ module Axm
2
+ class Client
3
+ module OrganizationDeviceActivities
4
+ # Get information for an organization device activity that a device management action, such as assign or unassign, creates.
5
+ #
6
+ # @param options [Hash] Optional query parameters to filter returned attributes.
7
+ # - fields: (Array) Array of fields to include in the response.
8
+ # @return [<Hash>] A single organization device activity resource.
9
+ #
10
+ # See: https://developer.apple.com/documentation/applebusinessmanagerapi/get-orgdeviceactivity-information
11
+ # See: https://developer.apple.com/documentation/appleschoolmanagerapi/get-orgdeviceactivity-information
12
+ def org_device_activity(activity_id, options = {})
13
+ get("v1/orgDeviceActivities/#{activity_id}", options)
14
+ end
15
+
16
+ # Assigns a device to an MDM server.
17
+ #
18
+ # @param device_ids [Array<String>] Array of device IDs to be assigned.
19
+ # @param mdm_server_id [String] The unique identifier of the MDM server to which the device will be assigned.
20
+ # @return [Hash, Integer] The response from the POST method, containing details of the assignment and status code.
21
+ def assign(device_id, mdm_server_id)
22
+ assignment_change(device_id, mdm_server_id, "ASSIGN_DEVICES")
23
+ end
24
+
25
+ # Unassigns a device from an MDM server.
26
+ #
27
+ # @param device_ids [Array<String>] Array of device IDs to be unassigned.
28
+ # @param mdm_server_id [String] The unique identifier of the MDM server to which the device will be assigned.
29
+ # @return [Hash, Integer] The response from the POST method, containing details of the assignment and status code.
30
+ def unassign(device_id, mdm_server_id)
31
+ assignment_change(device_id, mdm_server_id, "UNASSIGN_DEVICES")
32
+ end
33
+
34
+ private
35
+
36
+ # Sends a request to change the assignment of a device to an MDM server.
37
+ #
38
+ # @param device_ids [Array<String>] Array of IDs of devices to be assigned or unassigned.
39
+ # @param mdm_server_id [String] The unique identifier of the MDM server.
40
+ # @param activity_type [String] The type of activity being performed ("ASSIGN_DEVICES" or "UNASSIGN_DEVICES").
41
+ # @return [Hash, Integer] The response from the POST request, containing details of the operation and status code.
42
+ def assignment_change(device_ids, mdm_server_id, activity_type)
43
+ devices = device_ids.map do |device_id|
44
+ {
45
+ type: "orgDevices",
46
+ id: device_id
47
+ }
48
+ end
49
+
50
+ request_body = {
51
+ data: {
52
+ type: "orgDeviceActivities",
53
+ attributes: {
54
+ activityType: activity_type
55
+ },
56
+ relationships: {
57
+ mdmServer: {
58
+ data: {
59
+ type: "mdmServers",
60
+ id: mdm_server_id
61
+ }
62
+ },
63
+ devices: {
64
+ data: devices
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ post("v1/orgDeviceActivities", request_body)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -25,6 +25,50 @@ module Axm
25
25
  def device(id, options = {})
26
26
  get("/v1/orgDevices/#{id}", options)
27
27
  end
28
+
29
+ # Fetch the assigned device management service ID for a device.
30
+ #
31
+ # @param device_id [String] The unique identifier of the device.
32
+ # @param options [Hash] Optional query parameters to filter or paginate results.
33
+ # @return [<Hash>] Identifier of the device's assigned MDM server.
34
+ #
35
+ # See: https://developer.apple.com/documentation/applebusinessmanagerapi/get-the-assigned-server-id-for-an-orgdevice
36
+ # See: https://developer.apple.com/documentation/appleschoolmanagerapi/get-the-assigned-server-id-for-an-orgdevice
37
+ def assigned_mdm_server_id(device_id, options = {})
38
+ get("v1/orgDevices/#{device_id}/relationships/assignedServer", options)
39
+ end
40
+
41
+ # Fetch the assigned device management service information for a device.
42
+ #
43
+ # @param device_id [String] The unique identifier of the device.
44
+ # @param options [Hash] Optional query parameters to filter or paginate results.
45
+ # - id: (String) The unique identifier of the device.
46
+ # - fields: (Array) Array of fields to include in the response.
47
+ # @return [<Hash>] A hash containing the identifier of the device's assigned MDM server.
48
+ #
49
+ # See: https://developer.apple.com/documentation/applebusinessmanagerapi/get-the-assigned-server-information-for-an-orgdevice
50
+ # See: https://developer.apple.com/documentation/appleschoolmanagerapi/get-the-assigned-server-information-for-an-orgdevice
51
+ def assigned_mdm_server(device_id, options = {})
52
+ options[:fields_key] = "mdmServers"
53
+
54
+ get("v1/orgDevices/#{device_id}/assignedServer", options)
55
+ end
56
+
57
+ # Fetch the AppleCare coverage information for a device.
58
+ #
59
+ # @param device_id [String] The unique identifier of the device.
60
+ # @param options [Hash] Optional query parameters to filter or paginate results.
61
+ # - fields: (Array) Array of fields to include in the response.
62
+ # - limit: (Integer) The number of included related resources to return (default: 100, maximum: 1000).
63
+ # @return [<Hash>] A hash containing the identifier of the device's assigned MDM server.
64
+ #
65
+ # See: https://developer.apple.com/documentation/applebusinessmanagerapi/get-all-apple-care-coverage-for-an-orgdevice
66
+ # See: https://developer.apple.com/documentation/appleschoolmanagerapi/get-all-apple-care-coverage-for-an-orgdevice
67
+ def applecare_coverage(device_id, options = {})
68
+ options[:fields_key] = "appleCareCoverage"
69
+
70
+ get("v1/orgDevices/#{device_id}/appleCareCoverage", options)
71
+ end
28
72
  end
29
73
  end
30
74
  end
data/lib/axm/client.rb CHANGED
@@ -4,10 +4,14 @@ require 'net/http'
4
4
  require 'securerandom'
5
5
  require 'time'
6
6
 
7
+ require 'axm/client/mdm_servers'
8
+ require 'axm/client/organization_device_activities'
7
9
  require 'axm/client/organization_devices'
8
10
 
9
11
  module Axm
10
12
  class Client
13
+ include MdmServers
14
+ include OrganizationDeviceActivities
11
15
  include OrganizationDevices
12
16
 
13
17
  # Initializes a new instance of the AXM client.
@@ -85,15 +89,7 @@ module Axm
85
89
  return cached_access_token unless token_expired
86
90
  end
87
91
 
88
- params = {
89
- grant_type: 'client_credentials',
90
- client_id: @client_id,
91
- client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
92
- client_assertion: client_assertion,
93
- scope: "#{scope}.api"
94
- }
95
-
96
- response = post('https://account.apple.com/auth/oauth2/v2/token', params)
92
+ response = exchange_access_token_request
97
93
 
98
94
  response_body = response.first if response.last.to_i == 200
99
95
 
@@ -114,10 +110,14 @@ module Axm
114
110
  def get(path, options = {})
115
111
  options = options.dup
116
112
 
117
- endpoint = path.split('/').last
113
+ # API endpoints are prefixed with 'v1', so we can extract the endpoint from the path.
114
+ endpoint = path.split('/')[1]
115
+
116
+ # For the cases where the fields_key is different to the path component, it can be overriden as an option.
117
+ fields_key = options.delete(:fields_key) || endpoint
118
118
 
119
119
  fields = options.delete(:fields)
120
- options["fields[#{endpoint}]"] = fields.join(',') if fields
120
+ options["fields[#{fields_key}]"] = fields.join(',') if fields
121
121
 
122
122
  uri = URI("https://#{api_domain}/#{path}")
123
123
  uri.query = URI.encode_www_form(options) unless options.empty?
@@ -133,20 +133,27 @@ module Axm
133
133
  JSON.parse(res.body)
134
134
  end
135
135
 
136
- # Sends a POST request to the specified URI with given parameters.
136
+ # Sends a POST request to exchange the credentials for an access token.
137
137
  #
138
- # @param uri [String, URI] The endpoint URI.
139
- # @param params [Hash] Parameters to include in the request body.
140
- # @return [Net::HTTPResponse] The HTTP response object.
141
- def post(uri, params = {})
142
- uri = URI(uri) if uri.is_a?(String)
138
+ # @return [Net::HTTPResponse, integer] The HTTP response object and status code.
139
+ def exchange_access_token_request
140
+ uri = URI('https://account.apple.com/auth/oauth2/v2/token')
141
+
142
+ request_body = {
143
+ grant_type: 'client_credentials',
144
+ client_id: @client_id,
145
+ client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
146
+ client_assertion: client_assertion,
147
+ scope: "#{scope}.api"
148
+ }
149
+
143
150
  http = Net::HTTP.new(uri.host, uri.port)
144
151
  http.use_ssl = uri.scheme == 'https'
145
152
 
146
153
  request = Net::HTTP::Post.new(uri)
147
154
  request['Host'] = uri.host
148
155
  request['Content-Type'] = 'application/x-www-form-urlencoded'
149
- request.body = URI.encode_www_form(params) unless params.empty?
156
+ request.body = URI.encode_www_form(request_body)
150
157
 
151
158
  response = http.request(request)
152
159
 
@@ -155,8 +162,34 @@ module Axm
155
162
  response_json = JSON.parse(response.body)
156
163
 
157
164
  raise 'Invalid request' if response_json['error'] == 'invalid_request'
165
+ raise 'Invalid client ID or key ID' if response_json['error'] == 'invalid_client'
158
166
 
159
167
  [response_json, response.code]
160
168
  end
169
+
170
+ # Sends a POST request to the specified URI with given parameters.
171
+ #
172
+ # @param uri [String, URI] The endpoint URI.
173
+ # @param request_body [Hash] Parameters to include in the request body.
174
+ # @return [Hash, Integer] The HTTP response object and status code.
175
+ def post(path, request_body = {})
176
+ uri = URI("https://#{api_domain}/#{path}")
177
+
178
+ http = Net::HTTP.new(uri.host, uri.port)
179
+ http.use_ssl = uri.scheme == 'https'
180
+
181
+ request = Net::HTTP::Post.new(uri)
182
+ request['Host'] = uri.host
183
+ request['Content-Type'] = 'application/json'
184
+ request['Authorization'] = "Bearer #{access_token['access_token']}"
185
+
186
+ request.body = request_body.to_json unless request_body.empty?
187
+
188
+ response = http.request(request)
189
+
190
+ response_body = JSON.parse(response.body)
191
+
192
+ [response_body, response.code]
193
+ end
161
194
  end
162
195
  end
data/lib/axm/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Axm
4
- VERSION = "0.1.2"
4
+ VERSION = "1.0.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: axm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nick-f
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-07-14 00:00:00.000000000 Z
11
+ date: 2025-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
@@ -40,6 +40,8 @@ files:
40
40
  - Rakefile
41
41
  - lib/axm.rb
42
42
  - lib/axm/client.rb
43
+ - lib/axm/client/mdm_servers.rb
44
+ - lib/axm/client/organization_device_activities.rb
43
45
  - lib/axm/client/organization_devices.rb
44
46
  - lib/axm/secret.rb
45
47
  - lib/axm/version.rb
@@ -66,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
68
  - !ruby/object:Gem::Version
67
69
  version: '0'
68
70
  requirements: []
69
- rubygems_version: 3.4.19
71
+ rubygems_version: 3.5.22
70
72
  signing_key:
71
73
  specification_version: 4
72
74
  summary: Gem to interact with the Apple Business Manager/Apple School Manager API