artrest 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|