artrest 0.0.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.
- checksums.yaml +15 -0
- data/.gitignore +20 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +31 -0
- data/artrest.gemspec +30 -0
- data/bin/artrest +194 -0
- data/lib/artrest.rb +81 -0
- data/lib/artrest/build.rb +31 -0
- data/lib/artrest/buildnumber.rb +26 -0
- data/lib/artrest/builds.rb +46 -0
- data/lib/artrest/dir_entry.rb +115 -0
- data/lib/artrest/repositories.rb +61 -0
- data/lib/artrest/repository.rb +42 -0
- data/lib/artrest/resource.rb +366 -0
- data/lib/artrest/resources.rb +54 -0
- data/lib/artrest/system.rb +74 -0
- data/lib/artrest/system_general_configuration.rb +45 -0
- data/lib/artrest/version.rb +3 -0
- data/spec/artrest/build_spec.rb +62 -0
- data/spec/artrest/buildnumber_spec.rb +45 -0
- data/spec/artrest/builds_spec.rb +69 -0
- data/spec/artrest/folder_spec.rb +88 -0
- data/spec/artrest/repositories_spec.rb +47 -0
- data/spec/artrest/repository_spec.rb +63 -0
- data/spec/artrest/resource_spec.rb +385 -0
- data/spec/artrest/resources_spec.rb +66 -0
- data/spec/artrest/system_general_configuration_spec.rb +72 -0
- data/spec/artrest/system_spec.rb +50 -0
- data/spec/artrest_spec.rb +30 -0
- data/spec/fixtures/build/build_response_correct.txt +7 -0
- data/spec/fixtures/buildnumber/buildnumber_25_response.txt +7 -0
- data/spec/fixtures/builds/build_api_response_correct.txt +7 -0
- data/spec/fixtures/folder/pom_file_response_1.txt +7 -0
- data/spec/fixtures/folder/sub_folder_response.txt +7 -0
- data/spec/fixtures/folder/top_folder_response.txt +7 -0
- data/spec/fixtures/repositories/libs_snapshot_local_folder_response.txt +7 -0
- data/spec/fixtures/repositories/repositories_response.txt +7 -0
- data/spec/fixtures/repository/libs_snapshot_local_response.txt +7 -0
- data/spec/fixtures/resource/artifact_response.txt +7 -0
- data/spec/fixtures/resource/build_response.txt +7 -0
- data/spec/fixtures/resource/buildnumber_response.txt +7 -0
- data/spec/fixtures/resource/builds_response.txt +7 -0
- data/spec/fixtures/resource/folder_response.txt +7 -0
- data/spec/fixtures/resource/json_response.txt +7 -0
- data/spec/fixtures/resource/repositories_response.txt +7 -0
- data/spec/fixtures/resource/repository_not_found_response.txt +7 -0
- data/spec/fixtures/resource/repository_response.txt +7 -0
- data/spec/fixtures/resource/repository_unauthorized_response.txt +8 -0
- data/spec/fixtures/resource/string_response.txt +188 -0
- data/spec/fixtures/resource/sub_response.txt +188 -0
- data/spec/fixtures/resource/system_general_configuration_response.txt +654 -0
- data/spec/fixtures/resource/system_response.txt +188 -0
- data/spec/fixtures/resources/resources_response.txt +7 -0
- data/spec/fixtures/system/200_OK_ping_response.txt +7 -0
- data/spec/fixtures/system/general_configuration_response.txt +654 -0
- data/spec/fixtures/system/system_response.txt +188 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +38 -0
- metadata +269 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
module ArtRest
|
3
|
+
|
4
|
+
# Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
|
5
|
+
# {Build Info}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-BuildInfo]
|
6
|
+
# resource.
|
7
|
+
#
|
8
|
+
# === Example
|
9
|
+
#
|
10
|
+
# buildInfo = ArtRest::Buildnumber.new('http://localhost:8081/artifactory/api/build/wicket/51', { ... })
|
11
|
+
#
|
12
|
+
class Buildnumber < ArtRest::Resource
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
def matches_path(path, options) # :nodoc:
|
17
|
+
path =~ %r|^/api/build/[a-zA-Z+-._]+/\d+$|
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
self.mime_type = MIME::Types['application/json']
|
22
|
+
|
23
|
+
self.resource_attributes :uri,
|
24
|
+
:buildInfo
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
module ArtRest
|
3
|
+
|
4
|
+
# Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
|
5
|
+
# {All Builds}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-AllBuilds]
|
6
|
+
# resource.
|
7
|
+
#
|
8
|
+
# === Example
|
9
|
+
#
|
10
|
+
# allBuilds = ArtRest::Builds.new('http://localhost:8081/artifactory/api/build', { ... })
|
11
|
+
#
|
12
|
+
class Builds < ArtRest::Resources
|
13
|
+
|
14
|
+
class << self
|
15
|
+
public
|
16
|
+
|
17
|
+
# Shortcut method to retrieve *the* ArtRest::Builds
|
18
|
+
# instance.
|
19
|
+
#
|
20
|
+
# * *Args* :
|
21
|
+
# - +base_url+ -> Our Artifactory server's base URL
|
22
|
+
# - +options+ -> A Hash containing username and password
|
23
|
+
# * *Returns* :
|
24
|
+
# - *The* ArtRest::Builds instance representing the
|
25
|
+
# collection of all Artifactory builds
|
26
|
+
#
|
27
|
+
def get(base_url, options)
|
28
|
+
Builds.new("#{base_url}/api/build", options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def matches_path(path, options) # :nodoc:
|
32
|
+
path =~ %r|^/api/build/?$|
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
self.mime_type = MIME::Types['application/json']
|
37
|
+
|
38
|
+
self.resources_creator = Proc.new do |content, options|
|
39
|
+
self_url = content['uri']
|
40
|
+
(content['builds'] || []).map { |build| ArtRest::Build.new("#{self_url}#{build['uri']}", options) }
|
41
|
+
end
|
42
|
+
|
43
|
+
self.resource_attributes :uri,
|
44
|
+
:builds
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
|
2
|
+
module ArtRest
|
3
|
+
|
4
|
+
module DirEntry
|
5
|
+
|
6
|
+
def self.create_node(resource_url, options, resource_descriptor, &block)
|
7
|
+
if resource_descriptor['folder'] or not resource_descriptor['children'].nil?
|
8
|
+
ArtRest::Folder.new(resource_url, options, &block)
|
9
|
+
else
|
10
|
+
ArtRest::Artifact.new(resource_url, options, &block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
|
17
|
+
# {Folder Info}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-FolderInfo]
|
18
|
+
# resource.
|
19
|
+
#
|
20
|
+
# === Example
|
21
|
+
#
|
22
|
+
# folder = ArtRest::Folder.new('http://localhost:8081/artifactory/api/storage/libs-release-local/commons-lang/commons-lang', { ... })
|
23
|
+
#
|
24
|
+
class Folder < ArtRest::Resources
|
25
|
+
include ArtRest::DirEntry
|
26
|
+
|
27
|
+
class << self
|
28
|
+
|
29
|
+
def matches_path(path, options) # :nodoc:
|
30
|
+
return false unless path =~ %r|^/api/storage/[a-zA-Z0-9_.+-]+/.+$|
|
31
|
+
content_hash = JSON.parse(RestClient::Resource.new([options[:base_url], path].join, options[:user], options[:password]).get)
|
32
|
+
return content_hash['children']
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
self.mime_type = MIME::Types['application/json']
|
37
|
+
|
38
|
+
self.resources_creator = Proc.new do |content, options|
|
39
|
+
(content['children'] || []).map { |child| ArtRest::DirEntry.create_node("#{content['uri']}#{child['uri']}", options, child) }
|
40
|
+
end
|
41
|
+
|
42
|
+
self.resource_attributes :path,
|
43
|
+
:lastUpdated,
|
44
|
+
:repo,
|
45
|
+
:uri,
|
46
|
+
:modifiedBy,
|
47
|
+
:created,
|
48
|
+
:createdBy,
|
49
|
+
:lastModified,
|
50
|
+
:metadataUri
|
51
|
+
|
52
|
+
public
|
53
|
+
|
54
|
+
def [](suburl, &new_block) # :nodoc:
|
55
|
+
subresource_content = JSON.parse(RestClient::Resource.new(url, options, &new_block)[suburl].get)
|
56
|
+
ArtRest::DirEntry.create_node([url, suburl].join, options, subresource_content, &new_block)
|
57
|
+
end
|
58
|
+
|
59
|
+
def traverse(&block)
|
60
|
+
each do |child_entry|
|
61
|
+
block.call(child_entry)
|
62
|
+
if child_entry.is_a?(ArtRest::Folder) then
|
63
|
+
child_entry.each &block
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
|
70
|
+
# {File Info}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-FileInfo]
|
71
|
+
# resource.
|
72
|
+
#
|
73
|
+
# === Example
|
74
|
+
#
|
75
|
+
# file = ArtRest::File.new('http://localhost:8081/artifactory/api/storage/libs-release-local/commons-lang/commons-lang/3.2/commons-lang-3.2.jar', { ... })
|
76
|
+
#
|
77
|
+
class Artifact < ArtRest::Resource
|
78
|
+
include ArtRest::DirEntry
|
79
|
+
|
80
|
+
class << self
|
81
|
+
|
82
|
+
def matches_path(path, options) # :nodoc:
|
83
|
+
return false unless path =~ %r|^/api/storage/[a-zA-Z0-9_.+-]+/.+$|
|
84
|
+
content_hash = JSON.parse(RestClient::Resource.new([options[:base_url], path].join, options[:user], options[:password]).get)
|
85
|
+
return ! content_hash['children']
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
self.mime_type = MIME::Types['application/json']
|
90
|
+
|
91
|
+
self.resource_attributes :path,
|
92
|
+
:lastUpdated,
|
93
|
+
:repo,
|
94
|
+
:uri,
|
95
|
+
:modifiedBy,
|
96
|
+
:created,
|
97
|
+
:createdBy,
|
98
|
+
:lastModified,
|
99
|
+
:metadataUri,
|
100
|
+
:mimeType,
|
101
|
+
:downloadUri,
|
102
|
+
:size,
|
103
|
+
:checksums,
|
104
|
+
:originalChecksums
|
105
|
+
|
106
|
+
public
|
107
|
+
|
108
|
+
# Overwritten to always throw NotImplementedError since ArtRest::Artifacts
|
109
|
+
# don't have sub resources.
|
110
|
+
#
|
111
|
+
def [](suburl, &new_block)
|
112
|
+
raise NotImplementedError.new("Instances of ArtRest::Artifact don't have child resources")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
module ArtRest
|
3
|
+
|
4
|
+
# Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
|
5
|
+
# {Repositories}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-GetRepositories]
|
6
|
+
# resource.
|
7
|
+
#
|
8
|
+
# === Example
|
9
|
+
#
|
10
|
+
# repos = ArtRest::Repositories.new('http://localhost:8081/artifactory/api/repositories', { ... })
|
11
|
+
#
|
12
|
+
class Repositories < ArtRest::Resources
|
13
|
+
|
14
|
+
class << self
|
15
|
+
public
|
16
|
+
|
17
|
+
# Shortcut method to retrieve *the* ArtRest::Repositories
|
18
|
+
# instance.
|
19
|
+
#
|
20
|
+
# * *Args* :
|
21
|
+
# - +base_url+ -> Our Artifactory server's base URL
|
22
|
+
# - +options+ -> A Hash containing username and password
|
23
|
+
# * *Returns* :
|
24
|
+
# - *The* ArtRest::Repositories instance representing the
|
25
|
+
# collection of all Artifactory repositories
|
26
|
+
#
|
27
|
+
def get(base_url, options)
|
28
|
+
Repositories.new("#{base_url}/api/repositories", options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def matches_path(path, options) # :nodoc:
|
32
|
+
path =~ %r|^/api/repositories/?$|
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
self.mime_type = MIME::Types['application/json']
|
37
|
+
|
38
|
+
self.resources_creator = Proc.new do |content, options|
|
39
|
+
content.map { |repo| ArtRest::Repository.new(repo['url'], options) }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Look up and return the repository[rdoc-ref:ArtRest::Repository] named
|
43
|
+
# +repo_name+. Optionally yield that repository to a block provided a
|
44
|
+
# block is given.
|
45
|
+
#
|
46
|
+
# * *Args* :
|
47
|
+
# [+repo_name+] Name of the repository to return
|
48
|
+
# * *Returns* :
|
49
|
+
# - The repository named +repo_name+, an ArtRest::Repository instance
|
50
|
+
# * *Raises* :
|
51
|
+
# [+RestClient::ResourceNotFound+] If +repo_name+ does not point to an
|
52
|
+
# existing repository
|
53
|
+
#
|
54
|
+
def repository(repo_name) # :yields: repository
|
55
|
+
repo_url = concat_urls(base_url, "/#{repo_name}/")
|
56
|
+
repo = ArtRest::Repository.new(repo_url, options, block)
|
57
|
+
yield repo if block_given?
|
58
|
+
repo
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
module ArtRest
|
3
|
+
|
4
|
+
# Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
|
5
|
+
# repository resource.
|
6
|
+
#
|
7
|
+
# *IMPORTANT* This is *not* the {Repository Configuration}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-RepositoryConfiguration]
|
8
|
+
# resource as this would require Artifactory Pro. It is rather a repository's
|
9
|
+
# top-level {folder}[rdoc-ref:ArtRest::Folder] "/".
|
10
|
+
#
|
11
|
+
# === Example
|
12
|
+
#
|
13
|
+
# libs_releas_local = ArtRest::Repository.new('http://localhost:8081/artifactory/api/storage/libs-release-local', { ... })
|
14
|
+
#
|
15
|
+
class Repository < ArtRest::Resources
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
def matches_path(path, options) # :nodoc:
|
20
|
+
path =~ %r|^/api/storage/[a-zA-Z+-._]+/?$|
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
self.mime_type = MIME::Types['application/json']
|
25
|
+
|
26
|
+
self.resources_creator = Proc.new do |content, options|
|
27
|
+
self_url = content['uri']
|
28
|
+
(content['children'] || []).map { |child| ArtRest::DirEntry.create_node("#{self_url}#{child['uri']}", options, child) }
|
29
|
+
end
|
30
|
+
|
31
|
+
self.resource_attributes :path,
|
32
|
+
:lastUpdated,
|
33
|
+
:repo,
|
34
|
+
:uri,
|
35
|
+
:modifiedBy,
|
36
|
+
:created,
|
37
|
+
:createdBy,
|
38
|
+
:lastModified,
|
39
|
+
:metadataUri,
|
40
|
+
:children
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,366 @@
|
|
1
|
+
|
2
|
+
module ArtRest
|
3
|
+
|
4
|
+
# Abstract base class for all Artifactory resources.
|
5
|
+
#
|
6
|
+
class Resource < RestClient::Resource
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
public
|
11
|
+
|
12
|
+
# Add +subclass+ to the list of known subclasses.
|
13
|
+
#
|
14
|
+
# * *Args* :
|
15
|
+
# - +subclass+ -> The subclass to add
|
16
|
+
#
|
17
|
+
def inherited(subclass)
|
18
|
+
@@subclasses ||= []
|
19
|
+
@@subclasses << subclass
|
20
|
+
end
|
21
|
+
|
22
|
+
# Define a singleton method +mime_type+ on the class this
|
23
|
+
# method is called on, where calling +mime_type+ will return
|
24
|
+
# +value+.
|
25
|
+
#
|
26
|
+
# This essentially emulates a "proper" class variable
|
27
|
+
# +mime_type+.
|
28
|
+
#
|
29
|
+
# * *Args* :
|
30
|
+
# - +value+ -> This class' mime type [MIME::Type/required]
|
31
|
+
#
|
32
|
+
def mime_type=(value)
|
33
|
+
self.singleton_class.class_eval do
|
34
|
+
define_method(:mime_type) do
|
35
|
+
value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Take an array of symbols +attrs+ and for each symbol
|
41
|
+
# "attr" define an instance method +attr+ on this class
|
42
|
+
# that returns <tt>content["attr"]</tt>.
|
43
|
+
#
|
44
|
+
# So, given an attribute <tt>:modifiedBy</tt> this will define
|
45
|
+
#
|
46
|
+
# def modifiedBy
|
47
|
+
# content['modifiedBy']
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
def resource_attributes(*attrs)
|
51
|
+
attrs.each do |attr|
|
52
|
+
define_method(attr) do
|
53
|
+
return content[attr.to_s] unless block_given?
|
54
|
+
yield content[attr.to_s]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Factory method: create and return an instance of a concrete
|
60
|
+
# ArtRest::Resource subclass appropriate for the supplied
|
61
|
+
# +resource_url+.
|
62
|
+
#
|
63
|
+
# === Example
|
64
|
+
#
|
65
|
+
# ArtRest::Resource.create('http://localhost:8081/artifactory/api/system,
|
66
|
+
# options)
|
67
|
+
#
|
68
|
+
# will return an ArtRest::System instance.
|
69
|
+
#
|
70
|
+
# * *Args* :
|
71
|
+
# [+resource_url+] URL of the resource to create [required]
|
72
|
+
# [+options+] Options hash holding base_url, user and
|
73
|
+
# password [required]
|
74
|
+
# [+block+] Passed on to <tt>RestClient::Resource.initialize</tt> [optional]
|
75
|
+
# * *Returns* :
|
76
|
+
# - An instance of a concrete ArtRest::Resource subclass appropriate
|
77
|
+
# for the supplied +resource_url+
|
78
|
+
#
|
79
|
+
def create(resource_url, options, &block)
|
80
|
+
check_options(options)
|
81
|
+
@@subclasses.each do |subclass|
|
82
|
+
return subclass.new(resource_url, options, &block) if
|
83
|
+
subclass.respond_to?(:matches_path) and
|
84
|
+
subclass.matches_path(relative_path(resource_url, options), options)
|
85
|
+
end
|
86
|
+
raise ArgumentError.new("Unsupported resource URL #{resource_url}")
|
87
|
+
end
|
88
|
+
|
89
|
+
def check_options(options) # :nodoc:
|
90
|
+
raise ArgumentError, "Must pass :base_url" unless options[:base_url]
|
91
|
+
raise ArgumentError, "Must pass :user" unless options[:user]
|
92
|
+
raise ArgumentError, "Must pass :password" unless options[:password]
|
93
|
+
end
|
94
|
+
|
95
|
+
protected
|
96
|
+
|
97
|
+
def relative_path(resource_url, options) # :nodoc:
|
98
|
+
check_options(options)
|
99
|
+
base_path = Addressable::URI.parse(options[:base_url]).path
|
100
|
+
resource_path = Addressable::URI.parse(resource_url).path
|
101
|
+
resource_path.slice!(base_path)
|
102
|
+
resource_path
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# This class' mime type
|
107
|
+
self.mime_type = MIME::Types['application/json']
|
108
|
+
|
109
|
+
public
|
110
|
+
|
111
|
+
# Create a new ArtRest::Resource instance, representing the Artifactory
|
112
|
+
# resource located at +resource_url+. Use username and password
|
113
|
+
# contained in +options+ to authenticate against the Artifactory server.
|
114
|
+
# Optionally take and cache the parsed +content+ of the resource pointed
|
115
|
+
# to by +resource_url+ in case the caller has already accessed that
|
116
|
+
# resource. A +block+, if given, will transparently be passed on to
|
117
|
+
# RestClient::Resource's - our ancestor's - constructor.
|
118
|
+
#
|
119
|
+
# * *Args* :
|
120
|
+
# - +resource_url+ -> URL of the Artifactory resource
|
121
|
+
# - +options+ -> A hash containing our Artifactory server's base url,
|
122
|
+
# and a username and a password to authenticate against that server
|
123
|
+
# - +content+ -> The *parsed* content of the resource, if the caller
|
124
|
+
# has already accessed that resource [optional]
|
125
|
+
# - +block+ -> A block that will be passed on to
|
126
|
+
# RestClient::Resource#initialize [optional]
|
127
|
+
#
|
128
|
+
def initialize(resource_url, options, content = nil, &block)
|
129
|
+
self.class.check_options(options)
|
130
|
+
super(resource_url, options, &block)
|
131
|
+
@content = content if content
|
132
|
+
end
|
133
|
+
|
134
|
+
# Return our Artifactory server's <em>base url</em>.
|
135
|
+
#
|
136
|
+
# === Example
|
137
|
+
#
|
138
|
+
# http://localhost:8081/artifactory
|
139
|
+
#
|
140
|
+
def base_url
|
141
|
+
options[:base_url]
|
142
|
+
end
|
143
|
+
|
144
|
+
# Return this resource's *parsed* content. In almost all cases this will
|
145
|
+
# be a Hash representing this resource's JSON content. The only
|
146
|
+
# exception is ArtRest::System, where the content is a plain text
|
147
|
+
# representation and thus returned as a String.
|
148
|
+
#
|
149
|
+
# If called with a +block+, yield this resource's parsed content to that
|
150
|
+
# block.
|
151
|
+
#
|
152
|
+
# * *Args* :
|
153
|
+
# [+block+] A block to yield this resource's parsed content to [optional]
|
154
|
+
# * *Returns* :
|
155
|
+
# - If called *without* a block, this resource's *parsed* content, most
|
156
|
+
# often a +Hash+, otherwise a +String+.
|
157
|
+
# If called *with* a block, +self+
|
158
|
+
# * *Raises* :
|
159
|
+
# [+RestClient::ResourceNotFound+] If the resource this instance
|
160
|
+
# represents does in fact not exist
|
161
|
+
# on the server
|
162
|
+
# [+RestClient::Unauthorized+] If accessing this resource is not
|
163
|
+
# authorized
|
164
|
+
#
|
165
|
+
def content # :yields: content
|
166
|
+
return _content unless block_given?
|
167
|
+
# If our block returns a value, this will become this resource's new
|
168
|
+
# content
|
169
|
+
yield _content
|
170
|
+
self
|
171
|
+
end
|
172
|
+
|
173
|
+
# Return this resource's *unparsed* content as a string[rdoc-ref:String].
|
174
|
+
#
|
175
|
+
# Most of Artifactory's resources are represented in JSON, and these
|
176
|
+
# will be parsed into an equivalent ruby {hash}[rdoc-ref:Hash] upon load. This
|
177
|
+
# method will *unparse* that hash back into a JSON string.
|
178
|
+
#
|
179
|
+
# In all other cases, when a resource's mime type is *not*
|
180
|
+
# 'application/json', that resource's representation will remain
|
181
|
+
# unchanged, i.e. will be stored as a string, and this method will
|
182
|
+
# return that string as is.
|
183
|
+
#
|
184
|
+
# * *Returns* :
|
185
|
+
# - This resource's unparsed content, a {string}[rdoc-ref:String]
|
186
|
+
# * *Raises* :
|
187
|
+
# [+RestClient::ResourceNotFound+] If the resource this instance
|
188
|
+
# represents does in fact not exist
|
189
|
+
# on the server
|
190
|
+
# [+RestClient::Unauthorized+] If accessing this resource is not
|
191
|
+
# authorized
|
192
|
+
#
|
193
|
+
def unparsed_content(fmt = :plain)
|
194
|
+
return _content unless self.class.mime_type == MIME::Types['application/json']
|
195
|
+
case fmt
|
196
|
+
when :pretty then
|
197
|
+
JSON.pretty_generate(_content)
|
198
|
+
else
|
199
|
+
JSON.generate(_content)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Set this resource's representation to +value+. Handle +value+ as
|
204
|
+
# appropriate for this resource's mime type:
|
205
|
+
#
|
206
|
+
# === application/json
|
207
|
+
#
|
208
|
+
# If we are dealing with a JSON resource - the most common case -
|
209
|
+
# +value+ may be one of
|
210
|
+
#
|
211
|
+
# * Hash: will be accepted as is
|
212
|
+
# * String: will be interpreted as a JSON encoded string and parsed into a
|
213
|
+
# ruby Hash
|
214
|
+
#
|
215
|
+
# Raise an +ArgumentError+ in all other cases.
|
216
|
+
#
|
217
|
+
# === text/plain or application/xml
|
218
|
+
#
|
219
|
+
# In this case, +value+ may be of any kind, but will be converted into a
|
220
|
+
# String via calling +value+.#to_s.
|
221
|
+
#
|
222
|
+
# * *Args* :
|
223
|
+
# - +value+ -> This resource's new content/representation. See above.
|
224
|
+
# * *Raises* :
|
225
|
+
# - +ArgumentError+ -> If this resource's mime type is
|
226
|
+
# +application/json+ and value is neither a Hash nor a String
|
227
|
+
#
|
228
|
+
def content=(value)
|
229
|
+
if value.nil?
|
230
|
+
@content = nil
|
231
|
+
return
|
232
|
+
end
|
233
|
+
case self.class.mime_type
|
234
|
+
when MIME::Types['application/json'] then
|
235
|
+
if value.is_a? Hash
|
236
|
+
@content = value
|
237
|
+
elsif value.is_a? String
|
238
|
+
@content = JSON.parse(value)
|
239
|
+
else
|
240
|
+
raise ArgumentError, "Can only accept Hash and String. Got: #{value.class}"
|
241
|
+
end
|
242
|
+
else
|
243
|
+
@content = value.to_s
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Yield this resource's parsed content to +block+. If block returns a value,
|
248
|
+
# accept this value as this resource's new content. Calling this method
|
249
|
+
# is therefore a potentially destructive operation.
|
250
|
+
#
|
251
|
+
# Also see #content.
|
252
|
+
#
|
253
|
+
# * *Args* :
|
254
|
+
# [+block+] A block to yield this resource's parsed content to
|
255
|
+
# * *Returns* :
|
256
|
+
# - +self+ This instance
|
257
|
+
# * *Raises* :
|
258
|
+
# [+RestClient::ResourceNotFound+] If the resource this instance
|
259
|
+
# represents does in fact not exist
|
260
|
+
# on the server
|
261
|
+
# [+RestClient::Unauthorized+] If accessing this resource is not
|
262
|
+
# authorized
|
263
|
+
#
|
264
|
+
def content! &block # :yields: content
|
265
|
+
# If our block returns a value, this will become this resource's new
|
266
|
+
# content
|
267
|
+
new_content = yield _content
|
268
|
+
self.content = new_content if new_content
|
269
|
+
self
|
270
|
+
end
|
271
|
+
|
272
|
+
# Look up and return the ArtRest::Resource instance that is located at
|
273
|
+
# +relative_path+ relative to this resource. Take care to return the
|
274
|
+
# appropriate ArtRest::Resource subtype.
|
275
|
+
#
|
276
|
+
# === Example
|
277
|
+
#
|
278
|
+
# Given
|
279
|
+
#
|
280
|
+
# repository = ArtRest::Repository.new('http://localhost:8081/artifactory/api/storage/libs-release-local', { ... })
|
281
|
+
#
|
282
|
+
# the expression
|
283
|
+
#
|
284
|
+
# folder = repository['/commons-lang/commons-lang/3.2.0']
|
285
|
+
#
|
286
|
+
# will return an ArtRest::Folder instance.
|
287
|
+
#
|
288
|
+
# * *Args* :
|
289
|
+
# [+relative_path+] The path of the resource to look up, relative to
|
290
|
+
# this resource
|
291
|
+
# [+block+] A block that will be transparently passed on to
|
292
|
+
# RestClient::Resource#initialize [optional]
|
293
|
+
# * *Returns* :
|
294
|
+
# - An ArtRest::Resource instance of appropriate type representing the
|
295
|
+
# Artifactory resource located at +relative_path+ relative to this resource
|
296
|
+
#
|
297
|
+
def [](relative_path, &new_block)
|
298
|
+
case
|
299
|
+
when block_given? then
|
300
|
+
ArtRest::Resource.create(concat_urls(url, relative_path), options, &new_block)
|
301
|
+
when block then
|
302
|
+
ArtRest::Resource.create(concat_urls(url, relative_path), options, &block)
|
303
|
+
else
|
304
|
+
ArtRest::Resource.create(concat_urls(url, relative_path), options)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# Hide method from RestClient::Resource by default since they
|
309
|
+
# may make no sense on a concrete subclass.
|
310
|
+
protected :get, :post, :put, :delete, :head, :patch
|
311
|
+
|
312
|
+
protected
|
313
|
+
|
314
|
+
# Post changes made to this resource's content/representation since
|
315
|
+
# loading it from Artifactory - if any - back to the server, i.e.
|
316
|
+
# *update* the resource this instance represents.
|
317
|
+
#
|
318
|
+
# === Example
|
319
|
+
#
|
320
|
+
# sysConfig = ArtRest::System::GeneralConfiguration.get
|
321
|
+
# sysConfig.content = ...
|
322
|
+
# sysConfig.update!
|
323
|
+
#
|
324
|
+
# * *Args* :
|
325
|
+
# - +additional_headers+ -> Any additional headers to be set on the
|
326
|
+
# POST request
|
327
|
+
#
|
328
|
+
def update!(additional_headers = {}, &block)
|
329
|
+
post(self.unparsed_content, additional_headers, &block)
|
330
|
+
end
|
331
|
+
|
332
|
+
# Take +content+ and make it this resource's new #content by calling
|
333
|
+
# #content= +content+. Subsequently, post this resource's changes back to
|
334
|
+
# Artifactory, i.e. *update* the resource this instance represents.
|
335
|
+
#
|
336
|
+
# Calling this method on a resource +res+ is therefore equivalent to
|
337
|
+
#
|
338
|
+
# res.content = content
|
339
|
+
# res.update!(additional_headers, &block)
|
340
|
+
#
|
341
|
+
# * *Args* :
|
342
|
+
# [+content+] This resource's new #content
|
343
|
+
# [+additional_headers+] Any additional headers to be set on this POST
|
344
|
+
# request that calling this method entails
|
345
|
+
# [+block+] A block that will be passed on to
|
346
|
+
# RestClient::Resource#post, i.e. it will handle
|
347
|
+
# the response to our POST request
|
348
|
+
#
|
349
|
+
def update_with!(content, additional_headers = {}, &block)
|
350
|
+
self.content = content
|
351
|
+
update!(additional_headers, &block)
|
352
|
+
end
|
353
|
+
|
354
|
+
private
|
355
|
+
|
356
|
+
def _content # :nodoc:
|
357
|
+
return @content if @content
|
358
|
+
raw = get
|
359
|
+
@content = case self.class.mime_type
|
360
|
+
when MIME::Types['application/json'] then JSON.parse(raw)
|
361
|
+
else raw
|
362
|
+
end
|
363
|
+
@content
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|