ruby-jss 1.1.3 → 1.2.0b1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ruby-jss might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGES.md +51 -0
- data/lib/jss/api_connection.rb +33 -11
- data/lib/jss/api_object.rb +85 -68
- data/lib/jss/api_object/advanced_search.rb +12 -0
- data/lib/jss/api_object/computer.rb +2 -2
- data/lib/jss/api_object/creatable.rb +9 -3
- data/lib/jss/api_object/extension_attribute.rb +3 -1
- data/lib/jss/api_object/group.rb +47 -8
- data/lib/jss/api_object/ibeacon.rb +198 -0
- data/lib/jss/api_object/mdm.rb +4 -1
- data/lib/jss/api_object/mobile_device.rb +2 -2
- data/lib/jss/api_object/patch_policy.rb +15 -0
- data/lib/jss/api_object/updatable.rb +6 -0
- data/lib/jss/validate.rb +55 -3
- data/lib/jss/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2aa99b8b7dcfc161d2251f1af4b6efa5d302a575bcadc5feb843536252acf98b
|
4
|
+
data.tar.gz: cb691744d29c3d9ff178cd56d33dcab35f7a5a00ddcdc831b6ade3f33209496e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fc08c5a37bd84d8077b15827357918e2d1b901b85f5341099540941cff8001ffcbeb0242a2e133e1909efd0fc14b6910e92b8f5d03660344129dd1404485f24
|
7
|
+
data.tar.gz: 6548b0c75545dda10280d8200cb2496799dcf015304c2e5d32da3db34591f2ee02e53c7e6bb15f6138628744ee16cb87760f8215ced4f19a7325a07683dfe505
|
data/CHANGES.md
CHANGED
@@ -4,43 +4,94 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## \[1.2.0] - Unreleased
|
8
|
+
### Added
|
9
|
+
- APIConnection#flushcache can be used to flush all cached data, or just for specific APIObject lists or ExtensionAttribute definitions. This is now used more often throughout ruby-jss.
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
- Group.all_static was returning all_smart, now actually returns all_static
|
13
|
+
|
14
|
+
- Fix in error message raised in Group.change_membership
|
15
|
+
|
16
|
+
- APIConnection#connect now flushes all cached data, so if you use it to change which server is being used by an existing connection, you don't keep the cached data from the old server.
|
17
|
+
|
18
|
+
- PatchPolicy now overrides APIObject.fetch to still allow for fetching by name, even there's no patchpolicies/name/... endpoint
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
- Group#create with `calculate_members: true`, will sometimes try to re-read the new group before the JSS knows it exists, causing a `404 Not Found` error. This is especially common when using a clustered environment behind a load-balanced hostname (like _something.jamfcloud.com_).
|
22
|
+
Now, if a 404 happens when trying to refresh the membership using calculate_members, ruby-jss will retry every second for up to 10 seconds.
|
23
|
+
To change the number of retries, provide an integer with the `retries:` parameter. If you don't need to know the group members after creation, pass `calculate_members: false` when calling Group#create or Group#save
|
24
|
+
|
25
|
+
- Creating, Updating, or Deleting objects of the APIObject subclasses now flushes the cached `all` lists for that class, so subseqent uses of the `all` lists will refresh the data from the API. This means that `APIObject.valid_id` will work immediately upon object creation. NOTE: However, when using a clustered environment behind a load-balanced hostname (like jamfcloud.com), it may take some time for the `all` list to update on all nodes of the cluster, so you might still need to pause up to the number of seconds defined of the cluster's sync interval to ensure valid lists.
|
26
|
+
|
27
|
+
- Case-insentive lookup & validation methods in APIObject now use `String#casecmp?` for much simpler code
|
28
|
+
|
29
|
+
- Creatable#create no longer takes an `api:` parameter (it never should have) The API connection given in #make is always used for creating the object in the API.
|
30
|
+
|
31
|
+
- Added class JSS::IBeacon, implementing the .../ibeacons/... endpoints
|
32
|
+
|
7
33
|
## \[1.1.3] - 2019-09-23
|
8
34
|
### Added
|
9
35
|
- MobileDeviceExtensionAttribute now has a `.history` class method matching that of ComputerExtensionAttribute. Requires direct MySQL database access. Thanks @aurica!
|
36
|
+
|
10
37
|
- JSS::AmbiguousError exception class
|
38
|
+
|
11
39
|
- More caching of API data to improve general speed
|
12
40
|
- The hashes created by `APIObject.map_all_ids_to(blah)`
|
13
41
|
- ExtensionAttribute definitions when used by extendable classes
|
42
|
+
|
14
43
|
- APIObject.fetch can take the search term `:random` and you'll get a randomly selected object. Example: `a_random_computer = JSS::Computer.fetch :random`
|
44
|
+
|
15
45
|
- Keys of the hash returned by `Computer#hardware` are now available as instance methods on Computer objects. So as well as `a_computer.hardware[:total_ram]` you can also do `a_computer.total_ram`
|
46
|
+
|
16
47
|
- Policy now recognizes the frequency Symbol `:once_per_user_per_computer`
|
48
|
+
|
17
49
|
- Attribute reader :management_status added to Computer class
|
50
|
+
|
18
51
|
- Implemented some useful String methods from newer versions of Ruby into older Rubies: `casecmp?`, `delete_prefix`, & `delete_suffix`
|
52
|
+
|
19
53
|
- master_distribution_point class method in APIConnection & DistribtutionPoint now raise an error when no dist. point is 'master'
|
20
54
|
- The error states that the cloud dist. point may be the master, and there's no classic API access to it.
|
21
55
|
|
22
56
|
|
23
57
|
### Fixed
|
24
58
|
- Can't Modify Frozen Hash error when instantiating JSS::Scopbable::Scope. Thanks to @shahn for reporting this one.
|
59
|
+
|
25
60
|
- MobileDeviceExtensionAttribute now handles empty `as_of` timestamp. Thanks @aurica!
|
61
|
+
|
26
62
|
- A few typos. Thanks to @cybertunnel for finding some.
|
63
|
+
|
27
64
|
- A bug when parsing the `server_path` parameter to `API::Connection.new`
|
65
|
+
|
28
66
|
- Bugs in handling blank values in Policy#search_by_path and Policy#printer_ids. Thanks @cybertunnel
|
67
|
+
|
29
68
|
- Computer.management_data with a specified subset returned one level too high in the data structure
|
69
|
+
|
30
70
|
- NetworkSegment.my_network_segment: error in number of params passed to other methods
|
71
|
+
|
31
72
|
- Script#name= now works again, no longer uses a constant from an ancient version. Thanks @shahn
|
73
|
+
|
32
74
|
- Computer#asset_tag= now accepts nil to erase the value
|
75
|
+
|
33
76
|
- APIConnection.my_distribution_point & DistributionPoint.my_distribution_point now return the master_distribution_point object if there isn't one assigned to the current network segment.
|
77
|
+
|
34
78
|
- RestClient no longer warns about calling 'to_i' on Responses when calling APIConnection#put_rsrc & #post_rsrc
|
35
79
|
|
36
80
|
### Changed
|
37
81
|
- Monkey Patches are being moved to a better, more traceable technique, see https://www.justinweiss.com/articles/3-ways-to-monkey-patch-without-making-a-mess/
|
82
|
+
|
38
83
|
- MobileDevices and Computers now raise JSS::AmbiguousError when being fetched by explicitly by name, e.g. `JSS::Computer.fetch name: 'foo'` and that name is not unique in the JSS. Previously, you'd get an object back, but no guarantee as to which one it was. You'll still get an undefined object if you use a bare searchterm, e.g. `JSS::Computer.fetch 'foo'`
|
84
|
+
|
39
85
|
- Documentation for subclassing APIObject is updated & expanded. See the comments above the class definition in api_object.rb
|
86
|
+
|
40
87
|
- `APIObject.valid_id` is now case-insensitive
|
88
|
+
|
41
89
|
- Removed deprecated VALID_DATA_KEYS constants from APIObject subclasses
|
90
|
+
|
42
91
|
- Various changes in APIObject and its subclasses to try making `.fetch` and other lookup-methods faster.
|
92
|
+
|
43
93
|
- All of the NetworkSegment-related methods in APIConnection have been moved back to NetworkSegment. The methods in APIConnection still work, but are marked deprecated and will go away eventually.
|
94
|
+
|
44
95
|
- Removed last call to deprecated `URI.encode`, replaced with `CGI.escape`
|
45
96
|
|
46
97
|
|
data/lib/jss/api_connection.rb
CHANGED
@@ -324,6 +324,9 @@ module JSS
|
|
324
324
|
|
325
325
|
RSRC_NOT_FOUND_MSG = 'The requested resource was not found'.freeze
|
326
326
|
|
327
|
+
# These classes are extendable, and may need cache flushing for EA definitions
|
328
|
+
EXTENDABLE_CLASSES = [JSS::Computer, JSS::MobileDevice, JSS::User].freeze
|
329
|
+
|
327
330
|
# Attributes
|
328
331
|
#####################################
|
329
332
|
|
@@ -420,10 +423,8 @@ module JSS
|
|
420
423
|
#
|
421
424
|
def initialize(args = {})
|
422
425
|
@name = args.delete :name
|
423
|
-
@name ||= :
|
426
|
+
@name ||= :unknown
|
424
427
|
@connected = false
|
425
|
-
@object_list_cache = {}
|
426
|
-
@ext_attr_definition_cache = {}
|
427
428
|
connect args unless args.empty?
|
428
429
|
end # init
|
429
430
|
|
@@ -467,6 +468,9 @@ module JSS
|
|
467
468
|
# @return [true]
|
468
469
|
#
|
469
470
|
def connect(args = {})
|
471
|
+
# new connections always get new caches
|
472
|
+
flushcache
|
473
|
+
|
470
474
|
args[:no_port_specified] = args[:port].to_s.empty?
|
471
475
|
args = apply_connection_defaults args
|
472
476
|
|
@@ -966,15 +970,33 @@ module JSS
|
|
966
970
|
# Empty all cached lists from this connection
|
967
971
|
# then run garbage collection to clear any available memory
|
968
972
|
#
|
973
|
+
# If an APIObject Subclass's RSRC_LIST_KEY is specified, only the caches
|
974
|
+
# for that class are flushed (e.g. :computers, :comptuer_groups)
|
975
|
+
#
|
969
976
|
# NOTE if you've referenced objects in these caches, those objects
|
970
977
|
# won't be removed from memory, but all cached data will be recached
|
971
978
|
# as needed.
|
972
979
|
#
|
980
|
+
# @param key[Symbol, Class] Flush only the caches for the given RSRC_LIST_KEY. or
|
981
|
+
# the EAdef cache for the given extendable class. If nil (the default)
|
982
|
+
# flushes all caches
|
983
|
+
#
|
973
984
|
# @return [void]
|
974
985
|
#
|
975
|
-
def flushcache
|
976
|
-
|
977
|
-
|
986
|
+
def flushcache(key = nil)
|
987
|
+
if EXTENDABLE_CLASSES.include? key
|
988
|
+
@ext_attr_definition_cache[key] = {}
|
989
|
+
elsif key
|
990
|
+
map_key_pfx = "#{key}_map_"
|
991
|
+
@object_list_cache.delete_if do |cache_key, _cache|
|
992
|
+
cache_key == key || cache_key.to_s.start_with?(map_key_pfx)
|
993
|
+
end
|
994
|
+
@ext_attr_definition_cache
|
995
|
+
else
|
996
|
+
@object_list_cache = {}
|
997
|
+
@ext_attr_definition_cache = {}
|
998
|
+
end
|
999
|
+
|
978
1000
|
GC.start
|
979
1001
|
end
|
980
1002
|
|
@@ -1001,7 +1023,7 @@ module JSS
|
|
1001
1023
|
|
1002
1024
|
# raise exception if not connected
|
1003
1025
|
def validate_connected
|
1004
|
-
raise JSS::InvalidConnectionError, 'Not Connected. Use .connect first.
|
1026
|
+
raise JSS::InvalidConnectionError, "Connection '#{@name}' Not Connected. Use .connect first." unless connected?
|
1005
1027
|
end
|
1006
1028
|
|
1007
1029
|
# Apply defaults from the JSS::CONFIG,
|
@@ -1258,8 +1280,8 @@ module JSS
|
|
1258
1280
|
# @return [APIConnection] the new, active connection
|
1259
1281
|
#
|
1260
1282
|
def self.new_api_connection(args = {})
|
1283
|
+
args[:name] ||= :default
|
1261
1284
|
@api = APIConnection.new args
|
1262
|
-
@api
|
1263
1285
|
end
|
1264
1286
|
|
1265
1287
|
# Switch the connection used for all API interactions to the
|
@@ -1282,7 +1304,7 @@ module JSS
|
|
1282
1304
|
# @return [void]
|
1283
1305
|
#
|
1284
1306
|
def self.use_default_connection
|
1285
|
-
use_api_connection
|
1307
|
+
use_api_connection @api
|
1286
1308
|
end
|
1287
1309
|
|
1288
1310
|
# The currently active JSS::APIConnection instance.
|
@@ -1290,7 +1312,7 @@ module JSS
|
|
1290
1312
|
# @return [JSS::APIConnection]
|
1291
1313
|
#
|
1292
1314
|
def self.api
|
1293
|
-
@api
|
1315
|
+
@api ||= APIConnection.new name: :default
|
1294
1316
|
end
|
1295
1317
|
|
1296
1318
|
# aliases of module methods
|
@@ -1310,7 +1332,7 @@ module JSS
|
|
1310
1332
|
end
|
1311
1333
|
|
1312
1334
|
# create the default connection
|
1313
|
-
new_api_connection
|
1335
|
+
new_api_connection unless @api
|
1314
1336
|
|
1315
1337
|
# Save the default connection in the API constant,
|
1316
1338
|
# mostly for backward compatibility.
|
data/lib/jss/api_object.rb
CHANGED
@@ -345,7 +345,7 @@ module JSS
|
|
345
345
|
no_aliases ? @lookup_keys.values.uniq : @lookup_keys
|
346
346
|
end
|
347
347
|
|
348
|
-
# Given a lookup
|
348
|
+
# Given a lookup key, or an alias of one, return the matching fetch_rsrc_key
|
349
349
|
# for building a fetch/GET resource URL, or nil if no fetch_rsrc_key is defined.
|
350
350
|
#
|
351
351
|
# See {OTHER_LOOKUP_KEYS} in the APIObject class comments/docs above for details.
|
@@ -452,7 +452,7 @@ module JSS
|
|
452
452
|
|
453
453
|
cache = api.object_list_cache
|
454
454
|
cache_key = self::RSRC_LIST_KEY
|
455
|
-
|
455
|
+
api.flushcache(cache_key) if refresh
|
456
456
|
return cache[cache_key] if cache[cache_key]
|
457
457
|
|
458
458
|
cache[cache_key] = api.get_rsrc(self::RSRC_BASE)[cache_key]
|
@@ -582,6 +582,7 @@ module JSS
|
|
582
582
|
# @return [Integer, nil] the id of the matching object, or nil if it doesn't exist
|
583
583
|
#
|
584
584
|
def self.valid_id(identifier, refresh = false, api: JSS.api)
|
585
|
+
|
585
586
|
# refresh if needed
|
586
587
|
all(refresh, api: api) if refresh
|
587
588
|
|
@@ -591,26 +592,11 @@ module JSS
|
|
591
592
|
keys_to_check = lookup_keys(no_aliases: true)
|
592
593
|
keys_to_check.delete :id # we've already checked :id
|
593
594
|
|
594
|
-
# downcase for speedy case-insensitivity -
|
595
|
-
# include?, and I assume value?, is faster with downcasing. See
|
596
|
-
# https://stackoverflow.com/questions/9333952/case-insensitive-arrayinclude/9334066#9334066
|
597
|
-
identifier.downcase! if identifier.is_a? String
|
598
|
-
|
599
595
|
keys_to_check.each do |key|
|
600
596
|
mapped_ids = map_all_ids_to key, api: api
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
# if name is not unique, skip to the next key, there is no
|
605
|
-
# valid id for a non-unique name
|
606
|
-
if key == :name
|
607
|
-
num_name_matches = mapped_ids.values.select { |n| n == identifier }.size
|
608
|
-
next unless num_name_matches == 1
|
609
|
-
else
|
610
|
-
next unless mapped_ids.value? identifier
|
611
|
-
end
|
612
|
-
|
613
|
-
return mapped_ids.invert[identifier]
|
597
|
+
matches = mapped_ids.select { |_id, ident| ident.casecmp? identifier }
|
598
|
+
# If exactly one match, return the id
|
599
|
+
return matches.keys.first if matches.size == 1
|
614
600
|
end
|
615
601
|
|
616
602
|
nil
|
@@ -627,8 +613,10 @@ module JSS
|
|
627
613
|
#
|
628
614
|
# # => the Integer id, or nil if no such serial number
|
629
615
|
#
|
630
|
-
# Raises a JSS::Ambiguous error if
|
631
|
-
#
|
616
|
+
# Raises a JSS::Ambiguous error if there's more than one matching value
|
617
|
+
# for any key, which might be true of names for Computers and Devices
|
618
|
+
#
|
619
|
+
# This is similar to .valid_id, except only one key is searched
|
632
620
|
#
|
633
621
|
# @param key [Symbol] they key in which to look for the identifier. Must be
|
634
622
|
# a valid lookup key for this subclass.
|
@@ -645,24 +633,23 @@ module JSS
|
|
645
633
|
# @return [Integer, nil] the id of the matching object, or nil if it
|
646
634
|
# doesn't exist
|
647
635
|
#
|
648
|
-
def self.id_for_identifier(key,
|
636
|
+
def self.id_for_identifier(key, val, refresh = false, api: JSS.api)
|
649
637
|
# refresh if needed
|
650
638
|
all(refresh, api: api) if refresh
|
651
639
|
|
652
640
|
# get the real key if an alias was used
|
653
641
|
key = real_lookup_key key
|
654
642
|
|
655
|
-
|
656
|
-
|
657
|
-
validate_unique_name(ident) if key == :name
|
643
|
+
# do id's expicitly, they are integers
|
644
|
+
return all_ids.include?(val) ? val : nil if key == :id
|
658
645
|
|
659
|
-
# downcase for speed
|
660
|
-
ident.downcase! if ident.is_a? String
|
661
646
|
mapped_ids = map_all_ids_to key, api: api
|
662
|
-
mapped_ids.
|
663
|
-
|
647
|
+
matches = mapped_ids.select { |_id, map_val| val.casecmp? map_val }
|
648
|
+
raise JSS::AmbiguousError, "Key #{key}: value '#{val}' is not unique for #{self}" if matches.size > 1
|
664
649
|
|
665
|
-
|
650
|
+
return nil if matches.size.zero?
|
651
|
+
|
652
|
+
matches.keys.first
|
666
653
|
end
|
667
654
|
|
668
655
|
# Return true or false if an object of this subclass
|
@@ -814,52 +801,70 @@ module JSS
|
|
814
801
|
api ||= JSS.api
|
815
802
|
|
816
803
|
# refresh the .all list if needed
|
817
|
-
|
804
|
+
if args.delete(:refresh) || searchterm == :random
|
805
|
+
all(:refresh, api: api)
|
806
|
+
just_refreshed = true
|
807
|
+
else
|
808
|
+
just_refreshed = false
|
809
|
+
end
|
818
810
|
|
819
811
|
# a random object?
|
820
|
-
if searchterm == :random
|
821
|
-
return new id: all_ids.sample, api: api
|
822
|
-
end
|
812
|
+
return new id: all.sample[:id], api: api if searchterm == :random
|
823
813
|
|
824
814
|
# get the lookup key and value, if given
|
825
815
|
fetch_key, fetch_val = args.to_a.first
|
816
|
+
fetch_rsrc_key = fetch_rsrc_key(fetch_key)
|
826
817
|
|
827
|
-
|
828
|
-
validate_unique_name(fetch_val) if fetch_key == :name
|
818
|
+
err_detail = "where #{fetch_key} = #{fetch_val}"
|
829
819
|
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
820
|
+
# names should raise an error if more than one exists,
|
821
|
+
# so we always have to do id_for_identifier, which will do so.
|
822
|
+
if fetch_rsrc_key == :name
|
823
|
+
id = id_for_identifier fetch_key, fetch_val, !just_refreshed, api: api
|
824
|
+
fetch_rsrc = id ? "#{self::RSRC_BASE}/name/#{CGI.escape fetch_val.to_s}" : nil
|
825
|
+
|
826
|
+
# if the fetch rsrc key exists, it can be used directly in an endpoint path
|
827
|
+
# so, use it directly, rather than looking up the id first.
|
828
|
+
elsif fetch_rsrc_key
|
829
|
+
fetch_rsrc = "#{self::RSRC_BASE}/#{fetch_rsrc_key}/#{CGI.escape fetch_val.to_s}"
|
834
830
|
|
835
|
-
#
|
836
|
-
# the
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
831
|
+
# it has an OTHER_LOOKUP_KEY but that key doesn't have a fetch_rsrc
|
832
|
+
# so we look in the .map_all_ids_to_* hash for it.
|
833
|
+
elsif fetch_key
|
834
|
+
id = id_for_identifier fetch_key, fetch_val, !just_refreshed, api: api
|
835
|
+
fetch_rsrc = id ? "#{self::RSRC_BASE}/id/#{id}" : nil
|
836
|
+
|
837
|
+
# no fetch key was given in the args, so try a search term
|
842
838
|
elsif searchterm
|
843
839
|
id = valid_id searchterm, api: api
|
840
|
+
fetch_rsrc = id ? "#{self::RSRC_BASE}/id/#{id}" : nil
|
844
841
|
err_detail = "matching #{searchterm}"
|
842
|
+
|
845
843
|
else
|
846
844
|
raise ArgumentError, 'Missing searchterm or fetch key'
|
847
845
|
end
|
848
|
-
raise JSS::NoSuchItemError, "No #{self::RSRC_OBJECT_KEY} found #{err_detail}" unless id
|
849
846
|
|
850
|
-
|
847
|
+
begin
|
848
|
+
return new fetch_rsrc: fetch_rsrc, api: api
|
849
|
+
rescue RestClient::NotFound
|
850
|
+
raise JSS::NoSuchItemError, "No #{self::RSRC_OBJECT_KEY} found #{err_detail}" unless fetch_rsrc
|
851
|
+
end
|
851
852
|
end # fetch
|
852
853
|
|
853
854
|
# Make a ruby instance of a not-yet-existing APIObject.
|
854
855
|
#
|
855
|
-
# This is
|
856
|
-
#
|
857
|
-
# and helps avoid the confusion of using ruby's .new class method for making
|
858
|
-
# ruby instances.
|
856
|
+
# This is how to create new objects in the JSS. A name: must be provided,
|
857
|
+
# and different subclasses can take other named parameters.
|
859
858
|
#
|
860
859
|
# For retrieving existing objects in the JSS, use {APIObject.fetch}
|
861
860
|
#
|
862
|
-
#
|
861
|
+
# After calling this you'll have a local instance, which will be created
|
862
|
+
# in the JSS when you call #create on it. see {APIObject#create}
|
863
|
+
#
|
864
|
+
# @param name[String] The name of this object, generally must be uniqie
|
865
|
+
#
|
866
|
+
# @param api[JSS::APIConnection] the connection thru which to make this
|
867
|
+
# object. Defaults to the deault API connection in JSS.api
|
863
868
|
#
|
864
869
|
# @param args[Hash] The data for creating an object, such as name:
|
865
870
|
# See {APIObject#initialize}
|
@@ -868,6 +873,9 @@ module JSS
|
|
868
873
|
#
|
869
874
|
def self.make(**args)
|
870
875
|
validate_not_metaclass(self)
|
876
|
+
unless constants.include?(:CREATABLE)
|
877
|
+
raise JSS::UnsupportedError, "Creating #{self.class::RSRC_LIST_KEY} isn't yet supported. Please use other Casper workflows."
|
878
|
+
end
|
871
879
|
raise ArgumentError, "Use '#{self.class}.fetch id: xx' to retrieve existing JSS objects" if args[:id]
|
872
880
|
|
873
881
|
args[:api] ||= JSS.api
|
@@ -921,9 +929,14 @@ module JSS
|
|
921
929
|
api.delete_rsrc "#{self::RSRC_BASE}/id/#{vid}"
|
922
930
|
else
|
923
931
|
skipped << vid
|
924
|
-
end # if current_ids include
|
932
|
+
end # if current_ids include vid
|
925
933
|
end # each victim
|
926
934
|
|
935
|
+
# clear any cached all-lists or id-maps for this class
|
936
|
+
# so they'll re-cache as needed
|
937
|
+
api.flushcache self::RSRC_LIST_KEY
|
938
|
+
# all :refresh, api: api
|
939
|
+
|
927
940
|
skipped
|
928
941
|
end # self.delete
|
929
942
|
|
@@ -932,17 +945,6 @@ module JSS
|
|
932
945
|
raise JSS::UnsupportedError, 'JSS::APIObject is a metaclass. Do not use it directly' if klass == JSS::APIObject
|
933
946
|
end
|
934
947
|
|
935
|
-
# Raise an exception if a name is being used for fetching and it isn't
|
936
|
-
# unique. Case Insensitive
|
937
|
-
def self.validate_unique_name(name, refresh = false)
|
938
|
-
return unless defined? self::NON_UNIQUE_NAMES
|
939
|
-
|
940
|
-
name.downcase!
|
941
|
-
matches = all_names(refresh).map(&:downcase).select { |n| n == name }
|
942
|
-
raise JSS::AmbiguousError, "Name '#{name}' is not unique for #{self}" if matches.size > 1
|
943
|
-
end
|
944
|
-
|
945
|
-
|
946
948
|
# Attributes
|
947
949
|
#####################################
|
948
950
|
|
@@ -1024,9 +1026,11 @@ module JSS
|
|
1024
1026
|
def save
|
1025
1027
|
if @in_jss
|
1026
1028
|
raise JSS::UnsupportedError, 'Updating this object in the JSS is currently not supported by ruby-jss' unless updatable?
|
1029
|
+
|
1027
1030
|
update
|
1028
1031
|
else
|
1029
1032
|
raise JSS::UnsupportedError, 'Creating this object in the JSS is currently not supported by ruby-jss' unless creatable?
|
1033
|
+
|
1030
1034
|
create
|
1031
1035
|
end
|
1032
1036
|
end
|
@@ -1120,6 +1124,12 @@ module JSS
|
|
1120
1124
|
@id = nil
|
1121
1125
|
@in_jss = false
|
1122
1126
|
@need_to_update = false
|
1127
|
+
|
1128
|
+
# clear any cached all-lists or id-maps for this class
|
1129
|
+
# so they'll re-cache as needed
|
1130
|
+
@api.flushcache self.class::RSRC_LIST_KEY
|
1131
|
+
# self.class.all :refresh, api: @api
|
1132
|
+
|
1123
1133
|
:deleted
|
1124
1134
|
end # delete
|
1125
1135
|
|
@@ -1275,7 +1285,12 @@ module JSS
|
|
1275
1285
|
|
1276
1286
|
raise JSS::MissingDataError, "You must provide a :name to create a #{self.class::RSRC_OBJECT_KEY}." unless args[:name]
|
1277
1287
|
|
1278
|
-
|
1288
|
+
return if defined? self.class::NON_UNIQUE_NAMES
|
1289
|
+
|
1290
|
+
matches = self.class.all_names(:refresh, api: @api).select { |n| n.casecmp? args[:name] }
|
1291
|
+
|
1292
|
+
raise JSS::AlreadyExistsError, "A #{self.class::RSRC_OBJECT_KEY} already exists with the name '#{args[:name]}'" unless matches.empty?
|
1293
|
+
|
1279
1294
|
end
|
1280
1295
|
|
1281
1296
|
# Given initialization args, perform an API lookup for an object.
|
@@ -1462,6 +1477,7 @@ module JSS
|
|
1462
1477
|
end
|
1463
1478
|
|
1464
1479
|
# Meta Programming
|
1480
|
+
####################################################
|
1465
1481
|
|
1466
1482
|
# Loop through the defined lookup keys and make
|
1467
1483
|
# .all_<key>s methods for each one, with
|
@@ -1537,6 +1553,7 @@ require 'jss/api_object/computer_invitation'
|
|
1537
1553
|
require 'jss/api_object/department'
|
1538
1554
|
require 'jss/api_object/distribution_point'
|
1539
1555
|
require 'jss/api_object/ebook'
|
1556
|
+
require 'jss/api_object/ibeacon'
|
1540
1557
|
require 'jss/api_object/ldap_server'
|
1541
1558
|
require 'jss/api_object/mac_application'
|
1542
1559
|
require 'jss/api_object/mobile_device'
|
@@ -198,6 +198,18 @@ module JSS
|
|
198
198
|
@id # remember to return the id
|
199
199
|
end
|
200
200
|
|
201
|
+
# Wrapper/alias for both create and update
|
202
|
+
def save(get_results = false)
|
203
|
+
if @in_jss
|
204
|
+
raise JSS::UnsupportedError, 'Updating this object in the JSS is currently not supported by ruby-jss' unless updatable?
|
205
|
+
update get_results
|
206
|
+
else
|
207
|
+
raise JSS::UnsupportedError, 'Creating this object in the JSS is currently not supported by ruby-jss' unless creatable?
|
208
|
+
create get_results
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
|
201
213
|
# Requery the API for the search results.
|
202
214
|
#
|
203
215
|
# This can be very slow, so temporarily reset the API timeout to 30 minutes
|
@@ -1076,13 +1076,13 @@ module JSS
|
|
1076
1076
|
|
1077
1077
|
def serial_number=(new_val)
|
1078
1078
|
return nil if new_val == @serial_number
|
1079
|
-
@serial_number = new_val.empty? ? new_val : JSS::Validate.
|
1079
|
+
@serial_number = new_val.empty? ? new_val : JSS::Validate.doesnt_already_exist(JSS::Computer, :serial_number, new_val, api: api)
|
1080
1080
|
@need_to_update = true
|
1081
1081
|
end
|
1082
1082
|
|
1083
1083
|
def udid=(new_val)
|
1084
1084
|
return nil if new_val == @udid
|
1085
|
-
@udid = new_val.empty? ? new_val : JSS::Validate.
|
1085
|
+
@udid = new_val.empty? ? new_val : JSS::Validate.doesnt_already_exist(JSS::Computer, :udid, new_val, api: api)
|
1086
1086
|
@need_to_update = true
|
1087
1087
|
end
|
1088
1088
|
|
@@ -71,15 +71,21 @@ module JSS
|
|
71
71
|
#
|
72
72
|
# @return [Integer] the jss ID of the newly created object
|
73
73
|
#
|
74
|
-
def create
|
75
|
-
api ||= @api
|
74
|
+
def create
|
76
75
|
raise JSS::UnsupportedError, "Creating or editing #{self.class::RSRC_LIST_KEY} isn't yet supported. Please use other Casper workflows." unless creatable?
|
76
|
+
|
77
77
|
raise AlreadyExistsError, "This #{self.class::RSRC_OBJECT_KEY} already exists. Use #update to make changes." if @in_jss
|
78
|
-
|
78
|
+
|
79
|
+
@api.post_rsrc(rest_rsrc, rest_xml) =~ %r{><id>(\d+)</id><}
|
79
80
|
@id = Regexp.last_match(1).to_i
|
80
81
|
@in_jss = true
|
81
82
|
@need_to_update = false
|
82
83
|
@rest_rsrc = "#{self.class::RSRC_BASE}/id/#{@id}"
|
84
|
+
|
85
|
+
# clear any caches for this class
|
86
|
+
# so they'll re-cache as needed
|
87
|
+
@api.flushcache self.class::RSRC_LIST_KEY
|
88
|
+
|
83
89
|
@id
|
84
90
|
end
|
85
91
|
|
@@ -180,6 +180,7 @@ module JSS
|
|
180
180
|
raise MissingDataError, 'No popup_choices set for Pop-up Menu input_type.' unless @popup_choices.is_a?(Array) && !@popup_choices.empty?
|
181
181
|
end
|
182
182
|
super
|
183
|
+
@api.flushcache self.class
|
183
184
|
end
|
184
185
|
|
185
186
|
# @see JSS::APIObject#delete
|
@@ -191,6 +192,7 @@ module JSS
|
|
191
192
|
@api.open_timeout = orig_open_timeout + 1800
|
192
193
|
begin
|
193
194
|
super
|
195
|
+
@api.flushcache self.class
|
194
196
|
ensure
|
195
197
|
@api.timeout = orig_timeout
|
196
198
|
@api.open_timeout = orig_open_timeout
|
@@ -233,7 +235,7 @@ module JSS
|
|
233
235
|
#
|
234
236
|
def data_type=(new_val)
|
235
237
|
return nil if @data_type == new_val
|
236
|
-
raise JSS::InvalidDataError, "data_type must be a string, one of: #{DATA_TYPES.join(', ')}" unless DATA_TYPES.include? new_val
|
238
|
+
raise JSS::InvalidDataError, "data_type must be a string, one of: '#{DATA_TYPES.join("', '")}'" unless DATA_TYPES.include? new_val
|
237
239
|
@data_type = new_val
|
238
240
|
@need_to_update = true
|
239
241
|
end #
|
data/lib/jss/api_object/group.rb
CHANGED
@@ -84,7 +84,7 @@ module JSS
|
|
84
84
|
# groups.
|
85
85
|
#
|
86
86
|
def self.all_static(refresh = false, api: JSS.api)
|
87
|
-
all(refresh, api: api).
|
87
|
+
all(refresh, api: api).reject { |g| g[:is_smart] }
|
88
88
|
end
|
89
89
|
|
90
90
|
# Immediatly add and/or remove members in a static group without
|
@@ -105,7 +105,7 @@ module JSS
|
|
105
105
|
# @return [void]
|
106
106
|
#
|
107
107
|
def self.change_membership(group, add_members: [], remove_members: [], api: JSS.api)
|
108
|
-
raise JSS::NoSuchItemError, "No #{self} matching '#{
|
108
|
+
raise JSS::NoSuchItemError, "No #{self} matching '#{group}'" unless (group_id = valid_id group, api: api)
|
109
109
|
raise JSS::UnsupportedError, "Not a static group, can't change membership directly" if map_all_ids_to(:is_smart, api: api)[group_id]
|
110
110
|
|
111
111
|
add_members = [add_members].flatten
|
@@ -210,7 +210,7 @@ module JSS
|
|
210
210
|
#####################################
|
211
211
|
|
212
212
|
# When creating a new group in the JSS, you must call .make with a :type key
|
213
|
-
# and a value of :smart or :static, as well as a :name
|
213
|
+
# and a value of :smart or :static, as well as a :name
|
214
214
|
#
|
215
215
|
# @see JSS::APIObject
|
216
216
|
#
|
@@ -236,23 +236,61 @@ module JSS
|
|
236
236
|
|
237
237
|
# @see Creatable#create
|
238
238
|
#
|
239
|
-
|
239
|
+
# @param calculate_members [Boolan] should the local membership list be
|
240
|
+
# re-read from the API after the group is created?
|
241
|
+
#
|
242
|
+
# @param retries [Integer] If calculate_members is true, refetching the
|
243
|
+
# group to re-read the membership can happen too fast, the JSS won't know
|
244
|
+
# it exists yet and will throw a RestClient::NotFound error. If that
|
245
|
+
# happens, try again this many times with a 1 second pause between attempts.
|
246
|
+
#
|
247
|
+
def create(calculate_members: true, retries: 10)
|
240
248
|
if @is_smart
|
241
249
|
raise JSS::MissingDataError, 'No criteria specified for smart group' unless @criteria
|
242
250
|
end
|
243
251
|
super()
|
244
|
-
|
252
|
+
|
253
|
+
if calculate_members
|
254
|
+
tries = 0
|
255
|
+
while tries < retries
|
256
|
+
begin
|
257
|
+
refresh_members
|
258
|
+
break
|
259
|
+
rescue RestClient::NotFound
|
260
|
+
sleep 1
|
261
|
+
tries += 1
|
262
|
+
end # begin
|
263
|
+
end # while
|
264
|
+
end # if calc members
|
265
|
+
|
245
266
|
@id
|
246
267
|
end
|
247
268
|
|
248
269
|
# @see Updatable#update
|
249
270
|
#
|
250
|
-
def update
|
251
|
-
super
|
252
|
-
refresh_members
|
271
|
+
def update(refresh: true)
|
272
|
+
super()
|
273
|
+
refresh_members if refresh
|
253
274
|
@id
|
254
275
|
end
|
255
276
|
|
277
|
+
# Wrapper/alias for both create and update
|
278
|
+
def save(**params)
|
279
|
+
params[:calculate_members] = true if params[:calculate_members].nil?
|
280
|
+
params[:retries] = 10 if params[:retries].nil?
|
281
|
+
params[:refresh] = true if params[:refresh].nil?
|
282
|
+
|
283
|
+
if @in_jss
|
284
|
+
raise JSS::UnsupportedError, 'Updating this object in the JSS is currently not supported by ruby-jss' unless updatable?
|
285
|
+
|
286
|
+
|
287
|
+
update refresh: params[:refresh]
|
288
|
+
else
|
289
|
+
raise JSS::UnsupportedError, 'Creating this object in the JSS is currently not supported by ruby-jss' unless creatable?
|
290
|
+
create calculate_members: params[:calculate_members], retries: params[:retries]
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
256
294
|
# @see APIObject#delete
|
257
295
|
#
|
258
296
|
def delete
|
@@ -269,6 +307,7 @@ module JSS
|
|
269
307
|
#
|
270
308
|
def criteria=(new_criteria)
|
271
309
|
raise InvalidDataError, 'Only smart groups have criteria.' unless @is_smart
|
310
|
+
|
272
311
|
super
|
273
312
|
end
|
274
313
|
|
@@ -0,0 +1,198 @@
|
|
1
|
+
### Copyright 2019 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
|
+
# Module Variables
|
30
|
+
#####################################
|
31
|
+
|
32
|
+
# Module Methods
|
33
|
+
#####################################
|
34
|
+
|
35
|
+
# Classes
|
36
|
+
#####################################
|
37
|
+
|
38
|
+
# An iBeacon in the JSS.
|
39
|
+
# @see JSS::APIObject
|
40
|
+
#
|
41
|
+
class IBeacon < JSS::APIObject
|
42
|
+
|
43
|
+
# Mix-Ins
|
44
|
+
#####################################
|
45
|
+
include JSS::Creatable
|
46
|
+
include JSS::Updatable
|
47
|
+
|
48
|
+
# Class Methods
|
49
|
+
#####################################
|
50
|
+
|
51
|
+
# Class Constants
|
52
|
+
#####################################
|
53
|
+
|
54
|
+
# The base for REST resources of this class
|
55
|
+
RSRC_BASE = 'ibeacons'.freeze
|
56
|
+
|
57
|
+
# the hash key used for the JSON list output of all objects in the JSS
|
58
|
+
RSRC_LIST_KEY = :ibeacons
|
59
|
+
|
60
|
+
# The hash key used for the JSON object output.
|
61
|
+
# It's also used in various error messages
|
62
|
+
RSRC_OBJECT_KEY = :ibeacon
|
63
|
+
|
64
|
+
# the object type for this object in
|
65
|
+
# the object history table.
|
66
|
+
# See {APIObject#add_object_history_entry}
|
67
|
+
OBJECT_HISTORY_OBJECT_TYPE = 360
|
68
|
+
|
69
|
+
# The major & minor values, if used, must be in this range
|
70
|
+
MAJOR_MINOR_RANGE = 0..65_535.freeze
|
71
|
+
|
72
|
+
# If not used, this is the value for the major and minor
|
73
|
+
MAJOR_MINOR_UNUSED = -1
|
74
|
+
|
75
|
+
# Attributes
|
76
|
+
#####################################
|
77
|
+
|
78
|
+
### @return [String] the top-level region identifier, a valid UUID
|
79
|
+
attr_reader :uuid
|
80
|
+
|
81
|
+
### @return [IPAddr] the mid-level region identifier,
|
82
|
+
# an integer within MAJOR_MINOR_RANGE or MAJOR_MINOR_UNUSED
|
83
|
+
attr_reader :major
|
84
|
+
|
85
|
+
### @return [IPAddr] the low-level region identifier,
|
86
|
+
# an integer within MAJOR_MINOR_RANGE or MAJOR_MINOR_UNUSED
|
87
|
+
attr_reader :minor
|
88
|
+
|
89
|
+
# Constructor
|
90
|
+
# @see JSS::APIObject.initialize
|
91
|
+
#####################################
|
92
|
+
def initialize(args = {})
|
93
|
+
super args
|
94
|
+
|
95
|
+
@uuid = @init_data[:uuid]
|
96
|
+
@major = @init_data[:major]
|
97
|
+
@minor = @init_data[:minor]
|
98
|
+
|
99
|
+
# defaults
|
100
|
+
@major ||= MAJOR_MINOR_UNUSED
|
101
|
+
@minor ||= MAJOR_MINOR_UNUSED
|
102
|
+
end # init
|
103
|
+
|
104
|
+
|
105
|
+
# Public Instance Methods
|
106
|
+
#####################################
|
107
|
+
|
108
|
+
# set the uuid
|
109
|
+
#
|
110
|
+
# @param newval[String] the new uuid
|
111
|
+
#
|
112
|
+
# @return [void]
|
113
|
+
#
|
114
|
+
def uuid=(newval)
|
115
|
+
JSS::Validate.uuid newval
|
116
|
+
@uuid = newval
|
117
|
+
@need_to_update = true
|
118
|
+
end
|
119
|
+
|
120
|
+
# Set the major value to for this iBeacon region.
|
121
|
+
# Use nil or -1 to not use the major value
|
122
|
+
#
|
123
|
+
# @param newval[String] the new value
|
124
|
+
#
|
125
|
+
# @return [void]
|
126
|
+
#
|
127
|
+
def major=(newval)
|
128
|
+
if newval.nil? || newval == MAJOR_MINOR_UNUSED
|
129
|
+
newval ||= MAJOR_MINOR_UNUSED
|
130
|
+
else
|
131
|
+
JSS::Validate.ibeacon_major_minor newval
|
132
|
+
end
|
133
|
+
@major = newval
|
134
|
+
@need_to_update = true
|
135
|
+
end
|
136
|
+
|
137
|
+
# Set the minoe value to for this iBeacon region.
|
138
|
+
# Use nil or -1 to not use the major value
|
139
|
+
#
|
140
|
+
# @param newval[String] the new value
|
141
|
+
#
|
142
|
+
# @return [void]
|
143
|
+
#
|
144
|
+
def minor=(newval)
|
145
|
+
if newval.nil? || newval == MAJOR_MINOR_UNUSED
|
146
|
+
newval ||= MAJOR_MINOR_UNUSED
|
147
|
+
else
|
148
|
+
JSS::Validate.ibeacon_major_minor newval
|
149
|
+
end
|
150
|
+
|
151
|
+
@minor = newval
|
152
|
+
@need_to_update = true
|
153
|
+
end
|
154
|
+
|
155
|
+
# @return is the major identifier being used in this region?
|
156
|
+
def using_major?
|
157
|
+
@major == MAJOR_MINOR_UNUSED
|
158
|
+
end
|
159
|
+
|
160
|
+
# @return is the minor identifier being used in this region?
|
161
|
+
def using_minor?
|
162
|
+
@minor == MAJOR_MINOR_UNUSED
|
163
|
+
end
|
164
|
+
|
165
|
+
# @see Creatable.create
|
166
|
+
def create
|
167
|
+
raise JSS::MissingDataError, 'uuid may not be empty' if @uuid.to_s.empty?
|
168
|
+
|
169
|
+
super
|
170
|
+
end
|
171
|
+
|
172
|
+
# @see Updatable.update
|
173
|
+
def update
|
174
|
+
raise JSS::MissingDataError, 'uuid may not be empty' if @uuid.to_s.empty?
|
175
|
+
|
176
|
+
super
|
177
|
+
end
|
178
|
+
|
179
|
+
# private instance methods
|
180
|
+
######################
|
181
|
+
private
|
182
|
+
|
183
|
+
# the xml formated data for adding or updating this in the JSS
|
184
|
+
#
|
185
|
+
def rest_xml
|
186
|
+
doc = REXML::Document.new APIConnection::XML_HEADER
|
187
|
+
ns = doc.add_element RSRC_OBJECT_KEY.to_s
|
188
|
+
ns.add_element('name').text = @name
|
189
|
+
ns.add_element('uuid').text = @uuid
|
190
|
+
ns.add_element('major').text = @major.to_s
|
191
|
+
ns.add_element('minor').text = @minor.to_s
|
192
|
+
doc.to_s
|
193
|
+
end # rest_xml
|
194
|
+
|
195
|
+
|
196
|
+
end # class ibeacon
|
197
|
+
|
198
|
+
end # module
|
data/lib/jss/api_object/mdm.rb
CHANGED
@@ -437,6 +437,9 @@ module JSS
|
|
437
437
|
def raw_targets_to_ids(targets, api: JSS.api, expand_groups: true)
|
438
438
|
targets = targets.is_a?(Array) ? targets : [targets]
|
439
439
|
|
440
|
+
# flush caches before checking ids and managment
|
441
|
+
api.flushcache self::RSRC_LIST_KEY
|
442
|
+
|
440
443
|
# make sure its an array of ids
|
441
444
|
targets.map! do |md|
|
442
445
|
id = valid_id md, api: api
|
@@ -453,7 +456,7 @@ module JSS
|
|
453
456
|
|
454
457
|
# make sure all of them are managed, or else the API will raise a 400
|
455
458
|
# 'Bad Request' when sending the command to an unmanaged target
|
456
|
-
all_mgd =
|
459
|
+
all_mgd = map_all_ids_to(:managed, api: api).select { |_id, mgd| mgd }.keys
|
457
460
|
|
458
461
|
targets.each do |target_id|
|
459
462
|
raise JSS::UnmanagedError, "#{self} with id #{target_id} is not managed. Cannot send command." unless all_mgd.include? target_id
|
@@ -497,14 +497,14 @@ module JSS
|
|
497
497
|
#
|
498
498
|
def serial_number=(new_val)
|
499
499
|
return nil if new_val == @serial_number
|
500
|
-
@serial_number = new_val.empty? ? new_val : JSS::Validate.
|
500
|
+
@serial_number = new_val.empty? ? new_val : JSS::Validate.doesnt_already_exist(self.class, :serial_number, new_val, api: api)
|
501
501
|
@need_to_update = true
|
502
502
|
end
|
503
503
|
|
504
504
|
#
|
505
505
|
def udid=(new_val)
|
506
506
|
return nil if new_val == @udid
|
507
|
-
@udid = new_val.empty? ? new_val : JSS::Validate.
|
507
|
+
@udid = new_val.empty? ? new_val : JSS::Validate.doesnt_already_exist(self.class, :udid, new_val, api: api)
|
508
508
|
@need_to_update = true
|
509
509
|
end
|
510
510
|
|
@@ -252,9 +252,24 @@ module JSS
|
|
252
252
|
def self.all_for_title(title, api: JSS.api)
|
253
253
|
title_id = JSS::PatchTitle.valid_id title
|
254
254
|
raise JSS::NoSuchItemError, "No PatchTitle matching '#{title}'" unless title_id
|
255
|
+
|
255
256
|
api.get_rsrc("#{RSRC_BY_PATCH_TITLE}#{title_id}")[RSRC_BY_PATCH_TITLE_LIST_KEY]
|
256
257
|
end
|
257
258
|
|
259
|
+
# Override APIObject.fetch, since there's no .../patchpolicies/name/... endpoint
|
260
|
+
# @see APIObject#fetch
|
261
|
+
#
|
262
|
+
def self.fetch(searchterm = nil, **args)
|
263
|
+
name_search = args.delete :name
|
264
|
+
if name_search
|
265
|
+
id = valid_id name_search
|
266
|
+
raise JSS::NoSuchItemError, "No #{self::RSRC_OBJECT_KEY} found #{err_detail}" unless id
|
267
|
+
|
268
|
+
args[:id] = id
|
269
|
+
end
|
270
|
+
super
|
271
|
+
end # fetch
|
272
|
+
|
258
273
|
# Attributes
|
259
274
|
################################
|
260
275
|
|
@@ -93,9 +93,15 @@ module JSS
|
|
93
93
|
return nil unless @need_to_update
|
94
94
|
raise JSS::UnsupportedError, "Editing #{self.class::RSRC_LIST_KEY} isn't yet supported. Please use other Casper workflows." unless updatable?
|
95
95
|
raise JSS::NoSuchItemError, "Not In JSS! Use #create to create this #{self.class::RSRC_OBJECT_KEY} in the JSS before updating it." unless @in_jss
|
96
|
+
|
96
97
|
@api.put_rsrc @rest_rsrc, rest_xml
|
97
98
|
@need_to_update = false
|
98
99
|
refresh_icon if self_servable?
|
100
|
+
|
101
|
+
# clear any cached all-lists or id-maps for this class
|
102
|
+
# so they'll re-cache as needed
|
103
|
+
@api.flushcache self.class::RSRC_LIST_KEY
|
104
|
+
|
99
105
|
@id
|
100
106
|
end # update
|
101
107
|
|
data/lib/jss/validate.rb
CHANGED
@@ -49,9 +49,13 @@ module JSS
|
|
49
49
|
def self.mac_address(val, msg = nil)
|
50
50
|
msg ||= "Not a valid MAC address: '#{val}'"
|
51
51
|
raise JSS::InvalidDataError, msg unless val =~ MAC_ADDR_RE
|
52
|
+
|
52
53
|
val
|
53
54
|
end
|
54
55
|
|
56
|
+
# Segments of a valid IPv4 address are integers in this range.
|
57
|
+
IP_SEGMENT_RANGE = 0..255
|
58
|
+
|
55
59
|
# Validate the format and content of an IPv4 address
|
56
60
|
#
|
57
61
|
# @param val[String] The value to validate
|
@@ -65,12 +69,14 @@ module JSS
|
|
65
69
|
ok = true
|
66
70
|
parts = val.strip.split '.'
|
67
71
|
ok = false unless parts.size == 4
|
68
|
-
parts.each { |p| ok = false unless p.jss_integer? &&
|
72
|
+
parts.each { |p| ok = false unless p.jss_integer? && IP_SEGMENT_RANGE.include?(p.to_i) }
|
69
73
|
raise JSS::InvalidDataError, msg unless ok
|
74
|
+
|
70
75
|
val
|
71
76
|
end
|
72
77
|
|
73
|
-
# Validate that a value doesn't already exist for a given identifier of a
|
78
|
+
# Validate that a value doesn't already exist for a given identifier of a
|
79
|
+
# given class
|
74
80
|
#
|
75
81
|
# e.g. when klass = JSS::Computer, identifier = :name, and val = 'foo'
|
76
82
|
# will raise an error when a computer named 'foo' exists
|
@@ -89,9 +95,17 @@ module JSS
|
|
89
95
|
#
|
90
96
|
# @return [Object] the validated unique value
|
91
97
|
#
|
92
|
-
def self.
|
98
|
+
def self.doesnt_already_exist(klass, identifier, val, msg = nil, api: JSS.api)
|
93
99
|
msg ||= "A #{klass} already exists with #{identifier} '#{val}'"
|
94
100
|
return val unless klass.all(:refresh, api: api).map { |i| i[identifier] }.include? val
|
101
|
+
|
102
|
+
key = klass.real_lookup_key identifier
|
103
|
+
|
104
|
+
# use map_all_ids_to cuz it works with any identifer, even non-existing
|
105
|
+
existing_values = klass.map_all_ids_to( key, api: api).values
|
106
|
+
matches = existing_values.select { |existing_val| existing_val.casecmp? val }
|
107
|
+
return val if matches.empty?
|
108
|
+
|
95
109
|
raise JSS::AlreadyExistsError, msg
|
96
110
|
end
|
97
111
|
|
@@ -113,6 +127,7 @@ module JSS
|
|
113
127
|
return bool if JSS::TRUE_FALSE.include? bool
|
114
128
|
return true if bool.to_s =~ /^(true|yes)$/i
|
115
129
|
return false if bool.to_s =~ /^(false|no)$/i
|
130
|
+
|
116
131
|
raise JSS::InvalidDataError, msg
|
117
132
|
end
|
118
133
|
|
@@ -131,6 +146,7 @@ module JSS
|
|
131
146
|
msg ||= 'Value must be an integer'
|
132
147
|
val = val.to_i if val.is_a?(String) && val.jss_integer?
|
133
148
|
raise JSS::InvalidDataError, msg unless val.is_a? Integer
|
149
|
+
|
134
150
|
val
|
135
151
|
end
|
136
152
|
|
@@ -145,6 +161,42 @@ module JSS
|
|
145
161
|
def self.non_empty_string(val, msg = nil)
|
146
162
|
msg ||= 'value must be a non-empty String'
|
147
163
|
raise JSS::InvalidDataError, msg unless val.is_a?(String) && !val.empty?
|
164
|
+
|
165
|
+
val
|
166
|
+
end
|
167
|
+
|
168
|
+
UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.freeze
|
169
|
+
|
170
|
+
# validate that the given value is a valid uuid string
|
171
|
+
#
|
172
|
+
# @param val [Object] the thing to validate
|
173
|
+
#
|
174
|
+
# @param msg[String] A custom error message when the value is invalid
|
175
|
+
#
|
176
|
+
# @return [String] the valid uuid string
|
177
|
+
#
|
178
|
+
def self.uuid(val, msg = nil)
|
179
|
+
msg ||= 'value must be valid uuid'
|
180
|
+
raise JSS::InvalidDataError, msg unless val.is_a?(String) && val =~ UUID_RE
|
181
|
+
|
182
|
+
val
|
183
|
+
end
|
184
|
+
|
185
|
+
# validate that the given value is an integer in the JSS::IBeacon::MAJOR_MINOR_RANGE
|
186
|
+
#
|
187
|
+
# @param val [Object] the thing to validate
|
188
|
+
#
|
189
|
+
# @param msg[String] A custom error message when the value is invalid
|
190
|
+
#
|
191
|
+
# @return [String] the valid integer
|
192
|
+
#
|
193
|
+
def self.ibeacon_major_minor(val, msg = nil)
|
194
|
+
msg ||= "value must be an integer in the range #{JSS::IBeacon::MAJOR_MINOR_RANGE}"
|
195
|
+
val = val.to_i if val.is_a?(String) && val.jss_integer?
|
196
|
+
ok = val.is_a? Integer
|
197
|
+
ok = JSS::IBeacon::MAJOR_MINOR_RANGE.include? val if ok
|
198
|
+
raise JSS::InvalidDataError, msg unless ok
|
199
|
+
|
148
200
|
val
|
149
201
|
end
|
150
202
|
|
data/lib/jss/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-jss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0b1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Lasell
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-
|
12
|
+
date: 2019-10-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: plist
|
@@ -180,6 +180,7 @@ files:
|
|
180
180
|
- lib/jss/api_object/group/computer_group.rb
|
181
181
|
- lib/jss/api_object/group/mobile_device_group.rb
|
182
182
|
- lib/jss/api_object/group/user_group.rb
|
183
|
+
- lib/jss/api_object/ibeacon.rb
|
183
184
|
- lib/jss/api_object/ldap_server.rb
|
184
185
|
- lib/jss/api_object/locatable.rb
|
185
186
|
- lib/jss/api_object/mac_application.rb
|
@@ -288,12 +289,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
288
289
|
version: 2.0.0
|
289
290
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
290
291
|
requirements:
|
291
|
-
- - "
|
292
|
+
- - ">"
|
292
293
|
- !ruby/object:Gem::Version
|
293
|
-
version:
|
294
|
+
version: 1.3.1
|
294
295
|
requirements: []
|
295
|
-
|
296
|
-
rubygems_version: 2.7.8
|
296
|
+
rubygems_version: 3.0.3
|
297
297
|
signing_key:
|
298
298
|
specification_version: 4
|
299
299
|
summary: A Ruby interface to the Jamf Pro REST API
|