class Jamf::Group

This is the parent class of the smart/static group objects in the JSS namely, {ComputerGroup}, {MobileDeviceGroup}, and {UserGroup}

It provides methods for working with the membership of static groups and, by including {Jamf::Criteriable}, the criteria for smart groups.

When changing the criteria of a smart group, use the criteria attribute, which is a {Jamf::Criteria} instance.

Subclasses must define these constants:

@see Jamf::APIObject

@see Jamf::Criteriable

Constants

GROUP_TYPES

the types of groups allowed for creation

ID_XML_TAG

the ‘id’ xml element tag

SITE_SUBSET

Where is the Site data in the API JSON?

Attributes

is_smart[R]

@return [Boolean] is this a smart group

members[R]

@return [Array<Hash>] the group membership

Each hash contains the identifiers for a member of the group, those being:

  • :id, :name, and possibly :udid, :serial_number, :mac_address, :alt_mac_address, and :wifi_mac_address

@see member_ids

@see member_names

notify?[R]

@return [Boolean] does this group send notifications when it changes?

notify_on_change[R]

@return [Boolean] does this group send notifications when it changes?

notify_on_change?[R]

@return [Boolean] does this group send notifications when it changes?

smart?[R]

@return [Boolean] is this a smart group

Public Class Methods

all_smart(refresh = false, api: nil, cnx: Jamf.cnx) click to toggle source

Returns an Array of all the smart groups.

   # File lib/jamf/api/classic/base_classes/group.rb
79 def self.all_smart(refresh = false, api: nil, cnx: Jamf.cnx)
80   cnx = api if api
81 
82   all(refresh, cnx: cnx).select { |g| g[:is_smart] }
83 end
all_static(refresh = false, api: nil, cnx: Jamf.cnx) click to toggle source

Returns an Array of all the static groups.

   # File lib/jamf/api/classic/base_classes/group.rb
88 def self.all_static(refresh = false, api: nil, cnx: Jamf.cnx)
89   cnx = api if api
90 
91   all(refresh, cnx: cnx).reject { |g| g[:is_smart] }
92 end
change_membership(group, add_members: [], remove_members: [], api: nil, cnx: Jamf.cnx) click to toggle source

Immediatly add and/or remove members in a static group without instantiating it first. Uses the <x_additions> and <x_deletions> XML elements available when sending a PUT request to the API.

@param group [String, Integer] The name or id of the group being changed

@param add_members [String, Integer, Array<String, Integer>] valid

identifier(s) for members to add

@param remove_members [String, Integer, Array<String, Integer>] valid

identifier(s) for members to remove

@param cnx [Jamf::Connection] The API connetion to use, uses the default

connection if not specified

@return [void]

    # File lib/jamf/api/classic/base_classes/group.rb
111 def self.change_membership(group, add_members: [], remove_members: [], api: nil, cnx: Jamf.cnx)
112   cnx = api if api
113 
114   raise Jamf::NoSuchItemError, "No #{self} matching '#{group}'" unless (group_id = valid_id group, cnx: cnx)
115   raise Jamf::UnsupportedError, "Not a static group, can't change membership" if map_all(:id, to: :is_smart, cnx: cnx)[group_id]
116 
117   add_members = [add_members].flatten
118   remove_members = [remove_members].flatten
119   return if add_members.empty? && remove_members.empty?
120 
121   # we must know the current group membership, because the API
122   # will raise a conflict error if we try to remove a member
123   # that isn't in the group (which is kinda lame - it should just
124   # ignore this, like it does when we add a member that's already
125   # in the group.)
126   # Its even more lame because we have to instantiate the group
127   # and part of the point of this class method is to avoid that.
128   current_member_ids = fetch(id: group_id, cnx: cnx).member_ids
129 
130   # nil if no changes to be made
131   xml_doc = change_membership_xml add_members, remove_members, current_member_ids
132   return unless xml_doc
133 
134   cnx.c_put "#{self::RSRC_BASE}/id/#{group_id}", xml_doc.to_s
135 end
new(**args) click to toggle source

When creating a new group in the JSS, you must call .make with a :type key and a value of :smart or :static, as well as a :name

@see Jamf::APIObject

Calls superclass method Jamf::APIObject::new
    # File lib/jamf/api/classic/base_classes/group.rb
226 def initialize(**args)
227   raise Jamf::InvalidDataError, 'New group creation must specify a :type of :smart or :static' if args[:id] == :new && !(GROUP_TYPES.include? args[:type])
228 
229   super
230 
231   @is_smart = @init_data[:is_smart] || (args[:type] == :smart)
232 
233   @members =
234     @init_data[self.class::MEMBER_CLASS::RSRC_LIST_KEY] || []
235 end

Private Class Methods

change_membership_xml(add_members, remove_members, current_member_ids) click to toggle source

return [REXML::Document, nil]

    # File lib/jamf/api/classic/base_classes/group.rb
139 def self.change_membership_xml(add_members, remove_members, current_member_ids)
140   # these are nil if there are no changes to make
141   addx = member_additions_xml(add_members, current_member_ids)
142   remx = member_removals_xml(remove_members, current_member_ids)
143   return nil unless addx || remx
144 
145   doc = REXML::Document.new Jamf::Connection::XML_HEADER
146   groupelem = doc.add_element self::RSRC_OBJECT_KEY.to_s
147   groupelem << addx if addx
148   groupelem << remx if remx
149   doc
150 end
member_additions_xml(add_members, current_member_ids) click to toggle source

@return [REXML::Element, nil]

    # File lib/jamf/api/classic/base_classes/group.rb
155 def self.member_additions_xml(add_members, current_member_ids)
156   return nil if add_members.empty?
157 
158   additions = REXML::Element.new self::ADD_MEMBERS_ELEMENT
159   member_added = false
160   add_members.each do |am|
161     am_id = self::MEMBER_CLASS.valid_id am
162     raise Jamf::NoSuchItemError, "No #{self::MEMBER_CLASS} matching '#{am}'" unless am_id
163     next if current_member_ids.include? am_id
164 
165     xam = additions.add_element self::MEMBER_CLASS::RSRC_OBJECT_KEY.to_s
166     xam.add_element(ID_XML_TAG).text = am_id.to_s
167     member_added = true
168   end # each
169 
170   member_added ? additions : nil
171 end
member_removals_xml(remove_members, current_member_ids) click to toggle source

@return [REXML::Element, nil]

    # File lib/jamf/api/classic/base_classes/group.rb
176 def self.member_removals_xml(remove_members, current_member_ids)
177   return nil if remove_members.empty?
178 
179   removals = REXML::Element.new self::REMOVE_MEMBERS_ELEMENT
180   member_removed = false
181   remove_members.each do |rm|
182     rm_id = self::MEMBER_CLASS.valid_id rm
183     next unless rm_id && current_member_ids.include?(rm_id)
184 
185     xrm = removals.add_element self::MEMBER_CLASS::RSRC_OBJECT_KEY.to_s
186     xrm.add_element(ID_XML_TAG).text = rm_id.to_s
187     member_removed = true
188   end # each
189 
190   member_removed ? removals : nil
191 end

Public Instance Methods

add_member(mem) click to toggle source

Add a member, by name or id

@param m the id or name of the member to add

@return [void]

    # File lib/jamf/api/classic/base_classes/group.rb
416 def add_member(mem)
417   raise UnsupportedError, "Smart group members can't be changed." if smart?
418 
419   @members << check_member(mem)
420   @need_to_update = true
421 end
change_membership(add_members: [], remove_members: []) click to toggle source

Immediatly add and/or remove members in this static group

IMPORTANT: This method changes the group in the JSS immediately,

there is no need to call #update/#save

@param add_members [String, Integer, Array<String, Integer>] valid

identifier(s) for members to add

@param remove_members [String, Integer, Array<String, Integer>] valid

identifier(s) for members to remove

@param cnx [Jamf::Connection] The API connetion to use, uses the default

connection if not specified

@return [void]

    # File lib/jamf/api/classic/base_classes/group.rb
473 def change_membership(add_members: [], remove_members: [])
474   self.class.change_membership(@id, add_members: add_members, remove_members: remove_members, cnx: @cnx)
475   refresh_members
476 end
clear() click to toggle source

Remove all members

@return [void]

    # File lib/jamf/api/classic/base_classes/group.rb
449 def clear
450   raise InvalidDataError, "Smart group members can't be changed." if @is_smart
451   return if @members.empty?
452 
453   @members.clear
454   @need_to_update = true
455 end
count()
Alias for: size
create(calculate_members: true, retries: 10) click to toggle source

@see Creatable#create

@param calculate_members [Boolan] should the local membership list be

re-read from the API after the group is created?

@param retries [Integer] If calculate_members is true, refetching the

group to re-read the membership can happen too fast, the JSS won't know
it exists yet and will throw a NoSuchItem error.  If that
happens, try again this many times with a 1 second pause between attempts.
Calls superclass method Jamf::APIObject::create
    # File lib/jamf/api/classic/base_classes/group.rb
250 def create(calculate_members: true, retries: 10)
251   raise Jamf::MissingDataError, 'No criteria specified for smart group' if @is_smart && !@criteria
252 
253   super()
254 
255   if calculate_members
256     tries = 0
257     while tries < retries
258       begin
259         refresh_members
260         break
261       rescue
262         sleep 1
263         tries += 1
264       end # begin
265     end # while
266   end # if calc members
267 
268   @id
269 end
criteria=(new_criteria) click to toggle source

Apply a new set of criteria to a smart group

@param new_criteria the new criteria for the smart group

Calls superclass method Jamf::Criteriable#criteria=
    # File lib/jamf/api/classic/base_classes/group.rb
310 def criteria=(new_criteria)
311   raise InvalidDataError, 'Only smart groups have criteria.' unless @is_smart
312 
313   super
314 end
delete() click to toggle source

@see APIObject#delete

Calls superclass method Jamf::APIObject::delete
    # File lib/jamf/api/classic/base_classes/group.rb
298 def delete
299   super
300   @is_smart = nil
301   @criteria = nil
302   @site = nil
303   @members = []
304 end
is_static()
Alias for: static?
make_smart(**params) click to toggle source

Change static group to smart group

@param args the options and settings use for switching the computer group from static group to smart group

@option args criteria The criteria to be user for the smart group

@return [void]

    # File lib/jamf/api/classic/base_classes/group.rb
323 def make_smart(**params)
324   return if @is_smart
325 
326   params[:criteria] = [] if params[:criteria].nil?
327 
328   criteria = params[:criteria]
329 
330   @is_smart = true
331   @need_to_update = true
332 end
Also aliased as: set_smart
make_static(**params) click to toggle source

Change smart group to static group

@param args the options and settings use for switching the computer group from smart group to static group

@option args preserve_members Should the smart group preserve it’s current members?

@return [void]

    # File lib/jamf/api/classic/base_classes/group.rb
343 def make_static(**params)
344   return unless @is_smart
345 
346   preserve_members = params.include? :preserve_members
347 
348   @is_smart = false
349 
350   clear unless preserve_members
351 end
Also aliased as: set_static
member_ids() click to toggle source

@return [Array<Integer>] the ids of the group members

    # File lib/jamf/api/classic/base_classes/group.rb
378 def member_ids
379   @members.map { |m| m[:id] }
380 end
member_names() click to toggle source

@return [Array<String>] the names of the group members

    # File lib/jamf/api/classic/base_classes/group.rb
372 def member_names
373   @members.map { |m| m[:name] }
374 end
members=(new_members) click to toggle source

Replace all @members with an array of uniq device identfiers (names, ids, serial numbers, etc) E.g: [ ‘lambic’, 1233, ‘2341’, ‘monkey’]

They must all be in the JSS or an error is raised before doing anything. See {#check_member}

@param new_members the new group members

@return [void]

    # File lib/jamf/api/classic/base_classes/group.rb
392 def members=(new_members)
393   raise UnsupportedError, "Smart group members can't be changed." if @is_smart
394   raise InvalidDataError, 'Arg must be an array of names and/or ids' unless new_members.is_a? Array
395 
396   ok_members = []
397   new_members.each do |m|
398     ok_members << check_member(m)
399   end
400 
401   ok_members.uniq!
402 
403   # make sure we've actually changed...
404   return if members.map { |m| m[:id] }.sort == ok_members.map { |m| m[:id] }.sort
405 
406   @members = ok_members
407   @need_to_update = true
408 end
refresh_members() click to toggle source

Refresh the membership from the API

@return [Array<Hash>] the refresh membership

    # File lib/jamf/api/classic/base_classes/group.rb
482 def refresh_members
483   @members = @cnx.c_get(@rest_rsrc)[self.class::RSRC_OBJECT_KEY][self.class::MEMBER_CLASS::RSRC_LIST_KEY]
484 end
remove_member(mem) click to toggle source

Remove a member by id, or name

@param m an identifier for the item to remove

@return [void]

    # File lib/jamf/api/classic/base_classes/group.rb
429 def remove_member(mem)
430   raise UnsupportedError, "Smart group members can't be changed." if smart?
431 
432   # See if we have the identifier in the @members hash
433   id_to_remove = @members.select { |mm| mm.values.include? mem }.first&.dig :id
434   # But the members hash might not have SN, macaddr, etc, and never has udid, so
435   # look at the MEMBER_CLASS if needed
436   id_to_remove ||= self.class::MEMBER_CLASS.valid_id mem
437 
438   # nothing to do if that id isn't one of our members
439   return unless id_to_remove && member_ids.include?(id_to_remove)
440 
441   @members.delete_if { |k, v| k == :id && v == id_to_remove }
442   @need_to_update = true
443 end
save(**params) click to toggle source

Wrapper/alias for both create and update

    # File lib/jamf/api/classic/base_classes/group.rb
280 def save(**params)
281   params[:calculate_members] = true if params[:calculate_members].nil?
282   params[:retries] = 10 if params[:retries].nil?
283   params[:refresh] = true if params[:refresh].nil?
284 
285   if @in_jss
286     raise Jamf::UnsupportedError, 'Updating this object in the JSS is currently not supported by ruby-jss' unless updatable?
287 
288     update refresh: params[:refresh]
289   else
290     raise Jamf::UnsupportedError, 'Creating this object in the JSS is currently not supported by ruby-jss' unless creatable?
291 
292     create calculate_members: params[:calculate_members], retries: params[:retries]
293   end
294 end
set_smart(**params)

backward compatility

Alias for: make_smart
set_static(**params)

backward compatility

Alias for: make_static
size() click to toggle source

How many members of the group?

@return [Integer] the number of members of the group

    # File lib/jamf/api/classic/base_classes/group.rb
359 def size
360   @members.count
361 end
Also aliased as: count
static?() click to toggle source

@return [Boolean] Is this a static group?

    # File lib/jamf/api/classic/base_classes/group.rb
365 def static?
366   !smart?
367 end
Also aliased as: is_static
update(refresh: true) click to toggle source

@see Updatable#update

Calls superclass method Jamf::APIObject#update
    # File lib/jamf/api/classic/base_classes/group.rb
273 def update(refresh: true)
274   super()
275   refresh_members if refresh
276   @id
277 end

Private Instance Methods

check_member(m) click to toggle source

Check that a potential group member is valid in the JSS. Arg must be an id or name. An exception is raised if the device doesn’t exist.

@return [Hash{:id=>Integer,:name=>String}] the valid id and name

    # File lib/jamf/api/classic/base_classes/group.rb
497 def check_member(m)
498   desired_id = self.class::MEMBER_CLASS.valid_id m, cnx: @cnx
499   raise Jamf::NoSuchItemError, "No #{self.class::MEMBER_CLASS::RSRC_OBJECT_KEY} matching '#{m}' in the JSS." unless desired_id
500 
501   desired_name = self.class::MEMBER_CLASS.map_all(:id, to: :name, cnx: @cnx)[desired_id]
502 
503   { name: desired_name, id: desired_id }
504 end
rest_xml() click to toggle source

the xml formated data for adding or updating this in the JSS,

    # File lib/jamf/api/classic/base_classes/group.rb
508 def rest_xml
509   doc = REXML::Document.new Jamf::Connection::XML_HEADER
510   group = doc.add_element self.class::RSRC_OBJECT_KEY.to_s
511   group.add_element('name').text = @name
512   group.add_element('is_smart').text = @is_smart
513   if @is_smart
514     group << @criteria.rest_xml if @criteria
515   else
516     group << self.class::MEMBER_CLASS.xml_list(@members, :id)
517   end
518 
519   add_site_to_xml(doc)
520 
521   doc.to_s
522 end