universal-git-client 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,123 @@
1
+ require 'universal-git-client/http/base'
2
+
3
+ module UniversalGitClient
4
+ module Http
5
+ class Github < Base
6
+ def user
7
+ with_response_validation! do
8
+ self.class.get('/user', default_options)
9
+ end
10
+ end
11
+
12
+ def organizations(page: 1, per_page: nil)
13
+ with_response_validation! do
14
+ self.class.get(
15
+ '/user/orgs',
16
+ default_options.merge(
17
+ query: {
18
+ page: page,
19
+ per_page: per_page || default_elements_per_page,
20
+ },
21
+ )
22
+ )
23
+ end
24
+ end
25
+
26
+ def user_repos(page: 1, per_page: nil)
27
+ with_response_validation! do
28
+ self.class.get(
29
+ '/user/repos',
30
+ default_options.merge(
31
+ query: {
32
+ affiliation: 'owner',
33
+ page: page,
34
+ per_page: per_page || default_elements_per_page,
35
+ },
36
+ )
37
+ )
38
+ end
39
+ end
40
+
41
+ def orga_repos(organization:, page: 1, per_page: nil)
42
+ # TODO: Handle permissions
43
+ with_response_validation! do
44
+ self.class.get(
45
+ "/orgs/#{organization}/repos",
46
+ default_options.merge(
47
+ query: {
48
+ page: page,
49
+ per_page: per_page || default_elements_per_page,
50
+ },
51
+ )
52
+ )
53
+ end
54
+ end
55
+
56
+ def repository(owner:, repo:)
57
+ with_response_validation! do
58
+ self.class.get("/repos/#{owner}/#{repo}", default_options)
59
+ end
60
+ end
61
+
62
+ def branches(owner:, repo:, page: 1, per_page: nil)
63
+ with_response_validation! do
64
+ self.class.get(
65
+ "/repos/#{owner}/#{repo}/branches",
66
+ default_options.merge(
67
+ query: {
68
+ page: page,
69
+ per_page: per_page || default_elements_per_page,
70
+ },
71
+ )
72
+ )
73
+ end
74
+ end
75
+
76
+ def branch(owner:, repo:, branch:)
77
+ with_response_validation! do
78
+ self.class.get(
79
+ "/repos/#{owner}/#{repo}/branches/#{branch}",
80
+ default_options
81
+ )
82
+ end
83
+ end
84
+
85
+ def download_repo_archive(owner:, repo:, branch: nil)
86
+ path = "#{base_url}/repos/#{owner}/#{repo}/zipball"
87
+ path << "/#{branch}" if branch
88
+ Down.download(path, down_default_options)
89
+ end
90
+
91
+ def setup_repo_webhook(owner:, repo:, webhook_url:, webhook_secret: nil)
92
+ with_response_validation! do
93
+ self.class.post(
94
+ "/repos/#{owner}/#{repo}/hooks",
95
+ default_options.merge(
96
+ body: {
97
+ name: 'web',
98
+ active: true,
99
+ events: [
100
+ 'push',
101
+ ],
102
+ config: {
103
+ url: webhook_url,
104
+ secret: webhook_secret,
105
+ content_type: 'json',
106
+ }.compact,
107
+ }.to_json,
108
+ )
109
+ )
110
+ end
111
+ end
112
+
113
+ def delete_repo_webhook(owner:, repo:, webhook_id:)
114
+ with_response_validation! do
115
+ self.class.delete(
116
+ "/repos/#{owner}/#{repo}/hooks/#{webhook_id}",
117
+ default_options
118
+ )
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,130 @@
1
+ require 'universal-git-client/http/base'
2
+ require 'uri'
3
+
4
+ module UniversalGitClient
5
+ module Http
6
+ class Gitlab < Base
7
+ def user
8
+ with_response_validation! do
9
+ self.class.get('/user', default_options)
10
+ end
11
+ end
12
+
13
+ def organizations(page: 1, per_page: nil)
14
+ with_response_validation! do
15
+ self.class.get(
16
+ '/groups',
17
+ default_options.merge(
18
+ query: {
19
+ page: page,
20
+ per_page: per_page || default_elements_per_page,
21
+ },
22
+ )
23
+ )
24
+ end
25
+ end
26
+
27
+ def user_repos(page: 1, per_page: nil)
28
+ current_user_id = self.user['id']
29
+ with_response_validation! do
30
+ self.class.get(
31
+ "/users/#{current_user_id}/projects",
32
+ default_options.merge(
33
+ query: {
34
+ page: page,
35
+ per_page: per_page || default_elements_per_page,
36
+ },
37
+ )
38
+ )
39
+ end
40
+ end
41
+
42
+ def orga_repos(organization:, page: 1, per_page: nil)
43
+ encoded_namespace = URI.encode_www_form_component(organization)
44
+ with_response_validation! do
45
+ self.class.get(
46
+ "/groups/#{encoded_namespace}/projects",
47
+ default_options.merge(
48
+ query: {
49
+ page: page,
50
+ per_page: per_page || default_elements_per_page,
51
+ },
52
+ )
53
+ )
54
+ end
55
+ end
56
+
57
+ def repository(owner:, repo:)
58
+ encoded_namespace = encode_namespace(owner, repo)
59
+ with_response_validation! do
60
+ self.class.get("/projects/#{encoded_namespace}", default_options)
61
+ end
62
+ end
63
+
64
+ def branches(owner:, repo:, page: 1, per_page: nil)
65
+ encoded_namespace = encode_namespace(owner, repo)
66
+ with_response_validation! do
67
+ self.class.get(
68
+ "/projects/#{encoded_namespace}/repository/branches",
69
+ default_options.merge(
70
+ query: {
71
+ page: page,
72
+ per_page: per_page || default_elements_per_page,
73
+ },
74
+ )
75
+ )
76
+ end
77
+ end
78
+
79
+ def branch(owner:, repo:, branch:)
80
+ encoded_namespace = encode_namespace(owner, repo)
81
+ with_response_validation! do
82
+ self.class.get(
83
+ "/projects/#{encoded_namespace}/repository/branches/#{branch}",
84
+ default_options
85
+ )
86
+ end
87
+ end
88
+
89
+ def download_repo_archive(owner:, repo:, branch: nil)
90
+ encoded_namespace = encode_namespace(owner, repo)
91
+ path = "#{base_url}/projects/#{encoded_namespace}/repository/archive.zip"
92
+ path << "?sha=#{branch}" if branch
93
+ Down.download(path, down_default_options)
94
+ end
95
+
96
+ def setup_repo_webhook(owner:, repo:, webhook_url:, webhook_secret: nil)
97
+ encoded_namespace = encode_namespace(owner, repo)
98
+ with_response_validation! do
99
+ self.class.post(
100
+ "/projects/#{encoded_namespace}/hooks",
101
+ default_options.merge(
102
+ body: {
103
+ url: webhook_url,
104
+ token: webhook_secret,
105
+ push_events: true,
106
+ enable_ssl_verification: true,
107
+ }.compact.to_json,
108
+ )
109
+ )
110
+ end
111
+ end
112
+
113
+ def delete_repo_webhook(owner:, repo:, webhook_id:)
114
+ encoded_namespace = encode_namespace(owner, repo)
115
+ with_response_validation! do
116
+ self.class.delete(
117
+ "/projects/#{encoded_namespace}/hooks/#{webhook_id}",
118
+ default_options
119
+ )
120
+ end
121
+ end
122
+
123
+ private
124
+
125
+ def encode_namespace(owner, repo)
126
+ URI.encode_www_form_component("#{owner}/#{repo}")
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,14 @@
1
+ module UniversalGitClient
2
+ module Http
3
+ module Helpers
4
+ class DummyResponse
5
+ def self.response(request: nil, response: nil, body: {}, options: {})
6
+ request ||= HTTParty::Request.new(Net::HTTP::Get, '/', options)
7
+ response ||= Net::HTTPSuccess.new('1.1', 200, 'Success')
8
+
9
+ HTTParty::Response.new(request, response, -> { '' }, body: body.to_json)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ require 'universal-git-client/errors'
2
+
3
+ module UniversalGitClient
4
+ module Http
5
+ module ResponseValidation
6
+ def with_response_validation!
7
+ response = yield
8
+ case response.code
9
+ when 200..399 then response
10
+ when 400 then raise Errors::BadRequest.new('BadRequest', response)
11
+ when 401 then raise Errors::Unauthorized.new('Unauthorized', response)
12
+ when 403 then raise Errors::Forbidden.new('Forbidden', response)
13
+ when 404 then raise Errors::NotFound.new('NotFound', response)
14
+ when 405 then raise Errors::MethodNotAllowed.new('MethodNotAllowed', response)
15
+ when 406 then raise Errors::NotAcceptable.new('NotAcceptable', response)
16
+ when 409 then raise Errors::Conflict.new('Conflict', response)
17
+ when 415 then raise Errors::UnsupportedMediaType.new('UnsupportedMediaType', response)
18
+ when 422 then raise Errors::UnprocessableEntity.new('UnprocessableEntity', response)
19
+ when 400..499 then raise Errors::ClientError.new('ClientError', response)
20
+ when 500 then raise Errors::InternalServerError.new('InternalServerError', response)
21
+ when 502 then raise Errors::BadGateway.new('BadGateway', response)
22
+ when 503 then raise Errors::ServiceUnavailable.new('ServiceUnavailable', response)
23
+ when 500..599 then raise Errors::ServerError.new('ServerError', response)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,103 @@
1
+ require 'json'
2
+ require 'fast_jsonapi'
3
+ require 'deep_merge'
4
+ require 'uri'
5
+ require 'nitlink'
6
+
7
+ module UniversalGitClient
8
+ module Normalizers
9
+ class Base
10
+ attr_reader :response, :resource
11
+
12
+ def initialize(response, resource)
13
+ @response = response
14
+ @resource = resource
15
+ end
16
+
17
+ def normalize
18
+ object = body_as_object
19
+ serializer = serializer_for_resource
20
+ serializer_options = {}
21
+ if object.is_a?(Array)
22
+ serializer_options.deep_merge!(pagination_options)
23
+ end
24
+ serializer.new(object, serializer_options)
25
+ end
26
+
27
+ def serializer_for_resource
28
+ self.class.const_get("#{resource}Serializer")
29
+ end
30
+
31
+ def body_as_object
32
+ @body_as_object ||= begin
33
+ struct = JSON.parse(response.body, object_class: OpenStruct)
34
+ if !struct.is_a?(Array) && struct.values
35
+ # Bitbucket embeds collections inside values
36
+ struct.values
37
+ else
38
+ struct
39
+ end
40
+ end
41
+ end
42
+
43
+ def pagination_options
44
+ {
45
+ meta: {
46
+ pagination: {
47
+ self: current_page_index,
48
+ first: first_page_index,
49
+ prev: prev_page_index,
50
+ next: next_page_index,
51
+ last: last_page_index,
52
+ per_page: per_page_index,
53
+ },
54
+ },
55
+ }
56
+ end
57
+
58
+ def current_page_index
59
+ response.request.options[:query][:page].to_s
60
+ rescue StandardError
61
+ nil
62
+ end
63
+
64
+ def first_page_index
65
+ fetch_rel_value('first')
66
+ end
67
+
68
+ def prev_page_index
69
+ fetch_rel_value('prev')
70
+ end
71
+
72
+ def next_page_index
73
+ fetch_rel_value('next')
74
+ end
75
+
76
+ def last_page_index
77
+ fetch_rel_value('last')
78
+ end
79
+
80
+ def per_page_index
81
+ response.request.options[:query][:per_page].to_s
82
+ rescue StandardError
83
+ nil
84
+ end
85
+
86
+ private
87
+
88
+ def fetch_rel_value(rel)
89
+ Hash[URI::decode_www_form(links.by_rel(rel).target.query)]['page']
90
+ rescue StandardError
91
+ nil
92
+ end
93
+
94
+ def links
95
+ @links ||= Nitlink::Parser.new.parse(response)
96
+ end
97
+ end
98
+
99
+ class BaseSerializer
100
+ include FastJsonapi::ObjectSerializer
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,93 @@
1
+ require 'universal-git-client/normalizers/base'
2
+
3
+ module UniversalGitClient
4
+ module Normalizers
5
+ class Bitbucket < Base
6
+ def first_page_index
7
+ '1'
8
+ end
9
+
10
+ def prev_page_index
11
+ get_page_index('previous')
12
+ end
13
+
14
+ def next_page_index
15
+ get_page_index('next')
16
+ end
17
+
18
+ def last_page_index
19
+ nil
20
+ end
21
+
22
+ def per_page_index
23
+ response.request.options[:query][:pagelen].to_s
24
+ rescue StandardError
25
+ nil
26
+ end
27
+
28
+ private
29
+
30
+ def get_page_index(rel)
31
+ Hash[
32
+ URI::decode_www_form(
33
+ URI.parse(response[rel]).query
34
+ )
35
+ ]['page']
36
+ rescue StandardError
37
+ nil
38
+ end
39
+
40
+ class UserSerializer < BaseSerializer
41
+ set_id :account_id
42
+ attribute :login, &:username
43
+ attribute :name, &:display_name
44
+
45
+ attribute :avatar_url do |object|
46
+ object.links.avatar.href
47
+ end
48
+ end
49
+
50
+ class OrganizationSerializer < BaseSerializer
51
+ set_id :uuid
52
+ attribute :login, &:username
53
+
54
+ attribute :avatar_url do |object|
55
+ object.links.avatar.href
56
+ end
57
+ end
58
+
59
+ class RepositorySerializer < BaseSerializer
60
+ set_id :uuid
61
+ attributes :name, :full_name
62
+ attribute :full_path, &:full_name
63
+ attribute :private, &:is_private
64
+ attribute :archived, &:is_archived
65
+
66
+ attribute :default_branch do |object|
67
+ object.mainbranch&.name
68
+ end
69
+ end
70
+
71
+ class BranchSerializer < BaseSerializer
72
+ set_id :name
73
+ attributes :name, :protected
74
+
75
+ attribute :commit do |object|
76
+ object.target.to_h[:hash]
77
+ end
78
+ end
79
+
80
+ class WebhookSerializer < BaseSerializer
81
+ set_id :uuid
82
+ attributes :url, :active
83
+ attribute :events do |object|
84
+ events = []
85
+ events << 'push' if object.events.include?('repo:push')
86
+ end
87
+ attribute :type do |object|
88
+ object.subject.type.capitalize
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end