haveapi-go-client 0.29.5 → 0.29.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c01550d0855ee797d70262f9e58865b8ae4746a40f28bff0db9ae41753fa98d
4
- data.tar.gz: 8e4d6c3ae75b9f1372b83745d1fe1e410318ea0f97eefe95a667faaa6aaa0adc
3
+ metadata.gz: 87fc318cd53bb26a499a61928aabce8b28971e9d941dcf02f01748c8c4483d5f
4
+ data.tar.gz: 9dd0dfa4aea746dfb0845532cf7e01e24d660e1916029ad21ac6e1656366e6d1
5
5
  SHA512:
6
- metadata.gz: 653d4d280f32e875ba56c9142bd396013afcb806010da65cb192e8789f856f2fea6cae53e2e60330d359385e8616b46a8150820794c01099608ea11a44557b7e
7
- data.tar.gz: 5d269f12efb123784ea35b2431b37f5850f72a2e32de18ba91a14d3d64e221ed871e8dfc4091eff48bf4823cf69b6e81f39bcd6b6f5d82773a67854bf945d6ed
6
+ metadata.gz: 9c4d4996b2ea0f7d0697303c190f099542d1fed2672abbfeafc74e87518cfc57468e0a4dbb76ee4607e32678e5c72f8f89490e9cd990b3a000ffe4d863525c14
7
+ data.tar.gz: 8f5b45390fdb4e21ddb47e37e4d1fb68fbc105b2058fd22b48e321d0f352f3b236ab170468055acd4c80c210cb4c574699b9968976a482d0b429a6157a022d60
data/README.md CHANGED
@@ -76,4 +76,5 @@ api.SetLanguage("cs")
76
76
 
77
77
  The value is sent in `Accept-Language` by default. Use
78
78
  `SetLanguageHeader("X-Language")` when the API is configured with a custom
79
- language header.
79
+ language header. Read the configured values with `GetLanguage()` and
80
+ `GetLanguageHeader()`.
@@ -18,5 +18,5 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'haveapi-client', '~> 0.29.5'
21
+ spec.add_dependency 'haveapi-client', '~> 0.29.6'
22
22
  end
@@ -27,6 +27,10 @@ module HaveAPI::GoClient
27
27
  # @return [String]
28
28
  attr_reader :go_name
29
29
 
30
+ # Names of fields for the action and its aliases in the resource Go struct
31
+ # @return [Array<String>]
32
+ attr_reader :go_member_names
33
+
30
34
  # Data type for Go
31
35
  # @return [String]
32
36
  attr_reader :go_type
@@ -78,10 +82,31 @@ module HaveAPI::GoClient
78
82
  @blocking = desc[:blocking]
79
83
  end
80
84
 
81
- # Return action name with all aliases, camelized
85
+ # Return allocated action member names, including aliases.
82
86
  # @return [Array<String>]
83
87
  def all_names(&)
84
- ([go_name] + aliases.map { |v| camelize(v) }).uniq.each(&)
88
+ go_member_names.each(&)
89
+ end
90
+
91
+ # @return [String]
92
+ def go_member_name
93
+ go_member_names.first
94
+ end
95
+
96
+ # @param allocator [MemberNameAllocator]
97
+ def allocate_member_names(allocator)
98
+ @go_member_names = ([name] + aliases.map(&:to_s)).uniq.map do |raw_name|
99
+ allocator.allocate(
100
+ camelize(raw_name),
101
+ suffix: 'Action',
102
+ identity: [
103
+ 'action',
104
+ resource.resource_path.map(&:name).join('/'),
105
+ name,
106
+ raw_name
107
+ ].join(':')
108
+ )
109
+ end
85
110
  end
86
111
 
87
112
  # @return [Boolean]
@@ -116,7 +141,7 @@ module HaveAPI::GoClient
116
141
  end
117
142
 
118
143
  def <=>(other)
119
- go_name <=> other.go_name
144
+ [go_name, name] <=> [other.go_name, other.name]
120
145
  end
121
146
 
122
147
  protected
@@ -1,5 +1,37 @@
1
+ require 'haveapi/go_client/utils'
2
+
1
3
  module HaveAPI::GoClient
2
4
  class ApiVersion
5
+ CLIENT_MEMBERS = %w[
6
+ AllowOAuth2Origin
7
+ Authentication
8
+ DoBodyRequest
9
+ DoQueryStringRequest
10
+ GetLanguage
11
+ GetLanguageHeader
12
+ SetHTTPClient
13
+ SetLanguage
14
+ SetLanguageHeader
15
+ SetTimeout
16
+ Url
17
+ ].freeze
18
+
19
+ AUTHENTICATION_CLIENT_MEMBERS = {
20
+ basic: %w[
21
+ SetBasicAuthentication
22
+ ],
23
+ oauth2: %w[
24
+ RevokeAccessToken
25
+ SetExistingOAuth2Auth
26
+ ],
27
+ token: %w[
28
+ RevokeAuthToken
29
+ SetExistingTokenAuth
30
+ SetNewTokenAuth
31
+ SetTokenAuthMode
32
+ ]
33
+ }.freeze
34
+
3
35
  # @return [Array<Authentication::Base>]
4
36
  attr_reader :auth_methods
5
37
 
@@ -14,6 +46,10 @@ module HaveAPI::GoClient
14
46
  Resource.new(self, k, v)
15
47
  end.sort!
16
48
 
49
+ client_members = CLIENT_MEMBERS + desc[:authentication].keys.flat_map do |name|
50
+ AUTHENTICATION_CLIENT_MEMBERS.fetch(name.to_sym, [])
51
+ end
52
+ Resource.allocate_member_names(@resources, [], reserved: client_members)
17
53
  @resources.each(&:resolve_associations)
18
54
 
19
55
  @auth_methods = desc[:authentication].map do |k, v|
@@ -47,6 +47,11 @@ module HaveAPI::GoClient
47
47
  @request_action ||= resource.actions.detect { |a| a.name == 'request' }
48
48
  end
49
49
 
50
+ # @return [Action]
51
+ def revoke_action
52
+ @revoke_action ||= resource.actions.detect { |a| a.name == 'revoke' }
53
+ end
54
+
50
55
  # @return [Array<Action>]
51
56
  def custom_actions
52
57
  @custom_actions ||= resource.actions.reject do |a|
@@ -28,6 +28,10 @@ module HaveAPI::GoClient
28
28
  # @return [String]
29
29
  attr_reader :go_name
30
30
 
31
+ # Name of the field in the parent Go struct
32
+ # @return [String]
33
+ attr_reader :go_member_name
34
+
31
35
  # Type in Go
32
36
  # @return [String]
33
37
  attr_reader :go_type
@@ -55,6 +59,31 @@ module HaveAPI::GoClient
55
59
  @actions = desc[:actions].map do |k, v|
56
60
  Action.new(self, k.to_s, v, prefix:)
57
61
  end.sort!
62
+ self.class.allocate_member_names(@resources, @actions, reserved: %w[Client])
63
+ end
64
+
65
+ # @param resources [Array<Resource>]
66
+ # @param actions [Array<Action>]
67
+ # @param reserved [Array<String>]
68
+ def self.allocate_member_names(resources, actions, reserved:)
69
+ allocator = MemberNameAllocator.new(reserved)
70
+
71
+ resources.each do |resource|
72
+ resource.assign_go_member_name(
73
+ allocator.allocate(
74
+ resource.go_name,
75
+ suffix: 'Resource',
76
+ identity: "resource:#{resource.resource_path.map(&:name).join('/')}"
77
+ )
78
+ )
79
+ end
80
+
81
+ actions.each { |action| action.allocate_member_names(allocator) }
82
+ end
83
+
84
+ # @param name [String]
85
+ def assign_go_member_name(name)
86
+ @go_member_name = name
58
87
  end
59
88
 
60
89
  # @return [ApiVersion]
@@ -112,7 +141,7 @@ module HaveAPI::GoClient
112
141
  end
113
142
 
114
143
  def <=>(other)
115
- go_name <=> other.go_name
144
+ [go_name, name] <=> [other.go_name, other.name]
116
145
  end
117
146
 
118
147
  protected
@@ -2,6 +2,48 @@ require 'digest/sha1'
2
2
  require 'json'
3
3
 
4
4
  module HaveAPI::GoClient
5
+ class MemberNameAllocator
6
+ def initialize(reserved = [])
7
+ @used = reserved.to_h { |name| [name, true] }
8
+ end
9
+
10
+ # Allocate a Go struct member name while preserving the preferred name
11
+ # whenever it does not collide with another field or method.
12
+ #
13
+ # @param preferred [String]
14
+ # @param suffix [String]
15
+ # @param identity [String]
16
+ # @return [String]
17
+ def allocate(preferred, suffix:, identity:)
18
+ return use(preferred) unless @used[preferred]
19
+
20
+ suffixed = "#{preferred}#{suffix}"
21
+ return use(suffixed) unless @used[suffixed]
22
+
23
+ digest = Digest::SHA1.hexdigest(identity)
24
+
25
+ (8..digest.length).each do |length|
26
+ candidate = "#{suffixed}_#{digest[0, length]}"
27
+ return use(candidate) unless @used[candidate]
28
+ end
29
+
30
+ index = 2
31
+ loop do
32
+ candidate = "#{suffixed}_#{digest}_#{index}"
33
+ return use(candidate) unless @used[candidate]
34
+
35
+ index += 1
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ def use(name)
42
+ @used[name] = true
43
+ name
44
+ end
45
+ end
46
+
5
47
  module Utils
6
48
  GO_KEYWORDS = %w[
7
49
  break case chan const continue default defer else fallthrough for func go
@@ -1,5 +1,5 @@
1
1
  module HaveAPI
2
2
  module GoClient
3
- VERSION = '0.29.5'.freeze
3
+ VERSION = '0.29.6'.freeze
4
4
  end
5
5
  end
@@ -230,6 +230,12 @@ RSpec.describe HaveAPI::GoClient::Generator do
230
230
  if err := c.SetLanguageHeader("X-Language"); err != nil {
231
231
  t.Fatalf("set language header failed: %v", err)
232
232
  }
233
+ if got := c.GetLanguage(); got != "cs-CZ" {
234
+ t.Fatalf("expected configured language cs-CZ, got %q", got)
235
+ }
236
+ if got := c.GetLanguageHeader(); got != "X-Language" {
237
+ t.Fatalf("expected configured language header X-Language, got %q", got)
238
+ }
233
239
 
234
240
  httpReq, err := http.NewRequest("GET", "#{base_url}/v1/test", nil)
235
241
  if err != nil {
@@ -444,6 +450,144 @@ RSpec.describe HaveAPI::GoClient::Generator do
444
450
  end
445
451
  end
446
452
 
453
+ it 'allocates resource and action members without hiding client configuration' do
454
+ description = oauth2_revoke_description
455
+ description[:resources].merge!(
456
+ language: {
457
+ resources: {},
458
+ actions: {}
459
+ },
460
+ set_language: {
461
+ resources: {},
462
+ actions: {}
463
+ },
464
+ get_language: {
465
+ resources: {},
466
+ actions: {}
467
+ },
468
+ url: {
469
+ resources: {},
470
+ actions: {}
471
+ },
472
+ authentication: {
473
+ resources: {},
474
+ actions: {}
475
+ },
476
+ revoke_access_token: {
477
+ resources: {},
478
+ actions: {}
479
+ },
480
+ widget: {
481
+ resources: {
482
+ client: {
483
+ resources: {},
484
+ actions: {}
485
+ },
486
+ list: {
487
+ resources: {},
488
+ actions: {}
489
+ }
490
+ },
491
+ actions: {
492
+ run: action_description(
493
+ method: 'GET',
494
+ path: '/widgets/run',
495
+ aliases: %w[client list]
496
+ )
497
+ }
498
+ }
499
+ )
500
+
501
+ Dir.mktmpdir('haveapi-go-client-member-names-') do |dir|
502
+ communicator = instance_double(
503
+ HaveAPI::Client::Communicator,
504
+ describe_api: description
505
+ )
506
+ allow(HaveAPI::Client::Communicator).to receive(:new).and_return(communicator)
507
+
508
+ generator = described_class.new(
509
+ 'http://unused.example',
510
+ dir,
511
+ module: 'example.com/haveapi-member-names',
512
+ package: 'client'
513
+ )
514
+ generator.generate
515
+ generator.go_fmt
516
+
517
+ File.write(File.join(dir, 'client', 'member_names_test.go'), <<~GO)
518
+ package client
519
+
520
+ import "testing"
521
+
522
+ func TestCollisionSafeMemberNames(t *testing.T) {
523
+ c := New("http://unused.example")
524
+
525
+ if c.Language == nil {
526
+ t.Fatalf("language resource is not initialized")
527
+ }
528
+ if c.SetLanguageResource == nil || c.GetLanguageResource == nil {
529
+ t.Fatalf("language method collision resources are not initialized")
530
+ }
531
+ if c.UrlResource == nil || c.AuthenticationResource == nil {
532
+ t.Fatalf("fixed client member collision resources are not initialized")
533
+ }
534
+ if c.RevokeAccessTokenResource == nil {
535
+ t.Fatalf("OAuth2 method collision resource is not initialized")
536
+ }
537
+ if c.Widget.ClientResource == nil || c.Widget.List == nil {
538
+ t.Fatalf("nested resources are not initialized")
539
+ }
540
+ if c.Widget.Run != c.Widget.ClientAction {
541
+ t.Fatalf("client alias does not reference the run action")
542
+ }
543
+ if c.Widget.Run != c.Widget.ListAction {
544
+ t.Fatalf("list alias does not reference the run action")
545
+ }
546
+
547
+ if err := c.SetLanguage("cs-CZ"); err != nil {
548
+ t.Fatalf("set language failed: %v", err)
549
+ }
550
+ if err := c.SetLanguageHeader("X-Language"); err != nil {
551
+ t.Fatalf("set language header failed: %v", err)
552
+ }
553
+ if c.GetLanguage() != "cs-CZ" || c.GetLanguageHeader() != "X-Language" {
554
+ t.Fatalf("language configuration was not retained")
555
+ }
556
+ }
557
+ GO
558
+
559
+ go_out, go_err, go_status = Open3.capture3(
560
+ { 'CGO_ENABLED' => '0' },
561
+ 'go',
562
+ 'test',
563
+ './...',
564
+ chdir: dir
565
+ )
566
+ expect(go_status).to be_success, "go test failed: #{go_out}\n#{go_err}"
567
+ end
568
+ end
569
+
570
+ it 'reserves only authentication methods emitted for the described API' do
571
+ description = oauth2_revoke_description
572
+ description[:resources][:revoke_access_token] = {
573
+ resources: {},
574
+ actions: {}
575
+ }
576
+
577
+ with_oauth2 = HaveAPI::GoClient::ApiVersion.new(description)
578
+ oauth2_resource = with_oauth2.resources.detect do |resource|
579
+ resource.name == 'revoke_access_token'
580
+ end
581
+ expect(oauth2_resource.go_member_name).to eq('RevokeAccessTokenResource')
582
+
583
+ description[:authentication] = {}
584
+ without_oauth2 = HaveAPI::GoClient::ApiVersion.new(description)
585
+ plain_resource = without_oauth2.resources.detect do |resource|
586
+ resource.name == 'revoke_access_token'
587
+ end
588
+ expect(plain_resource.go_member_name).to eq('RevokeAccessToken')
589
+ end
590
+
447
591
  it 'generates an OAuth2 client that sends the revoke token as form data' do
448
592
  Dir.mktmpdir('haveapi-go-client-oauth2-') do |dir|
449
593
  communicator = instance_double(
@@ -53,7 +53,7 @@ func (client *Client) SetNewTokenAuth(options *TokenAuthOptions) error {
53
53
  auth := &TokenAuth{Resource: resource, Mode: HttpHeader}
54
54
  auth.setDefaultOptions(options)
55
55
 
56
- request := resource.Request.Prepare()
56
+ request := resource.<%= auth.request_action.go_member_name %>.Prepare()
57
57
  request.SetInput(&AuthTokenActionTokenRequestInput{
58
58
  <% auth.request_action.input.parameters.each do |p| -%>
59
59
  <%= p.go_name %>: options.<%= p.go_name %>,
@@ -98,7 +98,7 @@ func (auth *TokenAuth) setDefaultOptions(options *TokenAuthOptions) {
98
98
  func (auth *TokenAuth) nextAuthenticationStep(options *TokenAuthOptions, action string, token string) error {
99
99
  <% auth.custom_actions.each do |a| -%>
100
100
  if action == <%= go_string_literal(a.name) %> {
101
- request := auth.Resource.<%= a.go_name %>.Prepare()
101
+ request := auth.Resource.<%= a.go_member_name %>.Prepare()
102
102
  input := request.NewInput()
103
103
  input.SetToken(token)
104
104
 
@@ -162,7 +162,7 @@ func (client *Client) SetTokenAuthMode(mode Mode) {
162
162
  // RevokeAuthToken will revoke the authentication token and remove authentication
163
163
  // from the client
164
164
  func (client *Client) RevokeAuthToken() error {
165
- revoke := client.Authentication.(*TokenAuth).Resource.Revoke.Prepare()
165
+ revoke := client.Authentication.(*TokenAuth).Resource.<%= auth.revoke_action.go_member_name %>.Prepare()
166
166
  resp, err := revoke.Call()
167
167
 
168
168
  if err != nil {
@@ -13,18 +13,14 @@ type Client struct {
13
13
  // Options for authentication method
14
14
  Authentication Authenticator
15
15
 
16
- // Language sent to the API server, e.g. "cs" or "cs-CZ".
17
- Language string
18
-
19
- // HTTP header used to send Language. Defaults to Accept-Language.
20
- LanguageHeader string
21
-
22
16
  httpClient *http.Client
23
17
  oauth2TrustedOrigins map[string]struct{}
18
+ language string
19
+ languageHeader string
24
20
 
25
21
  <% api.resources.each do |r| -%>
26
22
  // Resource <%= go_comment_text(r.full_dot_name) %>
27
- <%= r.go_name %> *<%= r.go_type %>
23
+ <%= r.go_member_name %> *<%= r.go_type %>
28
24
  <% end -%>
29
25
  }
30
26
 
@@ -36,7 +32,7 @@ func New(url string) *Client {
36
32
  }
37
33
 
38
34
  <% api.resources.each do |r| -%>
39
- c.<%= r.go_name %> = New<%= r.go_type %>(c)
35
+ c.<%= r.go_member_name %> = New<%= r.go_type %>(c)
40
36
  <% end -%>
41
37
 
42
38
  return c
data/template/i18n.go.erb CHANGED
@@ -33,10 +33,15 @@ func (client *Client) SetLanguage(language string) error {
33
33
  return err
34
34
  }
35
35
 
36
- client.Language = language
36
+ client.language = language
37
37
  return nil
38
38
  }
39
39
 
40
+ // GetLanguage returns the configured language request header value.
41
+ func (client *Client) GetLanguage() string {
42
+ return client.language
43
+ }
44
+
40
45
  // SetLanguageHeader configures the HTTP header used by SetLanguage.
41
46
  func (client *Client) SetLanguageHeader(header string) error {
42
47
  if !validHeaderName(header) {
@@ -47,30 +52,35 @@ func (client *Client) SetLanguageHeader(header string) error {
47
52
  return err
48
53
  }
49
54
 
50
- client.LanguageHeader = header
55
+ client.languageHeader = header
51
56
  return nil
52
57
  }
53
58
 
59
+ // GetLanguageHeader returns the configured language header name.
60
+ func (client *Client) GetLanguageHeader() string {
61
+ return client.languageHeader
62
+ }
63
+
54
64
  func (client *Client) addLanguageHeader(req *http.Request) {
55
- if client == nil || req == nil || client.Language == "" {
65
+ if client == nil || req == nil || client.language == "" {
56
66
  return
57
67
  }
58
68
 
59
- req.Header.Set(client.languageHeaderName(), client.Language)
69
+ req.Header.Set(client.languageHeaderName(), client.language)
60
70
  }
61
71
 
62
72
  func (client *Client) languageHeaderName() string {
63
- if client == nil || client.LanguageHeader == "" {
73
+ if client == nil || client.languageHeader == "" {
64
74
  return defaultLanguageHeader
65
75
  }
66
76
 
67
- return client.LanguageHeader
77
+ return client.languageHeader
68
78
  }
69
79
 
70
80
  func t(client *Client, key string, values map[string]string) string {
71
81
  language := ""
72
82
  if client != nil {
73
- language = client.Language
83
+ language = client.language
74
84
  }
75
85
 
76
86
  return tLanguage(language, key, values)
@@ -7,7 +7,7 @@ type <%= resource.go_type %> struct {
7
7
 
8
8
  <% resource.resources.each do |r| -%>
9
9
  // Resource <%= go_comment_text(r.full_dot_name) %>
10
- <%= r.go_name %> *<%= r.go_type %>
10
+ <%= r.go_member_name %> *<%= r.go_type %>
11
11
  <% end -%>
12
12
  <% resource.actions.each do |a| -%>
13
13
  <% a.all_names do |go_name| -%>
@@ -25,7 +25,7 @@ func New<%= resource.go_type %>(client *Client) *<%= resource.go_type %> {
25
25
  return &<%= resource.go_type %>{
26
26
  Client: client,
27
27
  <% resource.resources.each do |r| -%>
28
- <%= r.go_name %>: New<%= r.go_type %>(client),
28
+ <%= r.go_member_name %>: New<%= r.go_type %>(client),
29
29
  <% end -%>
30
30
  <% resource.actions.each do |a| -%>
31
31
  <% a.all_names do |go_name| -%>
@@ -39,8 +39,8 @@ func NewValidationError(client ...*Client) *ValidationError {
39
39
  Errors: make(map[string][]string),
40
40
  }
41
41
 
42
- if len(client) > 0 && client[0] != nil && client[0].Language != "" {
43
- validationErrorLanguages.Store(validationErrorKey(ret), client[0].Language)
42
+ if len(client) > 0 && client[0] != nil && client[0].language != "" {
43
+ validationErrorLanguages.Store(validationErrorKey(ret), client[0].language)
44
44
  runtime.SetFinalizer(ret, func(e *ValidationError) {
45
45
  validationErrorLanguages.Delete(validationErrorKey(e))
46
46
  })
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haveapi-go-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.29.5
4
+ version: 0.29.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Skokan
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 0.29.5
18
+ version: 0.29.6
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 0.29.5
25
+ version: 0.29.6
26
26
  description: Go client generator
27
27
  email:
28
28
  - jakub.skokan@vpsfree.cz