scalingo 3.4.0 → 4.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (282) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependabot.yml +17 -0
  3. data/.github/workflows/publish.yml +6 -3
  4. data/.github/workflows/ruby.yml +3 -3
  5. data/.rubocop.yml +46 -17
  6. data/CHANGELOG.md +20 -1
  7. data/Gemfile +2 -0
  8. data/README.md +13 -62
  9. data/bin/console +11 -1
  10. data/bin/lint +2 -0
  11. data/bin/setup +21 -6
  12. data/bin/specs +2 -0
  13. data/lib/scalingo/api/client.rb +26 -53
  14. data/lib/scalingo/api/endpoint.rb +88 -11
  15. data/lib/scalingo/auth/keys.rb +4 -50
  16. data/lib/scalingo/auth/scm_integrations.rb +4 -51
  17. data/lib/scalingo/auth/tokens.rb +5 -72
  18. data/lib/scalingo/auth/two_factor_auth.rb +4 -55
  19. data/lib/scalingo/auth/user.rb +3 -38
  20. data/lib/scalingo/auth.rb +1 -1
  21. data/lib/scalingo/bearer_token.rb +16 -9
  22. data/lib/scalingo/billing/profile.rb +3 -40
  23. data/lib/scalingo/billing.rb +1 -1
  24. data/lib/scalingo/client.rb +21 -31
  25. data/lib/scalingo/configuration.rb +3 -27
  26. data/lib/scalingo/core_client.rb +9 -30
  27. data/lib/scalingo/database/backups.rb +9 -0
  28. data/lib/scalingo/database/databases.rb +8 -0
  29. data/lib/scalingo/database.rb +13 -0
  30. data/lib/scalingo/faraday/extract_meta.rb +33 -0
  31. data/lib/scalingo/faraday/extract_root_value.rb +18 -0
  32. data/lib/scalingo/faraday/response.rb +36 -0
  33. data/lib/scalingo/regional/addons.rb +18 -130
  34. data/lib/scalingo/regional/apps.rb +10 -103
  35. data/lib/scalingo/regional/autoscalers.rb +5 -64
  36. data/lib/scalingo/regional/collaborators.rb +4 -51
  37. data/lib/scalingo/regional/containers.rb +4 -51
  38. data/lib/scalingo/regional/deployments.rb +3 -38
  39. data/lib/scalingo/regional/domains.rb +5 -64
  40. data/lib/scalingo/regional/environment.rb +6 -77
  41. data/lib/scalingo/regional/events.rb +5 -50
  42. data/lib/scalingo/regional/logs.rb +10 -28
  43. data/lib/scalingo/regional/metrics.rb +2 -34
  44. data/lib/scalingo/regional/notifiers.rb +7 -90
  45. data/lib/scalingo/regional/operations.rb +5 -18
  46. data/lib/scalingo/regional/scm_repo_links.rb +8 -103
  47. data/lib/scalingo/regional.rb +1 -1
  48. data/lib/scalingo/token_holder.rb +1 -46
  49. data/lib/scalingo/version.rb +1 -1
  50. data/scalingo.gemspec +7 -7
  51. metadata +63 -285
  52. data/lib/scalingo/api/response.rb +0 -69
  53. data/lib/scalingo/regional_database/backups.rb +0 -44
  54. data/lib/scalingo/regional_database/databases.rb +0 -31
  55. data/lib/scalingo/regional_database.rb +0 -13
  56. data/samples/auth/keys/_meta.json +0 -13
  57. data/samples/auth/keys/all-200.json +0 -62
  58. data/samples/auth/keys/create-201.json +0 -67
  59. data/samples/auth/keys/create-422.json +0 -34
  60. data/samples/auth/keys/destroy-204.json +0 -19
  61. data/samples/auth/keys/destroy-404.json +0 -19
  62. data/samples/auth/keys/show-200.json +0 -60
  63. data/samples/auth/keys/show-404.json +0 -19
  64. data/samples/auth/scm_integrations/_meta.json +0 -14
  65. data/samples/auth/scm_integrations/all-200.json +0 -41
  66. data/samples/auth/scm_integrations/create-201.json +0 -41
  67. data/samples/auth/scm_integrations/create-422.json +0 -36
  68. data/samples/auth/scm_integrations/destroy-204.json +0 -15
  69. data/samples/auth/scm_integrations/destroy-404.json +0 -23
  70. data/samples/auth/scm_integrations/show-200.json +0 -34
  71. data/samples/auth/scm_integrations/show-404.json +0 -23
  72. data/samples/auth/tokens/_meta.json +0 -13
  73. data/samples/auth/tokens/all-200.json +0 -32
  74. data/samples/auth/tokens/create-201.json +0 -37
  75. data/samples/auth/tokens/destroy-204.json +0 -19
  76. data/samples/auth/tokens/destroy-404.json +0 -19
  77. data/samples/auth/tokens/exchange-200.json +0 -25
  78. data/samples/auth/tokens/exchange-401.json +0 -24
  79. data/samples/auth/tokens/renew-200.json +0 -32
  80. data/samples/auth/tokens/renew-404.json +0 -20
  81. data/samples/auth/two_factor_auth/_meta.json +0 -10
  82. data/samples/auth/two_factor_auth/disable-not-initiated.json +0 -23
  83. data/samples/auth/two_factor_auth/disable-success.json +0 -29
  84. data/samples/auth/two_factor_auth/initiate-already-enabled.json +0 -29
  85. data/samples/auth/two_factor_auth/initiate-success.json +0 -36
  86. data/samples/auth/two_factor_auth/initiate-wrong-provider.json +0 -29
  87. data/samples/auth/two_factor_auth/status.json +0 -29
  88. data/samples/auth/two_factor_auth/validate-not-initiated.json +0 -29
  89. data/samples/auth/two_factor_auth/validate-success.json +0 -49
  90. data/samples/auth/two_factor_auth/validate-wrong.json +0 -29
  91. data/samples/auth/user/_meta.json +0 -10
  92. data/samples/auth/user/self.json +0 -54
  93. data/samples/auth/user/stop-free-trial.json +0 -24
  94. data/samples/auth/user/update-200.json +0 -59
  95. data/samples/auth/user/update-422.json +0 -33
  96. data/samples/billing/profile/_meta.json +0 -23
  97. data/samples/billing/profile/create-201.json +0 -50
  98. data/samples/billing/profile/create-400.json +0 -27
  99. data/samples/billing/profile/create-422.json +0 -44
  100. data/samples/billing/profile/show-200.json +0 -41
  101. data/samples/billing/profile/show-404.json +0 -22
  102. data/samples/billing/profile/update-200.json +0 -47
  103. data/samples/billing/profile/update-422.json +0 -32
  104. data/samples/regional/addons/_meta.json +0 -22
  105. data/samples/regional/addons/categories-guest.json +0 -36
  106. data/samples/regional/addons/categories-logged.json +0 -37
  107. data/samples/regional/addons/destroy-204.json +0 -19
  108. data/samples/regional/addons/destroy-404.json +0 -24
  109. data/samples/regional/addons/find-200.json +0 -48
  110. data/samples/regional/addons/find-404.json +0 -24
  111. data/samples/regional/addons/for-200.json +0 -50
  112. data/samples/regional/addons/providers-guest.json +0 -588
  113. data/samples/regional/addons/providers-logged.json +0 -705
  114. data/samples/regional/addons/provision-201.json +0 -58
  115. data/samples/regional/addons/provision-400.json +0 -29
  116. data/samples/regional/addons/sso-200.json +0 -49
  117. data/samples/regional/addons/sso-404.json +0 -24
  118. data/samples/regional/addons/token-200.json +0 -49
  119. data/samples/regional/addons/token-404.json +0 -24
  120. data/samples/regional/addons/update-200.json +0 -58
  121. data/samples/regional/addons/update-404.json +0 -30
  122. data/samples/regional/apps/_meta.json +0 -52
  123. data/samples/regional/apps/all.json +0 -99
  124. data/samples/regional/apps/create-201.json +0 -66
  125. data/samples/regional/apps/create-422.json +0 -34
  126. data/samples/regional/apps/destroy-204.json +0 -19
  127. data/samples/regional/apps/destroy-404.json +0 -24
  128. data/samples/regional/apps/destroy-422.json +0 -27
  129. data/samples/regional/apps/find-200.json +0 -60
  130. data/samples/regional/apps/find-404.json +0 -24
  131. data/samples/regional/apps/logs_url.json +0 -62
  132. data/samples/regional/apps/rename-200.json +0 -65
  133. data/samples/regional/apps/rename-404.json +0 -29
  134. data/samples/regional/apps/rename-422.json +0 -33
  135. data/samples/regional/apps/transfer-200.json +0 -65
  136. data/samples/regional/apps/transfer-404.json +0 -27
  137. data/samples/regional/apps/transfer-422.json +0 -34
  138. data/samples/regional/apps/update-200.json +0 -66
  139. data/samples/regional/apps/update-stack-404.json +0 -30
  140. data/samples/regional/autoscalers/_meta.json +0 -27
  141. data/samples/regional/autoscalers/create-201.json +0 -49
  142. data/samples/regional/autoscalers/create-500.json +0 -32
  143. data/samples/regional/autoscalers/destroy-204.json +0 -20
  144. data/samples/regional/autoscalers/destroy-404.json +0 -25
  145. data/samples/regional/autoscalers/find-200.json +0 -39
  146. data/samples/regional/autoscalers/find-404.json +0 -25
  147. data/samples/regional/autoscalers/for-200.json +0 -41
  148. data/samples/regional/autoscalers/update-200.json +0 -45
  149. data/samples/regional/autoscalers/update-404.json +0 -31
  150. data/samples/regional/autoscalers/update-500.json +0 -30
  151. data/samples/regional/collaborators/_meta.json +0 -17
  152. data/samples/regional/collaborators/accept-200.json +0 -60
  153. data/samples/regional/collaborators/accept-400.json +0 -24
  154. data/samples/regional/collaborators/accept-404.json +0 -24
  155. data/samples/regional/collaborators/destroy-204.json +0 -19
  156. data/samples/regional/collaborators/destroy-404.json +0 -24
  157. data/samples/regional/collaborators/for-200.json +0 -34
  158. data/samples/regional/collaborators/invite-201.json +0 -37
  159. data/samples/regional/collaborators/invite-422.json +0 -34
  160. data/samples/regional/containers/_meta.json +0 -25
  161. data/samples/regional/containers/for-200.json +0 -39
  162. data/samples/regional/containers/restart-202.json +0 -28
  163. data/samples/regional/containers/restart-422.json +0 -33
  164. data/samples/regional/containers/scale-202.json +0 -48
  165. data/samples/regional/containers/scale-422.json +0 -36
  166. data/samples/regional/containers/sizes-guest.json +0 -115
  167. data/samples/regional/containers/sizes-logged.json +0 -116
  168. data/samples/regional/deployments/_meta.json +0 -8
  169. data/samples/regional/deployments/find-200.json +0 -45
  170. data/samples/regional/deployments/find-404.json +0 -24
  171. data/samples/regional/deployments/for-with-paging.json +0 -35
  172. data/samples/regional/deployments/for-without-pages.json +0 -56
  173. data/samples/regional/deployments/logs-200.json +0 -22
  174. data/samples/regional/deployments/logs-404.json +0 -24
  175. data/samples/regional/domains/_meta.json +0 -21
  176. data/samples/regional/domains/create-201.json +0 -44
  177. data/samples/regional/domains/create-422.json +0 -33
  178. data/samples/regional/domains/destroy-204.json +0 -19
  179. data/samples/regional/domains/destroy-404.json +0 -24
  180. data/samples/regional/domains/find-200.json +0 -38
  181. data/samples/regional/domains/find-404.json +0 -24
  182. data/samples/regional/domains/for-200.json +0 -40
  183. data/samples/regional/domains/update-200.json +0 -44
  184. data/samples/regional/domains/update-404.json +0 -30
  185. data/samples/regional/domains/update-422.json +0 -33
  186. data/samples/regional/environment/_meta.json +0 -43
  187. data/samples/regional/environment/bulk-destroy-204.json +0 -19
  188. data/samples/regional/environment/bulk-update-200.json +0 -70
  189. data/samples/regional/environment/create-201.json +0 -36
  190. data/samples/regional/environment/create-422.json +0 -37
  191. data/samples/regional/environment/destroy-204.json +0 -19
  192. data/samples/regional/environment/destroy-404.json +0 -24
  193. data/samples/regional/environment/for-200.json +0 -31
  194. data/samples/regional/environment/update-200.json +0 -35
  195. data/samples/regional/environment/update-404.json +0 -30
  196. data/samples/regional/events/_meta.json +0 -3
  197. data/samples/regional/events/all-200.json +0 -619
  198. data/samples/regional/events/categories-guest.json +0 -66
  199. data/samples/regional/events/categories-logged.json +0 -67
  200. data/samples/regional/events/for-200.json +0 -404
  201. data/samples/regional/events/types-guest.json +0 -288
  202. data/samples/regional/events/types-logged.json +0 -289
  203. data/samples/regional/logs/_meta.json +0 -11
  204. data/samples/regional/logs/archives-200.json +0 -28
  205. data/samples/regional/logs/get-guest-200.json +0 -18
  206. data/samples/regional/logs/get-logged-200.json +0 -19
  207. data/samples/regional/logs/get-with-limit-200.json +0 -18
  208. data/samples/regional/metrics/_meta.json +0 -15
  209. data/samples/regional/metrics/for-invalid-400.json +0 -23
  210. data/samples/regional/metrics/for-valid-cpu-200.json +0 -747
  211. data/samples/regional/metrics/for-valid-router-404.json +0 -23
  212. data/samples/regional/metrics/types-guest.json +0 -66
  213. data/samples/regional/metrics/types-logged.json +0 -67
  214. data/samples/regional/notifiers/_meta.json +0 -23
  215. data/samples/regional/notifiers/create-201.json +0 -55
  216. data/samples/regional/notifiers/create-404.json +0 -30
  217. data/samples/regional/notifiers/create-422.json +0 -36
  218. data/samples/regional/notifiers/destroy-204.json +0 -19
  219. data/samples/regional/notifiers/destroy-404.json +0 -24
  220. data/samples/regional/notifiers/find-200.json +0 -47
  221. data/samples/regional/notifiers/find-404.json +0 -24
  222. data/samples/regional/notifiers/for-200.json +0 -49
  223. data/samples/regional/notifiers/platforms-guest.json +0 -184
  224. data/samples/regional/notifiers/platforms-logged.json +0 -185
  225. data/samples/regional/notifiers/test-200.json +0 -22
  226. data/samples/regional/notifiers/test-404.json +0 -25
  227. data/samples/regional/notifiers/update-200.json +0 -53
  228. data/samples/regional/operations/_meta.json +0 -5
  229. data/samples/regional/operations/find-200.json +0 -31
  230. data/samples/regional/operations/find-404.json +0 -24
  231. data/samples/regional/scm_repo_links/_meta.json +0 -22
  232. data/samples/regional/scm_repo_links/create-201.json +0 -54
  233. data/samples/regional/scm_repo_links/destroy-204.json +0 -15
  234. data/samples/regional/scm_repo_links/manual-deploy-200.json +0 -32
  235. data/samples/regional/scm_repo_links/show-200.json +0 -43
  236. data/samples/regional/scm_repo_links/update-200.json +0 -50
  237. data/samples/regional_database/backups/_meta.json +0 -4
  238. data/samples/regional_database/backups/archive-200.json +0 -24
  239. data/samples/regional_database/backups/archive-400.json +0 -24
  240. data/samples/regional_database/backups/create-201.json +0 -32
  241. data/samples/regional_database/backups/create-400.json +0 -24
  242. data/samples/regional_database/backups/for-200.json +0 -52
  243. data/samples/regional_database/backups/for-400.json +0 -24
  244. data/samples/regional_database/databases/_meta.json +0 -3
  245. data/samples/regional_database/databases/find-200.json +0 -47
  246. data/samples/regional_database/databases/find-400.json +0 -24
  247. data/samples/regional_database/databases/upgrade-202.json +0 -39
  248. data/samples/regional_database/databases/upgrade-400.json +0 -24
  249. data/spec/scalingo/api/client_spec.rb +0 -256
  250. data/spec/scalingo/api/endpoint_spec.rb +0 -45
  251. data/spec/scalingo/api/response_spec.rb +0 -301
  252. data/spec/scalingo/auth/keys_spec.rb +0 -58
  253. data/spec/scalingo/auth/scm_integrations_spec.rb +0 -58
  254. data/spec/scalingo/auth/tokens_spec.rb +0 -74
  255. data/spec/scalingo/auth/two_factor_auth_spec.rb +0 -69
  256. data/spec/scalingo/auth/user_spec.rb +0 -31
  257. data/spec/scalingo/auth_spec.rb +0 -15
  258. data/spec/scalingo/bearer_token_spec.rb +0 -72
  259. data/spec/scalingo/billing/profile_spec.rb +0 -55
  260. data/spec/scalingo/billing_spec.rb +0 -11
  261. data/spec/scalingo/client_spec.rb +0 -93
  262. data/spec/scalingo/configuration_spec.rb +0 -57
  263. data/spec/scalingo/core_client_spec.rb +0 -23
  264. data/spec/scalingo/regional/addons_spec.rb +0 -169
  265. data/spec/scalingo/regional/apps_spec.rb +0 -137
  266. data/spec/scalingo/regional/autoscalers_spec.rb +0 -84
  267. data/spec/scalingo/regional/collaborators_spec.rb +0 -69
  268. data/spec/scalingo/regional/containers_spec.rb +0 -67
  269. data/spec/scalingo/regional/deployments_spec.rb +0 -45
  270. data/spec/scalingo/regional/domains_spec.rb +0 -84
  271. data/spec/scalingo/regional/environment_spec.rb +0 -77
  272. data/spec/scalingo/regional/events_spec.rb +0 -65
  273. data/spec/scalingo/regional/logs_spec.rb +0 -39
  274. data/spec/scalingo/regional/metrics_spec.rb +0 -46
  275. data/spec/scalingo/regional/notifiers_spec.rb +0 -113
  276. data/spec/scalingo/regional/operations_spec.rb +0 -27
  277. data/spec/scalingo/regional/scm_repo_links_spec.rb +0 -48
  278. data/spec/scalingo/regional_database/backups_spec.rb +0 -58
  279. data/spec/scalingo/regional_database/databases_spec.rb +0 -39
  280. data/spec/scalingo/regional_database_spec.rb +0 -11
  281. data/spec/scalingo/regional_spec.rb +0 -14
  282. data/spec/scalingo/token_holder_spec.rb +0 -81
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90d46c83c5910349a51d707d80748ed81a542947e95397ce34f52c0f75a0bd56
4
- data.tar.gz: e6e2840344c04d9ea78ef69dbfadfb601f4dd1c7c62f3af511c16a0652f6b1d2
3
+ metadata.gz: 2e81dc5d1e89b30c848e17b2ef1ef0cbe73fd96555659225cf4d6bbd3558fa60
4
+ data.tar.gz: 6ebe2d660f1c2d696839d8db23a48932057003390fe55bedebf427898b36df27
5
5
  SHA512:
6
- metadata.gz: 7f166cbc46cd1ef4237e72765fb69348374f1c0d5f6e42bc3c544ff2f8926c3832a115d135241e2f3dd5a679297ce9b5a0196af80660d189d57aba3b9b11ab3f
7
- data.tar.gz: f261a4617f0dc6f8ef3158ac918386020bb6243d93a89f315efb1a338bc7348287f23d7ae3307f61c9e29eef673f4fe41c23343d168cc3fd8ebb526530b15187
6
+ metadata.gz: 3b851279a78169fc9ae01c8eea2c3e5ba9cf9898af057d3941a7d481fb6dd4372b4ada7beb54638c6b6fb0fb734a463bac4773cecc91af0462210a27372d3598
7
+ data.tar.gz: b9617375ab3230a1993874b8fc4b64e34cf07d9e1d7f9fc3341f37dd5c9d885b54cce1099d18bc51ba07fb990dedb6d950eb839efbe6881aea1e849851ba2f79
@@ -0,0 +1,17 @@
1
+ name: Dependabot auto-approve
2
+ on: pull_request
3
+
4
+ permissions:
5
+ # Mandatory for both the auto-merge enabling and approval steps
6
+ pull-requests: write
7
+ # Mandatory for the auto-merge enabling step
8
+ contents: write
9
+
10
+ jobs:
11
+ dependabot:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Automatically merge Dependabot PRs
15
+ uses: scalingo/ghaction-dependabot-automerge@v1
16
+ env:
17
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -7,15 +7,18 @@ on:
7
7
 
8
8
  jobs:
9
9
  build:
10
+ name: Build + Publish
10
11
  runs-on: ubuntu-latest
11
-
12
+ permissions:
13
+ contents: read
12
14
  steps:
13
- - uses: actions/checkout@v3
15
+ - uses: actions/checkout@v4
14
16
  - name: Set up Ruby
15
17
  uses: ruby/setup-ruby@v1
16
18
  with:
17
- ruby-version: 3.0
19
+ ruby-version: "3.1"
18
20
  bundler-cache: true
21
+ rubygems: "latest"
19
22
  - name: Publish to RubyGems
20
23
  run: |
21
24
  mkdir -p $HOME/.gem
@@ -14,7 +14,7 @@ jobs:
14
14
  lint:
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
- - uses: actions/checkout@v3
17
+ - uses: actions/checkout@v4
18
18
  - name: Set up Ruby
19
19
  uses: ruby/setup-ruby@v1
20
20
  with:
@@ -27,9 +27,9 @@ jobs:
27
27
  runs-on: ubuntu-latest
28
28
  strategy:
29
29
  matrix:
30
- ruby-version: ['2.6', '2.7', '3.0', '3.2']
30
+ ruby-version: ['3.0', '3.1', '3.2', '3.3']
31
31
  steps:
32
- - uses: actions/checkout@v3
32
+ - uses: actions/checkout@v4
33
33
  - name: Set up Ruby
34
34
  # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
35
35
  # change this to (see https://github.com/ruby/setup-ruby#versioning):
data/.rubocop.yml CHANGED
@@ -1,13 +1,18 @@
1
- inherit_from: []
2
-
3
- inherit_gem:
4
- standard: config/base.yml
5
-
6
1
  require:
7
2
  - standard
3
+ - standard-custom
4
+ - standard-performance
8
5
  - rubocop-performance
6
+ - rubocop-rspec
7
+
8
+ inherit_gem:
9
+ standard: config/base.yml
10
+ standard-custom: config/base.yml
11
+ standard-performance: config/base.yml
9
12
 
10
13
  AllCops:
14
+ TargetRubyVersion: 3.0
15
+ NewCops: enable
11
16
  SuggestExtensions: false
12
17
  Exclude:
13
18
  - 'node_modules/**/*'
@@ -15,18 +20,42 @@ AllCops:
15
20
  - 'vendor/**/*'
16
21
  - '.git/**/*'
17
22
 
18
- Style/TrailingCommaInArguments:
19
- Enabled: true
20
- EnforcedStyleForMultiline: consistent_comma
23
+ # Line-base counting is not reliable enough
24
+ RSpec/ExampleLength:
25
+ Enabled: false
26
+
27
+ # Deprecated cop, will be removed
28
+ RSpec/FilePath:
29
+ Enabled: false
30
+
31
+ # Not convinced by how strict this cop is by default
32
+ RSpec/MultipleExpectations:
33
+ Enabled: false
34
+
35
+ RSpec/MultipleMemoizedHelpers:
36
+ Enabled: false
37
+
38
+ # Really not convinced by this one
39
+ RSpec/NamedSubject:
40
+ Enabled: false
41
+
42
+ # Default is 3, but 4 is used and relevant (class / method / global state / local state)
43
+ RSpec/NestedGroups:
44
+ Max: 4
45
+
46
+ # Not always relevant for libs
47
+ RSpec/SpecFilePathFormat:
48
+ Exclude:
49
+ - 'spec/scalingo/faraday/**/*'
21
50
 
22
- Style/TrailingCommaInArrayLiteral:
23
- Enabled: true
24
- EnforcedStyleForMultiline: consistent_comma
51
+ # Not sure about this one - requires more research
52
+ RSpec/MessageSpies:
53
+ Enabled: false
25
54
 
26
- Style/TrailingCommaInHashLiteral:
27
- Enabled: true
28
- EnforcedStyleForMultiline: consistent_comma
55
+ # Not sure about this one - requires more research
56
+ RSpec/StubbedMock:
57
+ Enabled: false
29
58
 
30
- Style/Alias:
31
- Enabled: true
32
- EnforcedStyle: prefer_alias_method
59
+ # Not sure about this one - requires more research
60
+ RSpec/SubjectStub:
61
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,4 +1,23 @@
1
- ## Unreleased
1
+ ## 4.0.beta1 - 2024-04-15
2
+
3
+ * Breaking change: exceptions are raised on error responses (4xx, 5xx) and other errors (connection issue, timeouts)
4
+ * Trying to reach an endpoint without having the client authenticated will raise an exception without attempting the request
5
+ * Same when the token is expired. Expiration date is read from the token directly
6
+ * Associated configuration options have been removed
7
+ * Breaking change: rework DB api exposition
8
+ * Specs: rewrite all specs
9
+ * Breaking change: endpoint methods declaration is simplified:
10
+ * based on URI templates
11
+ * argument and method names are unified
12
+ * one "main" internal method, `Endpoint#request`
13
+ * Breaking change: automatic digging of the value if the reponse body is an object with a single key
14
+ * Breaking change: remove `Scalingo::API::Reponse` in favor of `Faraday::Response`
15
+
16
+ ## 3.5.0 - 2023-12-28
17
+
18
+ * Change: update Faraday to 2.x, released about two years ago. The public API of this gem doesn't change, therefore this is not a major release. However, if you manipulate directly faraday's objects, you may encounter breaking changes. Refer to [Faraday's website](https://lostisland.github.io/faraday/) for how to migrate.
19
+ * Compat: drop support for Ruby < 3. The lib is still expected to work for the time being.
20
+ * Compat: include Ruby 3.3 in the test matrix
2
21
 
3
22
  ## 3.4.0 - 2023-01-26
4
23
 
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in scalingo.gemspec
4
4
  gemspec
5
+
6
+ gem "simplecov"
data/README.md CHANGED
@@ -2,18 +2,10 @@
2
2
 
3
3
  A ruby wrapper for the Scalingo API
4
4
 
5
- ### Migration from v2
6
-
7
- This gem is changing its name from `scalingo-ruby-api` to `scalingo`,
8
- and the versioning does **not** reset; the first major version of `scalingo`
9
- will therefore be `3.x.x`.
10
-
11
- You can check the version 2 at [the v2 branch of this repository](https://github.com/Scalingo/scalingo-ruby-api/tree/v2)
12
-
13
5
  ## Installation
14
6
 
15
7
  ```ruby
16
- gem "scalingo", "3.0.0"
8
+ gem "scalingo"
17
9
  ```
18
10
 
19
11
  And then execute:
@@ -29,7 +21,7 @@ require "scalingo"
29
21
 
30
22
  scalingo = Scalingo::Client.new
31
23
  scalingo.authenticate_with(access_token: ENV["SCALINGO_TOKEN"])
32
- scalingo.user.self
24
+ scalingo.self
33
25
  ```
34
26
 
35
27
  ## Conventions
@@ -37,16 +29,13 @@ scalingo.user.self
37
29
  Most methods map to one (and only one) request, and their signature follows this format:
38
30
 
39
31
  ```ruby
40
- client.section.request(id, payload = {}, headers = nil, &block)
32
+ client.section.request(app_id:, id:, body:)
41
33
  ```
42
34
 
43
35
  * Depending on the request, there may be no id (collection and/or singular resource, such as `user`), one, or two ids (many resources are nested under an app).
44
36
  * Most of the time, this library won't do any processing of the payload, but there's a few things to know:
45
- * the root key shouldn't be specified, the library handles it
37
+ * the root key doesn't need to be specified, the library handles it
46
38
  * in some cases, the payload isn't passed as supplied (`metrics`, for instance, extracts the parts that are meant to be used as url fragments)
47
- * headers can be supplied on a per-request basis, using either the last argument or the block version:
48
- * when using the last argument, you may have to pass an empty hash payload (`{}`)
49
- * when using the block form, the faraday object is supplied as argument, and you can do any kind of treatment you would like
50
39
 
51
40
  ## Configuration
52
41
 
@@ -58,23 +47,13 @@ changing the configuration globally will therefore not affect already existing o
58
47
 
59
48
  ```ruby
60
49
  Scalingo.configure do |config|
61
- # Default region. Must be a supported region (agora_fr1, osc_fr1, osc_secnum_fr1)
50
+ # Default region. Must be a supported region (osc_fr1, osc_secnum_fr1)
62
51
  config.default_region = :osc_fr1
63
52
 
64
53
  # Configure the User Agent header
65
54
  config.user_agent = "Scalingo Ruby Client v#{Scalingo::VERSION}"
66
55
 
67
- # For how long is a bearer token considered valid (it will raise passed this delay).
68
- # Set to nil to never raise.
69
- config.exchanged_token_validity = 1.hour
70
-
71
- # Having this setting to true prevents performing requests that would fail due to lack of authentication headers.
72
- config.raise_on_missing_authentication = true
73
-
74
- # Raise an exception when the bearer token in use is supposed to be invalid
75
- config.raise_on_expired_token = false
76
-
77
- # These headers will be added to every request. Individual methods may override them.
56
+ # These headers will be added to every request
78
57
  # This should be a hash or a callable object that returns a hash.
79
58
  config.additional_headers = {}
80
59
 
@@ -87,26 +66,9 @@ You can also configure each client separately.
87
66
  Values not supplied will be copied from the global configuration.
88
67
 
89
68
  ```ruby
90
- scalingo = Scalingo::Client.new(raise_on_expired_token: false)
69
+ scalingo = Scalingo::Client.new(user_agent: "A new kind of agent")
91
70
  ```
92
71
 
93
- ## Response object
94
-
95
- Responses are parsed with the keys symbolized and then encapsulated in a `Scalingo::API::Response` object:
96
-
97
- * `response.status` containts the HTTP status code
98
- * `response.data` contains the "relevant" data, without the json root key (when relevant)
99
- * `response.full_body` contains the full response body
100
- * `response.meta` contains the meta object, if there's any
101
- * `response.headers` containts all the response headers
102
-
103
- Some helper methods are defined on this object:
104
- * `response.successful?` returns true when the code is 2XX
105
- * `response.paginated?` returns true if the reponse has metadata relative to pagination
106
- * `response.operation?` returns true if the response contains a header relative to an ongoing operation
107
- * `response.operation_url` returns the URL to query to get the status of the operation
108
- * `response.operation` performs a request to retrieve the operation
109
-
110
72
  ## Other details on the code architecture
111
73
 
112
74
  * `Scalingo::Client` instances hold configuration and the token used for authentication
@@ -127,13 +89,13 @@ scalingo.authenticate_with(access_token: "my_access_token")
127
89
  scalingo.authenticate_with(bearer_token: "my_bearer_jwt")
128
90
 
129
91
  # Return your profile
130
- scalingo.user.self
92
+ scalingo.self # or scalingo.auth.user.find
131
93
 
132
94
  # List your SSH Keys
133
95
  scalingo.keys.all # OR scalingo.auth.keys.all
134
96
 
135
97
  # Show one SSH Key
136
- scalingo.keys.show("my-key-id")
98
+ scalingo.keys.show(id: "my-key-id")
137
99
 
138
100
  # List your apps on the default region
139
101
  scalingo.apps.all # OR scalingo.region.apps.all
@@ -151,8 +113,6 @@ Requests to the [database API](https://developers.scalingo.com/databases/) requi
151
113
  extra authentication for each addon you want to interact with. [Addon authentication
152
114
  tokens are valid for one hour](https://developers.scalingo.com/addons#get-addon-token).
153
115
 
154
- Supported regions for database API are `db_api_osc_fr1` and `db_api_osc_secnum_fr1`.
155
-
156
116
  ```ruby
157
117
  require "scalingo"
158
118
 
@@ -160,23 +120,20 @@ scalingo = Scalingo::Client.new
160
120
  scalingo.authenticate_with(access_token: "my_access_token")
161
121
 
162
122
  # First, authenticate using the `addons` API
163
- scalingo.osc_fr1.addons.authenticate!(app_id, addon_id)
123
+ dbclient = scalingo.osc_fr1.addons.database_client_for(app_id:, id:)
164
124
 
165
125
  # Once authenticated for that specific addon, you can interact with
166
126
  # database and backup APIs.
167
127
  # IDs of databases are the IDs of the corresponding addons
168
128
 
169
129
  # get all information for a given database
170
- scalingo.db_api_osc_fr1.databases.find(addon_id)
130
+ dbclient.databases.find(id:)
171
131
 
172
132
  # get all backups for a given database
173
- scalingo.db_api_osc_fr1.backups.for(addon_id)
133
+ dbclient.backups.list(addon_id:)
174
134
 
175
135
  # get URL to download backup archive
176
- scalingo.db_api_osc_fr1.backups.archive(addon_id, backup_id)
177
-
178
- # you can omit the region to use the default one
179
- scalingo.databases.find(addon_id)
136
+ dbclient.backups.archive(addon_id:, id:)
180
137
 
181
138
  ```
182
139
 
@@ -193,9 +150,3 @@ bundle
193
150
  ```bash
194
151
  bundle exec rspec
195
152
  ```
196
-
197
- ### Release a new version
198
-
199
- ```bash
200
- # TODO
201
- ```
data/bin/console CHANGED
@@ -3,10 +3,20 @@
3
3
  require "bundler/setup"
4
4
  require "scalingo"
5
5
 
6
+ begin
7
+ require "dotenv"
8
+ Dotenv.load(".env.local")
9
+ rescue LoadError
10
+ puts("dotenv not available - no .env.local loading")
11
+ end
12
+
6
13
  # You can add fixtures and/or initialization code here to make experimenting
7
14
  # with your gem easier. You can also use a different console, if you like.
8
15
 
9
16
  # (If you use this, don't forget to add pry to your Gemfile!)
10
17
  require "pry"
11
18
 
12
- Pry.start
19
+ client = Scalingo::Client.new
20
+ client.authenticate_with(access_token: ENV["SCALINGO_API_TOKEN"]) if ENV["SCALINGO_API_TOKEN"].present?
21
+
22
+ Pry.start(binding, quiet: true)
data/bin/lint ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bash
2
+ bundle exec rubocop $@
data/bin/setup CHANGED
@@ -1,8 +1,23 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
1
+ #!/usr/bin/env ruby
2
+ require "fileutils"
5
3
 
6
- bundle install
4
+ # path to your application root.
5
+ GEM_ROOT = File.expand_path("..", __dir__)
7
6
 
8
- # Do any other automated setup that you need to do here
7
+ def system!(*args)
8
+ system(*args, exception: true)
9
+ end
10
+
11
+ FileUtils.chdir GEM_ROOT do
12
+ # This script is a way to set up or update your development environment automatically.
13
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
14
+ # Add necessary setup steps to this file.
15
+
16
+ puts "== Installing dependencies =="
17
+ system! "gem install bundler --conservative"
18
+ system("bundle check") || system!("bundle install")
19
+
20
+ # Creating useful but unversioned env files
21
+ puts "\n== Creating .env.*.local files =="
22
+ FileUtils.touch(".env.local")
23
+ end
data/bin/specs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bash
2
+ bundle exec rspec $@
@@ -1,14 +1,20 @@
1
1
  require "scalingo/token_holder"
2
+ require "scalingo/faraday/response"
3
+ require "scalingo/faraday/extract_meta"
4
+ require "scalingo/faraday/extract_root_value"
5
+ require "active_support/core_ext/hash"
2
6
 
3
7
  module Scalingo
4
8
  module API
5
9
  class Client
6
10
  include TokenHolder
7
11
 
8
- attr_reader :config, :token_holder, :url
12
+ attr_reader :config, :token_holder, :url, :region
9
13
 
10
- def initialize(url, scalingo: nil, config: {})
14
+ def initialize(url, scalingo: nil, region: nil, config: {})
11
15
  @url = url
16
+ @region = region
17
+
12
18
  parent_config = Scalingo.config
13
19
 
14
20
  if scalingo
@@ -29,32 +35,32 @@ module Scalingo
29
35
 
30
36
  def self.register_handler!(method_name, klass)
31
37
  define_method(method_name) do
32
- value = instance_variable_get("@#{method_name}")
38
+ value = instance_variable_get(:"@#{method_name}")
33
39
 
34
40
  if value.nil?
35
41
  value = klass.new(self)
36
- instance_variable_set("@#{method_name}", value)
42
+ instance_variable_set(:"@#{method_name}", value)
37
43
  end
38
44
 
39
45
  value
40
46
  end
41
47
  end
42
48
 
49
+ # :nocov:
43
50
  def inspect
44
51
  str = %(<#{self.class}:0x#{object_id.to_s(16)} url:"#{@url}" methods:)
45
52
 
46
- methods = self.class.instance_methods - Scalingo::API::Client.instance_methods
47
- str << methods.to_s
48
-
53
+ str << self.class.instance_methods(false).to_s
49
54
  str << ">"
50
55
  str
51
56
  end
57
+ # :nocov:
52
58
 
53
59
  ## Faraday objects
54
60
  def headers
55
61
  hash = {
56
62
  "User-Agent" => config.user_agent,
57
- "Accept" => "application/json",
63
+ "Accept" => "application/json"
58
64
  }
59
65
 
60
66
  if (extra = config.additional_headers).present?
@@ -67,68 +73,35 @@ module Scalingo
67
73
  def connection_options
68
74
  {
69
75
  url: url,
70
- headers: headers,
76
+ headers: headers
71
77
  }
72
78
  end
73
79
 
74
- # Note: when `config.raise_on_missing_authentication` is set to false,
75
- # this method may return the unauthenticated connection
76
- # even with `fallback_to_guest: false`
77
- def connection(fallback_to_guest: false)
78
- authenticated_connection
79
- rescue Error::Unauthenticated
80
- raise unless fallback_to_guest
81
-
82
- unauthenticated_connection
83
- end
84
-
85
- def unauthenticated_connection
86
- @unauthenticated_conn ||= Faraday.new(connection_options) { |conn|
80
+ def guest_connection
81
+ @guest_connection ||= Faraday.new(connection_options) { |conn|
82
+ conn.response :extract_root_value
83
+ conn.response :extract_meta
87
84
  conn.response :json, content_type: /\bjson$/, parser_options: {symbolize_names: true}
85
+ conn.response :raise_error
88
86
  conn.request :json
89
87
 
90
88
  conn.adapter(config.faraday_adapter) if config.faraday_adapter
91
89
  }
92
90
  end
93
91
 
94
- def authenticated_connection
92
+ def connection
95
93
  return @connection if @connection
96
94
 
97
95
  # Missing token handling. Token expiration is handled in the `value` method.
98
- unless token_holder.token&.value
99
- if config.raise_on_missing_authentication
100
- raise Error::Unauthenticated
101
- else
102
- return unauthenticated_connection
103
- end
104
- end
96
+ raise Error::Unauthenticated unless token_holder.token&.value&.present?
105
97
 
106
98
  @connection = Faraday.new(connection_options) { |conn|
99
+ conn.response :extract_root_value
100
+ conn.response :extract_meta
107
101
  conn.response :json, content_type: /\bjson$/, parser_options: {symbolize_names: true}
108
102
  conn.request :json
109
-
110
- if token_holder.token&.value
111
- auth_header = Faraday::Request::Authorization.header "Bearer", token_holder.token.value
112
- conn.headers[Faraday::Request::Authorization::KEY] = auth_header
113
- end
114
-
115
- conn.adapter(config.faraday_adapter) if config.faraday_adapter
116
- }
117
- end
118
-
119
- def database_connection(database_id)
120
- raise Error::Unauthenticated unless token_holder.authenticated_for_database?(database_id)
121
-
122
- @database_connections ||= {}
123
- @database_connections[database_id] ||= Faraday.new(connection_options) { |conn|
124
- conn.response :json, content_type: /\bjson$/, parser_options: {symbolize_names: true}
125
- conn.request :json
126
-
127
- bearer_token = token_holder.database_tokens[database_id]&.value
128
- if bearer_token
129
- auth_header = Faraday::Request::Authorization.header "Bearer", bearer_token
130
- conn.headers[Faraday::Request::Authorization::KEY] = auth_header
131
- end
103
+ conn.response :raise_error
104
+ conn.request :authorization, "Bearer", -> { token_holder.token&.value }
132
105
 
133
106
  conn.adapter(config.faraday_adapter) if config.faraday_adapter
134
107
  }
@@ -1,5 +1,5 @@
1
+ require "addressable/template"
1
2
  require "forwardable"
2
- require "scalingo/api/response"
3
3
 
4
4
  module Scalingo
5
5
  module API
@@ -7,28 +7,105 @@ module Scalingo
7
7
  extend Forwardable
8
8
  attr_reader :client
9
9
 
10
+ # Add a handler for a given endpoint
11
+ %i[get post put patch delete].each do |method|
12
+ # @example
13
+ # class Example < API::Endpoint
14
+ # get :all, "some-endpoint/{id}/subthings{?query*}", optional: [:query]
15
+ # post :create, "some-endpoint", root_key: :subthing
16
+ # end
17
+ define_singleton_method(method) do |name, path, **default_attrs, &default_block|
18
+ # @example
19
+ # endpoint = Example.new
20
+ # endpoint.all(id: "1", query: {page: 1})
21
+ # endpoint.create(name: "thing")
22
+ define_method(name) do |**runtime_attrs, &runtime_block|
23
+ params = {**default_attrs, **runtime_attrs}
24
+
25
+ request(method, path, **params) do |req|
26
+ default_block&.call(req, params)
27
+ runtime_block&.call(req, params)
28
+ end
29
+ end
30
+ end
31
+
32
+ # Those methods are not meant to be used outside of a class definition
33
+ private_class_method method
34
+ end
35
+
10
36
  def initialize(client)
11
37
  @client = client
12
38
  end
13
39
 
14
40
  def_delegator :client, :connection
15
- def_delegator :client, :database_connection
16
41
 
17
- def inspect
18
- str = %(<#{self.class}:0x#{object_id.to_s(16)} base_url:"#{@client.url}" endpoints:)
42
+ # Perform a request to the API.
43
+ # path can be an URI template; and faraday expect valid URIs - the parser raises when templates aren't fully expanded.
44
+ # therefore, we have to take care of the expansion before passing the path to faraday.
45
+ # note: This method is not unit-tested directly, but integrations tests are covering it extensively.
46
+ # @see https://github.com/sporkmonger/addressable?tab=readme-ov-file#uri-templates
47
+ # @see https://www.rfc-editor.org/rfc/rfc6570.txt
48
+ # @see https://github.com/lostisland/faraday/issues/1487
49
+ def request(method, path, body: nil, root_key: nil, connected: true, basic: nil, dry_run: false, params_as_body: false, **params, &block)
50
+ template = Addressable::Template.new(path)
19
51
 
20
- methods = self.class.instance_methods - Scalingo::API::Endpoint.instance_methods
52
+ # If the template has keys, we need to expand it with the params
53
+ if template.keys.present?
54
+ # We assume every variable in the template is required
55
+ expected_keys = Set.new(template.keys.map(&:to_sym))
56
+ # ... but we can opt out by specifying :optional when performing the request or in the endpoint definition
57
+ expected_keys -= params[:optional] if params[:optional].present?
21
58
 
22
- str << methods.to_s
23
- str << ">"
24
- str
59
+ # if any required key is missing, raise an error with the missing keys,
60
+ # as if it was a regular keyword argument that was not supplied
61
+ if expected_keys.present?
62
+ received_keys = Set.new(params.keys.map(&:to_sym))
63
+
64
+ unless received_keys.superset?(expected_keys)
65
+ missings = (expected_keys - received_keys).map { |item| sprintf("%p", item) }.join(" ")
66
+ raise ArgumentError, "missing keyword: #{missings}"
67
+ end
68
+ end
69
+
70
+ # Now, we can expand the template with the supplied params
71
+ actual_path = template.expand(params).to_s
72
+ else
73
+ # Otherwise, it's not a template but a string to be used as it is
74
+ actual_path = path
75
+ end
76
+
77
+ # we nest the given body under the root_key if it's present
78
+ request_body = body
79
+ request_body = {root_key => body} if request_body && root_key
80
+
81
+ # We can use the client in either connected or unconnected mode
82
+ conn = connected ? client.connection : client.guest_connection
83
+
84
+ # We can specify basic auth credentials if needed
85
+ conn.request :authorization, :basic, basic[:user], basic[:password] if basic.present?
86
+
87
+ # Finally, perform the request.
88
+ # Faraday sends params as query string for GET/HEAD/DELETE requests and as request body for the others;
89
+ # in some rare cases (variables bulk-delete) we need to send them as actual body.
90
+ if Faraday::METHODS_WITH_QUERY.include?(method.to_s) && params_as_body
91
+ conn.public_send(method, actual_path) do |req|
92
+ req.body = request_body
93
+ block&.call(req) || req
94
+ end
95
+ else
96
+ conn.public_send(method, actual_path, request_body, &block)
97
+ end
25
98
  end
26
99
 
27
- private
100
+ # :nocov:
101
+ def inspect
102
+ str = %(<#{self.class}:0x#{object_id.to_s(16)} base_url:"#{@client.url}" endpoints:)
28
103
 
29
- def unpack(*keys, &block)
30
- Response.unpack(client, keys: keys, &block)
104
+ str << self.class.instance_methods(false).to_s
105
+ str << ">"
106
+ str
31
107
  end
108
+ # :nocov:
32
109
  end
33
110
  end
34
111
  end