ruby-jss 1.3.3 → 1.4.1

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.

Potentially problematic release.


This version of ruby-jss might be problematic. Click here for more details.

@@ -51,12 +51,12 @@ module JSS
51
51
  # - :security
52
52
  #
53
53
  # Additionally, items that apper in macOS Slf Svc have these keys:
54
- # - :self_service_display_name
54
+ # - :self_service_display_name (but not JSS::MacApplication)
55
55
  # - :install_button_text
56
- # - :reinstall_button_text
56
+ # - :reinstall_button_text (but not JSS::MacApplication)
57
57
  # - :force_users_to_view_description
58
58
  # - :notification
59
- # - :notification_location # PENDING API FIX
59
+ # - :notification_location # PENDING API FIX, and also, not JSS::MacApplication
60
60
  # - :notification_subject
61
61
  # - :notification_message
62
62
  #
@@ -178,15 +178,14 @@ module JSS
178
178
  notification_reminders: true
179
179
  },
180
180
  JSS::MacApplication => {
181
- # in_self_service_data_path was finally implemnted in JamfPro 10.9
182
- # Jamf Product Issue [PI-003773]
183
- in_self_service_data_path: [:general, :deployment_type],
181
+ in_self_service_data_path: %i[general deployment_type],
184
182
  in_self_service: MAKE_AVAILABLE,
185
183
  not_in_self_service: AUTO_INSTALL_OR_PROMPT,
186
184
  targets: [:macos],
187
185
  payload: :app,
188
186
  can_display_in_categories: true,
189
187
  can_feature_in_categories: true,
188
+ notifications_supported: :ssvc_and_nctr,
190
189
  url_entity: 'app'
191
190
  # OTHER BUG: no notification options seem to be changable via the API
192
191
  },
@@ -329,6 +328,7 @@ module JSS
329
328
  #
330
329
  def self_service_view_url
331
330
  return nil unless @self_service_data_config[:url_entity]
331
+
332
332
  "#{USER_URL_BASE}#{@self_service_data_config[:url_entity]}&id=#{id}&action=#{USER_URL_VIEW_ACTION}"
333
333
  end
334
334
 
@@ -336,6 +336,7 @@ module JSS
336
336
  #
337
337
  def self_service_execute_url
338
338
  return nil unless @self_service_data_config[:url_entity]
339
+
339
340
  "#{USER_URL_BASE}#{@self_service_data_config[:url_entity]}&id=#{id}&action=#{USER_URL_EXEC_ACTION}"
340
341
  end
341
342
 
@@ -349,6 +350,7 @@ module JSS
349
350
  def self_service_description=(new_val)
350
351
  new_val = new_val.strip
351
352
  return if @self_service_description == new_val
353
+
352
354
  @self_service_description = new_val
353
355
  @need_to_update = true
354
356
  end
@@ -820,7 +822,7 @@ module JSS
820
822
  # ssvc subset...
821
823
  add_in_self_service_xml doc_root
822
824
 
823
- subset_key = @self_service_data_config[:self_service_subset] ? @self_service_data_config[:self_service_subset] : :self_service
825
+ subset_key = @self_service_data_config[:self_service_subset] || :self_service
824
826
 
825
827
  ssvc = doc_root.add_element subset_key.to_s
826
828
 
@@ -857,6 +859,7 @@ module JSS
857
859
  # add the xml specific to profiles
858
860
  def add_self_service_profile_xml(ssvc, doc_root)
859
861
  return unless self_service_payload == :profile
862
+
860
863
  if self_service_targets.include? :ios
861
864
  sec = ssvc.add_element('security')
862
865
  sec.add_element('removal_disallowed').text = PROFILE_REMOVAL_BY_USER[@self_service_user_removable]
@@ -871,6 +874,7 @@ module JSS
871
874
  def add_self_service_category_xml(ssvc)
872
875
  cats = ssvc.add_element('self_service_categories')
873
876
  return if self_service_categories.empty?
877
+
874
878
  self_service_categories.each do |cat|
875
879
  catelem = cats.add_element('category')
876
880
  catelem.add_element('name').text = cat[:name]
@@ -882,10 +886,14 @@ module JSS
882
886
  # set macOS settings in ssvc xml
883
887
  def add_self_service_macos_xml(ssvc)
884
888
  return unless self_service_targets.include? :macos
885
- ssvc.add_element('self_service_display_name').text = self_service_display_name if self_service_display_name
889
+
886
890
  ssvc.add_element('install_button_text').text = self_service_install_button_text if self_service_install_button_text
887
- ssvc.add_element('reinstall_button_text').text = self_service_reinstall_button_text if self_service_reinstall_button_text
888
891
  ssvc.add_element('force_users_to_view_description').text = self_service_force_users_to_view_description.to_s
892
+
893
+ return if self.class == JSS::MacApplication
894
+
895
+ ssvc.add_element('self_service_display_name').text = self_service_display_name if self_service_display_name
896
+ ssvc.add_element('reinstall_button_text').text = self_service_reinstall_button_text if self_service_reinstall_button_text
889
897
  end
890
898
 
891
899
 
@@ -103,7 +103,7 @@ module JSS
103
103
  # Methods
104
104
  #####################################
105
105
 
106
- #
106
+
107
107
  # Upload a file to the JSS via the REST Resource of the
108
108
  # object to which this module is mixed in.
109
109
  #
@@ -144,7 +144,7 @@ module JSS
144
144
 
145
145
  ### @return [Array<Hash>]
146
146
  ###
147
- ### The vpp assignments associated with this user
147
+ ### The user-based vpp assignments associated with this user
148
148
  ###
149
149
  ### Each Hash has then :id and :name for one assignment
150
150
  ###
@@ -249,6 +249,47 @@ module JSS
249
249
  @need_to_update = true
250
250
  end
251
251
 
252
+ # Workaround for the recurring Jamf Classic API Bug where
253
+ # JSON is missing data that should come in an array of hashes, but
254
+ # only comes as a hash with a single hash inside, with data for only
255
+ # the last item in the XML array.
256
+ #
257
+ # When needed, we fetch and parse the XML, which has the desired data.
258
+ # Use any truthy parameter to re-fetch the XML data, otherwise the
259
+ # data last fetched is used.
260
+ #
261
+ # In this case, the user group data is fetched as XML and returned as
262
+ # an Array of Hashes, one per group the user is a member of. Each hash
263
+ # containing three Symbol keys:
264
+ #
265
+ # id: Integer, the group id
266
+ # name: String, the group name
267
+ # is_smart: Boolean, is it a smart group or a static group?
268
+ #
269
+ # @param refresh[Boolean] Re-fetch the group data from the API
270
+ #
271
+ # @return [Array<Hash>] The groups the user is a member of.
272
+ #
273
+ def user_groups(refresh = false)
274
+ @grp_array = nil if refresh
275
+ return @grp_array if @grp_array
276
+
277
+ @grp_array = []
278
+ raw_xml = @api.get_rsrc "/users/id/#{@id}", :xml
279
+ xmlroot = REXML::Document.new(raw_xml).root
280
+ xml_grps = xmlroot.elements['user_groups']
281
+
282
+ xml_grps.each do |xml_grp|
283
+ next if xml_grp.name == 'size'
284
+
285
+ gid = xml_grp.elements['id'].text.to_i
286
+ gname = xml_grp.elements['name'].text
287
+ smart = xml_grp.elements['is_smart'].text == 'true'
288
+ @grp_array << { id: gid, name: gname, is_smart: smart }
289
+ end # groups.each
290
+
291
+ @grp_array
292
+ end # user_groups
252
293
 
253
294
  #####################################
254
295
  ### Private Instance Methods
@@ -0,0 +1,209 @@
1
+ ### Copyright 2020 Pixar
2
+
3
+ ###
4
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ ### with the following modification; you may not use this file except in
6
+ ### compliance with the Apache License and the following modification to it:
7
+ ### Section 6. Trademarks. is deleted and replaced with:
8
+ ###
9
+ ### 6. Trademarks. This License does not grant permission to use the trade
10
+ ### names, trademarks, service marks, or product names of the Licensor
11
+ ### and its affiliates, except as required to comply with Section 4(c) of
12
+ ### the License and to reproduce the content of the NOTICE file.
13
+ ###
14
+ ### You may obtain a copy of the Apache License at
15
+ ###
16
+ ### http://www.apache.org/licenses/LICENSE-2.0
17
+ ###
18
+ ### Unless required by applicable law or agreed to in writing, software
19
+ ### distributed under the Apache License with the above modification is
20
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ ### KIND, either express or implied. See the Apache License for the specific
22
+ ### language governing permissions and limitations under the Apache License.
23
+ ###
24
+ ###
25
+
26
+ ###
27
+ module JSS
28
+
29
+ # A VPP account defined in the JSS
30
+ #
31
+ class VPPAccount < JSS::APIObject
32
+
33
+ # Mix-Ins
34
+ #####################################
35
+ include JSS::Updatable
36
+ include JSS::Sitable
37
+
38
+ # Class Constants
39
+ #####################################
40
+
41
+ ### The base for REST resources of this class
42
+ RSRC_BASE = 'vppaccounts'.freeze
43
+
44
+ ### the hash key used for the JSON list output of all objects in the JSS
45
+ RSRC_LIST_KEY = :vpp_accounts
46
+
47
+ ### The hash key used for the JSON object output.
48
+ ### It's also used in various error messages
49
+ RSRC_OBJECT_KEY = :vpp_account
50
+
51
+ SITE_SUBSET = :top
52
+
53
+ # Attributes
54
+ #####################################
55
+
56
+ # @return [String] The full name of the local contact person for the acct
57
+ attr_reader :contact
58
+
59
+ # @return [String] The service token for connecting to the account at Apple.
60
+ # Currently not visible, appears as '***************'
61
+ attr_reader :service_token
62
+
63
+ # @return [String] The name of the company associated with the Acct/Token
64
+ attr_reader :account_name
65
+
66
+ # @return [Time] The expiration date of the Acct/Token
67
+ attr_reader :expiration_date
68
+
69
+ # @return [String] The location associated with the Acct/Token
70
+ attr_reader :location_name
71
+ alias location location_name
72
+
73
+ # @return [String] The Country Code associated with the acct
74
+ attr_reader :country
75
+
76
+ # @return [String] The AppleID associated with the acct
77
+ attr_reader :apple_id
78
+
79
+ # @return [Boolean] Automatically populate purchased content from Apple
80
+ # School Manager or Apple Business Manager in Jamf Pro
81
+ attr_reader :populate_catalog_from_vpp_content
82
+
83
+ # @return [Boolean] Display a notification to users on their mobile devices
84
+ # when a volume purchased app in a user-based volume assignment is no
85
+ # longer assigned to them
86
+ attr_reader :notify_disassociation
87
+
88
+ # @return [Boolean] Automatically register users that have Managed Apple IDs
89
+ # so they do not receive an invitation and are not prompted to register
90
+ # with volume purchasing
91
+ attr_reader :auto_register_managed_users
92
+
93
+ # Constructor
94
+ #####################################
95
+
96
+ # See JSS::APIObject#initialize
97
+ #
98
+ def initialize(args = {})
99
+ super
100
+ @contact = @init_data[:contact]
101
+ @service_token = @init_data[:service_token]
102
+ @account_name = @init_data[:account_name]
103
+ @expiration_date = @init_data[:expiration_date].to_s.empty? ? nil : Time.parse(@init_data[:expiration_date])
104
+ @location_name = @init_data[:location_name]
105
+ @country = @init_data[:country]
106
+ @apple_id = @init_data[:apple_id]
107
+ @populate_catalog_from_vpp_content = @init_data[:populate_catalog_from_vpp_content]
108
+ @notify_disassociation = @init_data[:notify_disassociation]
109
+ @auto_register_managed_users = @init_data[:auto_register_managed_users]
110
+ end
111
+
112
+ # Public Instance Methods
113
+ #####################################
114
+
115
+ # @param new_val[String] the value
116
+ #
117
+ # @return [void]
118
+ #
119
+ def contact=(new_val = @contact)
120
+ return if new_val == @contact
121
+
122
+ @contact = JSS::Validate.non_empty_string new_val, 'Contact must be a non-empty String'
123
+ @need_to_update = true
124
+ end
125
+
126
+ # @param new_val[String] the value
127
+ #
128
+ # @return [void]
129
+ #
130
+ def country=(new_val = @country)
131
+ return if new_val == @country
132
+
133
+ @country = JSS::Validate.app_store_country_code new_val
134
+ @need_to_update = true
135
+ end
136
+
137
+ # @param new_val[String] the value
138
+ #
139
+ # @return [void]
140
+ #
141
+ def apple_id=(new_val = @apple_id)
142
+ return if new_val == @apple_id
143
+
144
+ @apple_id = JSS::Validate.email_address new_val
145
+ @need_to_update = true
146
+ end
147
+
148
+ # @param new_val[String] the value
149
+ #
150
+ # @return [void]
151
+ #
152
+ def populate_catalog_from_vpp_content=(new_val = @populate_catalog_from_vpp_content)
153
+ return if new_val == @populate_catalog_from_vpp_content
154
+
155
+ @populate_catalog_from_vpp_content = JSS::Validate.boolean new_val
156
+ @need_to_update = true
157
+ end
158
+
159
+ # @param new_val[String] the value
160
+ #
161
+ # @return [void]
162
+ #
163
+ def notify_disassociation=(new_val = @notify_disassociation)
164
+ return if new_val == @notify_disassociation
165
+
166
+ @notify_disassociation = JSS::Validate.boolean new_val
167
+ @need_to_update = true
168
+ end
169
+
170
+ # @param new_val[String] the value
171
+ #
172
+ # @return [void]
173
+ #
174
+ def auto_register_managed_users=(new_val = @auto_register_managed_users)
175
+ return if new_val == @auto_register_managed_users
176
+
177
+ @auto_register_managed_users = JSS::Validate.boolean new_val
178
+ @need_to_update = true
179
+ end
180
+
181
+ # Private Instance Methods
182
+ #####################################
183
+ private
184
+
185
+ # Return a String with the XML Resource
186
+ # for submitting creation or changes to the JSS via
187
+ # the API via the Creatable or Updatable modules
188
+ #
189
+ # Most classes will redefine this method.
190
+ #
191
+ def rest_xml
192
+ doc = REXML::Document.new APIConnection::XML_HEADER
193
+ tmpl = doc.add_element self.class::RSRC_OBJECT_KEY.to_s
194
+ tmpl.add_element('name').text = @name
195
+ tmpl.add_element('contact').text = @contact
196
+ tmpl.add_element('country').text = @country
197
+ tmpl.add_element('apple_id').text = @apple_id
198
+ tmpl.add_element('populate_catalog_from_vpp_content').text = @populate_catalog_from_vpp_content.to_s
199
+ tmpl.add_element('notify_disassociation').text = @notify_disassociation.to_s
200
+ tmpl.add_element('auto_register_managed_users').text = @auto_register_managed_users.to_s
201
+
202
+ add_site_to_xml doc
203
+
204
+ doc.to_s
205
+ end
206
+
207
+ end # VPPAccount
208
+
209
+ end # JSS
@@ -26,9 +26,16 @@
26
26
  ###
27
27
  module JSS
28
28
 
29
- # A mix-in module to handle VPP-related data in API objects that can be
29
+ # A mix-in module to handleVPP-related data in API objects that can be
30
30
  # assigned via VPP.
31
31
  #
32
+ # NOTE: For now we are only working with device-based VPP assignments,
33
+ # which are done via the scope of the VPPable object (macapp, mobdevapp, ebook)
34
+ #
35
+ # User-based APP assignments will require the creation of a VPPAssignment class,
36
+ # and a VPPAssignmentScope class, since those scopes are very limited compared
37
+ # to ordinary scope.
38
+ #
32
39
  # To use this module, merely `include VPPable` when defining your
33
40
  # subclass of JSS::APIObject
34
41
  #
@@ -40,6 +47,50 @@ module JSS
40
47
  #####################################
41
48
  VPPABLE = true
42
49
 
50
+ # Mixed-in Class Methods
51
+ #
52
+ # This is a common technique to get class methods mixed in when
53
+ # you 'include' a module full of instance methods
54
+ #####################################
55
+
56
+ def self.included(klass)
57
+ klass.extend(ClassMethods)
58
+ end
59
+
60
+ # Methods in here will become class methods of the
61
+ # classes that include VPPable
62
+ module ClassMethods
63
+
64
+ # The names and assignment data for all class members that have
65
+ # VPP licenses that can be assigned by device.
66
+ # The assignment data is a hash of three keys pointing to integers:
67
+ # {
68
+ # total: int,
69
+ # used: int,
70
+ # remaining: int
71
+ # }
72
+ #
73
+ # WARNING: This must instantiate all objects, so is slow
74
+ #
75
+ # @return [Hash{String=>Hash}] The names and assignment data
76
+ def all_vpp_device_assignable
77
+ data = {}
78
+ all_ids.each do |id|
79
+ obj = fetch id: id
80
+ next unless obj.vpp_device_based?
81
+
82
+ data[obj.name] = {
83
+ total: obj.vpp_licenses_total,
84
+ used: obj.vpp_licenses_used,
85
+ remaining: obj.vpp_licenses_remaining
86
+ }
87
+ end
88
+ data
89
+ end # all_vpp_device_assignable
90
+
91
+ end # module ClassMethods
92
+
93
+
43
94
  # Mixed-in Attributes
44
95
  #####################################
45
96
 
@@ -48,6 +99,7 @@ module JSS
48
99
 
49
100
  # @return [Integer]
50
101
  attr_reader :vpp_admin_account_id
102
+ alias vpp_account_id vpp_admin_account_id
51
103
 
52
104
  # @return [Boolean]
53
105
  attr_reader :assign_vpp_device_based_licenses
@@ -55,8 +107,7 @@ module JSS
55
107
 
56
108
  # @return [Integer]
57
109
  attr_reader :total_vpp_licenses
58
- alias vpp_total_licenses total_vpp_licenses
59
- alias vpp_license_count total_vpp_licenses
110
+ alias vpp_licenses_total total_vpp_licenses
60
111
 
61
112
  # @return [Integer]
62
113
  attr_reader :remaining_vpp_licenses
@@ -66,22 +117,126 @@ module JSS
66
117
  attr_reader :used_vpp_licenses
67
118
  alias vpp_licenses_used used_vpp_licenses
68
119
 
120
+ #### How to assign VPP content & view assignments
121
+ #
122
+ # When doing device-based assignments, they are made via the
123
+ # Scope of the VPPable Object.
124
+ #
125
+ # There is no indication in the device's API data that an app/book was
126
+ # installed/licensed via VPP, it just shows up in the
127
+ # list of installed apps like any other.
128
+ #
129
+ # When doing user-based assignments, they are made via the (limited)
130
+ # scope of a 'Volume Assignment' object in Users -> Volume Assignement
131
+ # These objects are sort of like policies or config profiles in that they have
132
+ # payloads, and can assign multiple things at once (iosapps, macapps, ebooks)
133
+ # These are available as vppassignment objects in the API.
134
+ #
135
+ # User-based assignments show up in the User's Jamf record
136
+ # Users -> username -> (vpp acct name)
137
+ # There you'll see the names of objects assigned to the user, and the
138
+ # devices on which they've accepted the VPP invitation. In the User's
139
+ # API data, there isaa 'vpp_assignments' arry of hash's like this:
140
+ # [{:id=>13733, :uid=>"258_13733"}]
141
+ # However, that 'id' is not the id of any known vppassignment object, and
142
+ # the uid is... ?? The object model at Developer.jamf.com says those
143
+ # values should be an id and a name, probably pointing to a vppassignment
144
+ # object, but that isn't the case.
145
+ #
146
+ #
147
+ #### Figuring out how many, and where VPP lic. are used....
148
+ #
149
+ # IF dev. based assignement is turned on, then
150
+ # the VPPable object (app, ebook) in the API will show the total numbers
151
+ # of both user and device based assignments:
152
+ #
153
+ # "vpp": {
154
+ # "assign_vpp_device_based_licenses": true,
155
+ # "vpp_admin_account_id": 1,
156
+ # "total_vpp_licenses": 2,
157
+ # "remaining_vpp_licenses": 0,
158
+ # "used_vpp_licenses": 2
159
+ # }
160
+ #
161
+ # However, if assign_vpp_device_based_licenses is false, meaning
162
+ # all assignments are user-based, then no other info is shown in the API.
163
+ #
164
+ # In that case, in the UI, you can see the total assignments in a table
165
+ # in Settings -> Global Mgmt -> Volume Purch -> Content -> (ios/mac)
166
+ # The numbers shown there indicate all assignments, whether user- or
167
+ # deviced-based, just like the numbers in the API data for the VPPable
168
+ # object, if they are there.
169
+ # But there's no equivalent for that table data directly in the API when
170
+ # device-based is false.
171
+ #
172
+ # Also in the UI you can see the intividual computers, mobiledevs, and users
173
+ # to whom an object is assigned, no matter how it was assigned. Go to
174
+ # Users -> Volume Assignments -> [any assigment object] -> Apps/Books -> ios/mac
175
+ # and click on the number in the rightmost 'in use' column, and you'll
176
+ # see a page with 3 tabs, showing the individual computers, mobdevs, or users
177
+ # with the app/ebook assigned. EXCEPT this doesn't seem to expand
178
+ # scoped groups - when I added a static computer group with one computer to
179
+ # the scope of a MacApp, the total in-use count went up from 6 to 7, but the
180
+ # list of computers two which it was assigned still showed only 6. :-(
181
+ #
182
+ # You can also get to the same page via: Users->SeachVolumeContent
183
+ # then perform a simple search, and in the results page, click on the in-use
184
+ # number. If you click on the VolumeAssignments number you'll see a
185
+ # breakdown of the device assignments (from the app itself) and user assignments
186
+ # and their scopes, but the scopes will not expand any groups, just list them.
187
+ #
188
+ # So 2 questions:
189
+ # 1) How to see the total/used/remaining licenses for a VPPable object in the
190
+ # API, regardless of how it's deployed
191
+ #
192
+ # - first look at the VPPable object, and if the data is there, yer done.
193
+ # - If not, then the object is only assigned to users, so we can loop thru
194
+ # the vppassignment objects and count things up.
195
+ #
196
+ # 2) How to learn where the VPPable object is actually assigned - i.e.
197
+ # a list of users and/or devices. Note: this isn't a list of where it's
198
+ # installed, but to whom/where it is assigned.
199
+ #
200
+ # - TLDR: no scopable object in Jamf gives you such a list, so we probably
201
+ # don't need it.
202
+ #
203
+ # In the UI, the page you get when clicking the 'in use' column of various
204
+ # 'volume content' lists (see above) gets you the individually assigned
205
+ # hardware or users, but doesn't show those via groups.
206
+ # In the API - there doesn't seem to be any access at all, other than the
207
+ # scopes of the VPPable Object itself, and any vppassignments that contain it.
208
+ # Scanning through them is probably the only option, but could be slow once
209
+ # there are many - and expanding those scopes into an actual list of users
210
+ # and devices would be a pain to write
211
+ #
212
+
213
+ # Mixed-in Instance Methods
214
+ #####################################
69
215
 
70
216
  # Set whether or not the VPP licenses should be assigned
71
- # by device rather than by user
217
+ # by device as well as (or.. instead of?) by user
72
218
  #
73
219
  # @param new_val[Boolean] The new value
74
220
  #
75
221
  # @return [void]
76
222
  #
77
223
  def assign_vpp_device_based_licenses=(new_val)
78
- return nil if new_val == @assign_vpp_device_based_licenses
79
- raise JSS::InvalidDataError, 'New value must be true or false' unless new_val.jss_boolean?
80
- @assign_vpp_device_based_licenses = new_val
224
+ return if new_val == @assign_vpp_device_based_licenses
225
+
226
+ @assign_vpp_device_based_licenses = JSS::Validate.boolean new_val
81
227
  @need_to_update = true
82
228
  end
83
229
  alias vpp_device_based= assign_vpp_device_based_licenses=
84
230
 
231
+ # @return [String] The name of the vpp admin acct for this object
232
+ #
233
+ def vpp_admin_account_name
234
+ return unless @vpp_admin_account_id.is_a? Integer
235
+
236
+ JSS::VPPAccount.map_all_ids_to(:name)[@vpp_admin_account_id]
237
+ end
238
+ alias vpp_account_name vpp_admin_account_name
239
+
85
240
  # Mixed-in Private Instance Methods
86
241
  #####################################
87
242
  private
@@ -92,11 +247,12 @@ module JSS
92
247
  #
93
248
  def parse_vpp
94
249
  @vpp_codes = @init_data[:vpp_codes]
95
- @vpp_admin_account_id = @init_data[:vpp][:vpp_admin_account_id]
96
- @assign_vpp_device_based_licenses = @init_data[:vpp][:assign_vpp_device_based_licenses]
97
- @total_vpp_licenses = @init_data[:vpp][:total_vpp_licenses]
98
- @remaining_vpp_licenses = @init_data[:vpp][:remaining_vpp_licenses]
99
- @used_vpp_licenses = @init_data[:vpp][:used_vpp_licenses]
250
+ vpp_data = @init_data[:vpp]
251
+ @vpp_admin_account_id = vpp_data[:vpp_admin_account_id]
252
+ @assign_vpp_device_based_licenses = vpp_data[:assign_vpp_device_based_licenses]
253
+ @total_vpp_licenses = vpp_data[:total_vpp_licenses]
254
+ @remaining_vpp_licenses = vpp_data[:remaining_vpp_licenses]
255
+ @used_vpp_licenses = vpp_data[:used_vpp_licenses]
100
256
  end
101
257
 
102
258
  # Insert an appropriate vpp element into the XML for sending changes
@@ -109,7 +265,7 @@ module JSS
109
265
  def add_vpp_xml(xdoc)
110
266
  doc_root = xdoc.root
111
267
  vpp = doc_root.add_element 'vpp'
112
- vpp.add_element('assign_vpp_device_based_licenses').text = @assign_vpp_device_based_licenses
268
+ vpp.add_element('assign_vpp_device_based_licenses').text = @assign_vpp_device_based_licenses.to_s
113
269
  end
114
270
 
115
271
  end # VPPable