bullet_train-api 1.1.13 → 1.1.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a26bfba5a9e08beab9b2e9ef020751627ba9f5e622708f74bf44798ea903434b
4
- data.tar.gz: b40893d54092bb315a343891b69a0cc84a651c30775bd8840f0a3b917b791136
3
+ metadata.gz: d41bf508cee51978fb613e1eb742893560df176131046f2013c78424de30022a
4
+ data.tar.gz: 260a8f510030718dbdd1cb1683a2416b83720c524c91fc5a4bb2b5549d8f4341
5
5
  SHA512:
6
- metadata.gz: 2794b6b2cb7574361e60c9ef1e26b9cf1cf142c557f6bbebb60fe724b2c7c7ea0857b036ad432c0cfee1d9e286508db49ea63822d06ea71a0566407f60cbd082
7
- data.tar.gz: 2b4f042745a52886ba79973b159fe3a70b216f55591d537746d1a0a62365454ad6be01f132ed198665b03ed24f85022eeecd7fa5788555ce2dccdd52c5f1ae03
6
+ metadata.gz: ba5bdc2be5f38f9f7812624253cd15a09b24f2ad7433885a0c6cc17b89ac2dfbc5030e007786db0db7cf4958e3d8bb93d014b5ac0aa89d478c57b83084a0b62e
7
+ data.tar.gz: 4331563acc4e405b7aee50cf7b03afa8926c8c23d4854e8f6d0412ae9aa3e855bb2081f2053fbdbb8cc9afe0338d476d4d050e92f89b88197d44db0f2aa1731f
@@ -0,0 +1,63 @@
1
+ # API Versioning
2
+ Bullet Train's API layer is designed to help support the need of software developers to evolve their API over time while continuing to maintain support for versions of the API that users have already built against.
3
+
4
+ ## What is API versioning?
5
+ By default, Bullet Train will build out a "V1" version of your API. The version number is intended to represent a contract with your users that as long as they're hitting `/api/v1` endpoints, the structure of URLs, requests, and responses won't change in a way that will break the integrations they've created.
6
+
7
+ If a change to the API would break the established contract, we want to bump the API version number so we can differentiate between developers building against the latest version of the API (e.g. "V2") and developers who wrote code against the earlier version of the API (e.g. "V1"). This allows us the opportunity to ensure that older versions of the API continue to work as previously expected by the earlier developers.
8
+
9
+ ## When should you take advantage of API versioning?
10
+ You want to bump API versions as sparingly as possible. Even with all the tooling Bullet Train provides, maintaining backwards compatibility of older API versions comes at an ongoing cost. Generally speaking, you should only bump your API version when a customer is already using an API endpoint and you're making changes to the structure of your domain model that are not strictly additive and will break the established contract.
11
+
12
+ Importantly, if the changes you're making to your domain model are only additive, you don't need to bump your API version. Users shouldn't care that you're adding new attributes or new endpoints to your API, just as long as the ones they're already using don't change in a way that is breaking for them.
13
+
14
+ ## Background
15
+ By default, the following components in your API are created in versioned namespaces:
16
+
17
+ - API controllers are in `app/controllers/api/v1` and live in an `Api::V1` module.
18
+ - JSON views are in `app/controllers/api/v1`.
19
+ - Routes are in `config/routes/api/v1.rb`.
20
+ - Tests are in `test/controllers/api/v1` and live in an `Api::V1` module.
21
+
22
+ > It's also impotant to keep in mind that some dependencies of your API and API tests like models, factories, and permissions are not versioned, but as we'll cover later, this is something our approach helps you work around.
23
+
24
+ ## Bumping Your API Version
25
+
26
+ ⚠️ You must do this _before_ making the breaking changes to your API.
27
+
28
+ If you're in a situation where you know you need to bump your API version to help lock-in a backward compatible version of your API, you can simply run:
29
+
30
+ ```
31
+ rake bullet_train:api:bump_version
32
+ ```
33
+
34
+ > TODO This Rake task doesn't exist yet.
35
+
36
+ ## What happens when you bump an API version?
37
+ When you bump your API version, all of the files and directories that are namespaced with the API version number will be duplicated into a new namespace for the new API version number.
38
+
39
+ For example, when bumping from "V1" to "V2":
40
+
41
+ - A copy of all the API controllers in `app/controllers/api/v1` are copied into `app/controllers/api/v2`.
42
+ - A copy of all the JSON views in `app/views/api/v1` are copied into `app/views/api/v2`.
43
+ - A copy of all the routes in `config/routes/api/v1.rb` are copied into `config/routes/api/v2.rb`.
44
+ - A copy of all the tests in `test/controllers/api/v1` are copied into `test/controllers/api/v2`.
45
+
46
+ We also bump the value of `BulletTrain::Api.current_version` in `config/initializers/api.rb` so tools like Super Scaffolding know which version of your API to update going forward.
47
+
48
+ ## How does this help?
49
+ As a baseline, keeping a wholesale copy of the versioned API components helps lock in their behavior and protect them from change going forward. It's not a silver bullet, since unversioned dependencies (like your model, factories, and permissions) can still affect the behavior of these versioned API components, but even in that case these copied files give us a place where we can implement the logic that helps older versions of the API continue to operate even as unversioned components like our domain model continue changing.
50
+
51
+ ### Versioned API Tests
52
+ By versioning our API tests, we lock in a copy of what the assumptions were for older versions of the API. Should unversioned dependencies like our domain model change in ways that break earlier versions of our API, the test suite will let us know and help us figure out when we've implemented the appropriate logic in the older version of the API controller to restore the expected behavior for that version of the API.
53
+
54
+ ## Advanced Topics
55
+
56
+ ### Object-Oriented Inheritance
57
+ In order to reduce the surface area of legacy API controllers that you're maintaining, it might make sense in some cases to have an older versioned API controller simply inherit from a newer version or the current version of the same API controller. For example, this might make sense for endpoints that you know didn't have breaking changes across API versions.
58
+
59
+ ### Backporting New Features to Legacy API Versions
60
+ Typically we'd recommend you use new feature availability to encourage existing API users to upgrade to the latest version of the API. However, in some situations you may really need to make a newer API feature available to a user who is locked into a legacy version of your API for some other endpoint. This is totally fine if the feature is only additive. For example, if you're just adding a newer API endpoint in a legacy version of the API, you can simply have the new API controller in the legacy version of the API inherit from the API controller in the current version of the API.
61
+
62
+ ### Pruning Unused Legacy API Endpoints
63
+ Maintaining legacy endpoints has a very real cost, so you may choose to identify which endpoints aren't being used on legacy versions of your API and prune them from that version entirely. This has the effect of requiring existing API users to keep their API usage up-to-date before expanding the surface area of usage, which may or may not be desirable for you.
data/docs/api.md ADDED
@@ -0,0 +1,106 @@
1
+ # REST API
2
+ We believe every SaaS application should have an API and [webhooks](https://github.com/bullet-train-co/bullet_train-base/blob/main/docs/webhooks/outgoing.md) available to users, so Bullet Train aims to help automate the creation of a production-grade REST API using Rails-native tooling and provides a forward-thinking strategy for its long-term maintenance.
3
+
4
+ ## Background
5
+ Vanilla Rails scaffolding actually provides simple API functionality out-of-the-box: You can append `.json` to the URL of any scaffold and it will render a JSON representation instead of an HTML view. This functionality continues to work in Bullet Train, but our API implementation also builds on this simple baseline using the same tools with additional organization and some new patterns.
6
+
7
+ ## Goals
8
+
9
+ ### Zero-Effort API
10
+ As with vanilla Rails scaffolding, Super Scaffolding automatically generates your API as you scaffold new models, and unlike vanilla Rails scaffolding, it will automatically keep it up-to-date as you scaffold additional attributes onto your models.
11
+
12
+ ### Versioning by Default
13
+ By separating out and versioning API controllers, views, routes, and tests, Bullet Train provides [a methodology and tooling](/docs/api/versioning.md) to help ensure that once users have built against your API, changes in the structure of your domain model and API don't unexpectedly break existing integrations. You can [read more about API versioning](/docs/api/versioning.md).
14
+
15
+ ### Standard Rails Tooling
16
+ APIs are built using standard Rails tools like `ActiveController::API`, [Strong Parameters](https://api.rubyonrails.org/classes/ActionController/StrongParameters.html), `config/routes.rb`, and [Jbuilder](https://github.com/rails/jbuilder). Maintaining API endpoints doesn't require special knowledge and feels like regular Rails development.
17
+
18
+ ### Outsourced Authentication
19
+ In the same way we've adopted [Devise](https://github.com/heartcombo/devise) for best-of-breed and battle-tested authentication on the browser side, we've adopted [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) for best-of-breed and battle-tested authentication on the API side.
20
+
21
+ ### DRY Authorization Logic
22
+ Because our API endpoints are standard Rails controllers, they're able to leverage the exact same [permissions definitions and authorization logic](https://github.com/bullet-train-co/bullet_train-base/blob/main/docs/permissions.md) as our account controllers.
23
+
24
+ ## Structure
25
+ Where vanilla Rails uses a single controller in `app/controllers` for both in-browser and API requests, Bullet Train splits these into two separate controllers, one in `app/controllers/account` and another in `app/controllers/api/v1`, although a lot of logic is shared between the two.
26
+
27
+ API endpoints are defined in three parts:
28
+
29
+ 1. Routes are defined in `config/routes/api/v1.rb`.
30
+ 2. Controllers are defined in the `app/controllers/api/v1` directory.
31
+ 3. Jbuilder views are defined in the `app/views/api/v1` directory.
32
+
33
+ ## "API First" and Supporting Account Controllers
34
+ As previously mentioned, there is a lot of shared logic between account and API controllers. Importantly, there are a couple of responsbilities that are implemented "API first" in API controllers and then utilized by account controllers.
35
+
36
+ ### Strong Parameters
37
+ The primary definition of Strong Parameters for a given resource is defined in the most recent version of the API controller and included from there by the account controller. In account controllers, where you might expect to see a Strong Parameters definition, you'll see the following instead:
38
+
39
+ ```ruby
40
+ include strong_parameters_from_api
41
+ ```
42
+
43
+ > This may feel counter-intuitive to some developers and you might wonder why we don't flip this around and have the primary definition in the account controller and have the API controller delegate to it. The answer is a pragmatic one: creating and maintaining the defintion of Strong Paramters in the API controller means it gets automatically frozen in time should you ever need to [bump your API version number](/api/docs/versioning.md). We probably _could_ accomplish this if things were the other way around, but it wouldn't happen automatically.
44
+
45
+ If by chance there are additional attributes that should be permitted or specific logic that needs to be run as part of the account controller (or inversely, only in the API controller), you can specify that in the controller like so:
46
+
47
+ ```ruby
48
+ def permitted_fields
49
+ [:some_specific_attribute]
50
+ end
51
+
52
+ def permitted_arrays
53
+ {some_collection: []}
54
+ end
55
+
56
+ def process_params(strong_params)
57
+ assign_checkboxes(strong_params, :some_checkboxes)
58
+ strong_params
59
+ end
60
+ ```
61
+
62
+ ### Delegating `.json` View Rendering on Account Controllers
63
+
64
+ In Bullet Train, when you append `.json` to an account URL, the account controller doesn't actually have any `.json.jbuilder` templates in its view directory within `app/views/account`. Instead, by default the controller is configured to delegate the JSON rendering to the corresponding Jbuilder templates in the most recent version of the API, like so:
65
+
66
+ ```ruby
67
+ # GET /account/projects/:id or /account/projects/:id.json
68
+ def show
69
+ delegate_json_to_api
70
+ end
71
+ ```
72
+
73
+ ## Usage Example
74
+ First, provision a platform application in section titled "Your Applications" in the "Developers" menu of the application. When you create a new platform application, an access token that doesn't automatically expire will be automatically provisioned along with it. You can then use the access token to hit the API, as seen in the following Ruby-based example:
75
+
76
+ ```ruby
77
+ require 'net/http'
78
+ require 'uri'
79
+
80
+ # Configure an API client.
81
+ client = Net::HTTP.new('localhost', 3000)
82
+
83
+ headers = {
84
+ "Content-Type" => "application/json",
85
+ "Authorization" => "Bearer GfNLkDmzOTqAacR1Kqv0VJo7ft2TT-S_p8C6zPDBFhg"
86
+ }
87
+
88
+ # Fetch the team details.
89
+ response = client.get("/api/v1/teams/1", headers)
90
+
91
+ # Parse response.
92
+ team = JSON.parse(response.body)
93
+
94
+ # Update team name.
95
+ team["name"] = "Updated Team Name"
96
+
97
+ # Push the update to the API.
98
+ # Note that the team attributes are nested under a `team` key in the JSON body.
99
+ response = client.patch("/api/v1/teams/1", {team: team}.to_json, headers)
100
+ ```
101
+
102
+ ## Advanced Topics
103
+ - [API Versioning](/docs/api/versioning.md)
104
+
105
+ ## A Note About Other Serializers and API Frameworks
106
+ In early versions of Bullet Train we made the decision to adopt a specific serialization library, [ActiveModelSerializers](https://github.com/rails-api/active_model_serializers) and in subsequent versions we went as far as to adopt an entire third-party framework ([Grape](https://github.com/ruby-grape/grape)) and a third-party API specification ([JSON:API](https://jsonapi.org)). We now consider it out-of-scope to try and make such decisions on behalf of developers. Support for them in Bullet Train applications and in Super Scaffolding could be created by third-parties.
@@ -1,5 +1,5 @@
1
1
  module BulletTrain
2
2
  module Api
3
- VERSION = "1.1.13"
3
+ VERSION = "1.1.14"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet_train-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.13
4
+ version: 1.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-08 00:00:00.000000000 Z
11
+ date: 2022-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: standard
@@ -187,6 +187,8 @@ files:
187
187
  - config/locales/en/platform/access_tokens.en.yml
188
188
  - config/locales/en/platform/applications.en.yml
189
189
  - config/routes.rb
190
+ - docs/api.md
191
+ - docs/api/versioning.md
190
192
  - lib/bullet_train/api.rb
191
193
  - lib/bullet_train/api/engine.rb
192
194
  - lib/bullet_train/api/strong_parameters_reporter.rb