slack-ruby-bot-server 2.2.1 → 2.3.0
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 +4 -4
- data/.github/workflows/test-mongodb.yml +0 -5
- data/.github/workflows/test-postgresql.yml +1 -6
- data/AGENTS.md +1 -1
- data/CHANGELOG.md +8 -0
- data/CONTRIBUTING.md +3 -1
- data/README.md +172 -1
- data/UPGRADING.md +24 -0
- data/lib/slack-ruby-bot-server/api/endpoints/teams_endpoint.rb +114 -103
- data/lib/slack-ruby-bot-server/api/middleware.rb +2 -1
- data/lib/slack-ruby-bot-server/api/presenters/status_presenter.rb +9 -3
- data/lib/slack-ruby-bot-server/config.rb +8 -0
- data/lib/slack-ruby-bot-server/rspec/fabricators/slack_ruby_bot_server_team.rb +5 -0
- data/lib/slack-ruby-bot-server/rspec/fabricators/team.rb +3 -5
- data/lib/slack-ruby-bot-server/rspec/fabricators.rb +4 -0
- data/lib/slack-ruby-bot-server/rspec.rb +1 -3
- data/lib/slack-ruby-bot-server/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8190478be418eff0f5da4e0cfede6b2203664c4e0812783575e9e00519b42b1c
|
|
4
|
+
data.tar.gz: 8a06246c7bd7052c5adf79b3aedfd95493408d909faca16eaca538f638b0aa9f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 26e3f8e38c8196c079ae1eaa8c5a6f7ec32cb71e963cb8e3f61f1f017dde997a739d0208d25f5a6dc1d84d38c9ba90b50072a16536b38a4b1444cbf9fbbc0e3b
|
|
7
|
+
data.tar.gz: 371ab36d34805242886ed780d01784535a133156fe633349389281effde66d62d607422ec6539fa892a430390eada2f5c6e66f0bd54be7591f17097db6b9ee94
|
|
@@ -21,12 +21,7 @@ jobs:
|
|
|
21
21
|
entry:
|
|
22
22
|
# These Active Support versions pass quirks_mode, removed in JSON 3.
|
|
23
23
|
- { ruby: '3.2', mongoid: '6.4.8', mongodb: '4.4', json: '~> 2.0' }
|
|
24
|
-
- { ruby: '3.2', mongoid: '7.2.3', mongodb: '4.4', json: '~> 2.0' }
|
|
25
|
-
- { ruby: '3.2', mongoid: '7.3.0', mongodb: '4.4', json: '~> 2.0' }
|
|
26
|
-
- { ruby: '3.3', mongoid: '7.3.0', mongodb: '5.0', json: '~> 2.0' }
|
|
27
24
|
- { ruby: '3.4', mongoid: '7.5.2', mongodb: '5.0', json: '~> 2.0' }
|
|
28
|
-
- { ruby: '3.4', mongoid: '8.1.11', mongodb: '6.0', json: '~> 2.0' }
|
|
29
|
-
- { ruby: '3.4', mongoid: '8.1.11', mongodb: '7.0', json: '~> 2.0' }
|
|
30
25
|
- { ruby: '3.4', mongoid: '8.1.11', mongodb: '8.0', json: '~> 2.0' }
|
|
31
26
|
- { ruby: '3.3', mongoid: '~> 9.1', mongodb: '8.0', grape: '~> 3.2', json: '~> 3.0' }
|
|
32
27
|
- { ruby: '3.4', mongoid: '~> 9.1', mongodb: '8.0', grape: '~> 4.0', json: '~> 3.0' }
|
|
@@ -20,14 +20,9 @@ jobs:
|
|
|
20
20
|
fail-fast: false
|
|
21
21
|
matrix:
|
|
22
22
|
entry:
|
|
23
|
-
# Active Support 6.x
|
|
24
|
-
- { ruby: '3.2', postgresql: '11', active_record: '~> 6.0.0', json: '~> 2.0' }
|
|
23
|
+
# Active Support 6.x and 8.0 pass quirks_mode, removed in JSON 3.
|
|
25
24
|
- { ruby: '3.2', postgresql: '11', active_record: '~> 6.1.0', json: '~> 2.0' }
|
|
26
|
-
- { ruby: '3.2', postgresql: '14', active_record: '~> 7.0.0', json: '~> 2.0' }
|
|
27
25
|
- { ruby: '3.3', postgresql: '14', active_record: '~> 7.2.0', json: '~> 3.0' }
|
|
28
|
-
- { ruby: '3.4', postgresql: '14', active_record: '~> 7.2.0', json: '~> 3.0' }
|
|
29
|
-
- { ruby: '3.4', postgresql: '15', active_record: '~> 8.0.2', json: '~> 2.0' }
|
|
30
|
-
- { ruby: '3.4', postgresql: '16', active_record: '~> 8.0.2', json: '~> 2.0' }
|
|
31
26
|
- { ruby: '3.4', postgresql: '17', active_record: '~> 8.0.2', json: '~> 2.0' }
|
|
32
27
|
- { ruby: '3.3', postgresql: '18', active_record: '~> 8.1', grape: '~> 3.2', json: '~> 3.0' }
|
|
33
28
|
- { ruby: '3.4', postgresql: '18', active_record: '~> 8.1', grape: '~> 4.0', json: '~> 3.0' }
|
data/AGENTS.md
CHANGED
|
@@ -44,7 +44,7 @@ Browser tests require Firefox. Selenium Manager provisions geckodriver when need
|
|
|
44
44
|
|
|
45
45
|
## Changes and Compatibility
|
|
46
46
|
|
|
47
|
-
Preserve both database adapters
|
|
47
|
+
Preserve both database adapters, representative legacy ORM major versions, and the modern Ruby/Grape 3/4 combinations in the six-job-per-adapter CI matrices. Avoid multiplying equivalent jobs across database server versions. Quote version strings in workflow YAML and validate workflow changes with `actionlint`.
|
|
48
48
|
|
|
49
49
|
Keep JSON 2 constraints scoped to affected legacy matrix jobs through `JSON_VERSION`; modern jobs explicitly test JSON 3. Do not add a blanket JSON upper bound to the Gemfile or gemspec.
|
|
50
50
|
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
### Changelog
|
|
2
2
|
|
|
3
|
+
### 2.3.0 (2026/10/25)
|
|
4
|
+
|
|
5
|
+
* [#188](https://github.com/slack-ruby/slack-ruby-bot-server/pull/188): Reduce the database CI matrices from 22 to 12 jobs while retaining representative legacy ORM majors and modern Ruby/Grape 3/4 coverage - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
|
|
6
|
+
* [#189](https://github.com/slack-ruby/slack-ruby-bot-server/pull/189): Add a prefixed Team fabricator entry point and preserve application-defined `:team` fabricators in the legacy RSpec helper - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
|
|
7
|
+
* [#186](https://github.com/slack-ruby/slack-ruby-bot-server/pull/186): Document persisted custom Team attributes for both adapters and Rails concern loading/reloading - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
|
|
8
|
+
* [#183](https://github.com/slack-ruby/slack-ruby-bot-server/pull/183): Add a `config.server_pages` block to configure page middleware options centrally without changing the installed library's defaults - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
|
|
9
|
+
* [#184](https://github.com/slack-ruby/slack-ruby-bot-server/pull/184): Add a configurable API application and separately mountable team read/registration endpoints, preserving the default API while supporting custom routes and registration-only applications - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
|
|
10
|
+
* [#185](https://github.com/slack-ruby/slack-ruby-bot-server/pull/185): Skip inactive installations in status checks and deactivate revoked credentials through service callbacks, returning an explicit failed ping instead of failing the whole status response - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
|
|
3
11
|
### 2.2.1 (2026/10/24)
|
|
4
12
|
|
|
5
13
|
* [#182](https://github.com/slack-ruby/slack-ruby-bot-server/pull/182): Modernize CI with current actions, native database services, dependency caching, and explicit Ruby 4, Mongoid 9, Active Record 8.1, PostgreSQL 18, Grape 3/4, and JSON 2/3 coverage while retaining existing compatibility combinations - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
|
data/CONTRIBUTING.md
CHANGED
|
@@ -39,7 +39,9 @@ bundle exec rake
|
|
|
39
39
|
|
|
40
40
|
For PostgreSQL, set `DATABASE_ADAPTER=activerecord`, `ACTIVERECORD_VERSION`, and `DATABASE_URL` instead. `GRAPE_VERSION` optionally constrains the Grape version for either adapter; without it, Bundler selects a compatible version. CI preserves the older compatibility combinations and adds explicit Grape 3 and 4 jobs with current ORM versions, including Ruby 4. Each database runs in a health-checked service container, while Firefox runs under Xvfb. Selenium Manager provisions the matching driver.
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
CI runs six jobs per adapter: three representative legacy ORM/database stacks and three modern combinations (Ruby 3.3/Grape 3, Ruby 3.4/Grape 4, and Ruby 4/Grape 4). Mongoid retains 6.4, 7.5, 8.1, and 9.1; Active Record retains 6.1, 7.2, 8.0, and 8.1. Redundant database-server variants and older ORM minor combinations are omitted. A combination no longer appearing in CI is not by itself a change to the gem's supported runtime dependencies.
|
|
43
|
+
|
|
44
|
+
`JSON_VERSION` optionally constrains the development/test JSON dependency; otherwise it is unrestricted. CI uses JSON 2 for Mongoid 6-8 and Active Record 6.1 and 8.0 because their Active Support versions pass `quirks_mode`, removed in JSON 3. Mongoid 9, Active Record 7.2, and Active Record 8.1 jobs explicitly test JSON 3. These selectors do not restrict the published gem's runtime dependencies.
|
|
43
45
|
|
|
44
46
|
On Linux, install [Firefox](https://www.mozilla.org/firefox/) and `Xvfb`, then run:
|
|
45
47
|
|
data/README.md
CHANGED
|
@@ -17,9 +17,15 @@ Build a complete Slack bot service with Slack button integration, in Ruby.
|
|
|
17
17
|
- [Storage](#storage)
|
|
18
18
|
- [MongoDB](#mongodb)
|
|
19
19
|
- [ActiveRecord](#activerecord)
|
|
20
|
+
- [Extending Team](#extending-team)
|
|
21
|
+
- [Mongoid Attributes](#mongoid-attributes)
|
|
22
|
+
- [Active Record Attributes](#active-record-attributes)
|
|
23
|
+
- [Rails Loading and Reloading](#rails-loading-and-reloading)
|
|
20
24
|
- [OAuth Version and Scopes](#oauth-version-and-scopes)
|
|
21
25
|
- [Slack App](#slack-app)
|
|
22
26
|
- [API](#api)
|
|
27
|
+
- [Custom Endpoints](#custom-endpoints)
|
|
28
|
+
- [Team Endpoint Visibility](#team-endpoint-visibility)
|
|
23
29
|
- [App](#app)
|
|
24
30
|
- [Service Manager](#service-manager)
|
|
25
31
|
- [Lifecycle Callbacks](#lifecycle-callbacks)
|
|
@@ -27,7 +33,9 @@ Build a complete Slack bot service with Slack button integration, in Ruby.
|
|
|
27
33
|
- [Extensions](#extensions)
|
|
28
34
|
- [Service Class](#service-class)
|
|
29
35
|
- [HTML Templates](#html-templates)
|
|
36
|
+
- [Page Middleware Configuration](#page-middleware-configuration)
|
|
30
37
|
- [Access Tokens](#access-tokens)
|
|
38
|
+
- [Testing](#testing)
|
|
31
39
|
- [Sample Bots Using Slack Ruby Bot Server](#sample-bots-using-slack-ruby-bot-server)
|
|
32
40
|
- [Copyright & License](#copyright--license)
|
|
33
41
|
|
|
@@ -37,7 +45,7 @@ A library that contains a web server and a RESTful [Grape](http://github.com/rub
|
|
|
37
45
|
|
|
38
46
|
## Stable Release
|
|
39
47
|
|
|
40
|
-
You're reading the documentation for the **stable** release of slack-ruby-bot-server, 2.
|
|
48
|
+
You're reading the documentation for the **stable** release of slack-ruby-bot-server, 2.3.0. See [UPGRADING](UPGRADING.md) when upgrading from an older version. See [MIGRATING](MIGRATING.md) for help with migrating Legacy Slack Apps to Granular Scopes.
|
|
41
49
|
|
|
42
50
|
## Make Your Own
|
|
43
51
|
|
|
@@ -107,6 +115,76 @@ db_config = if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1'
|
|
|
107
115
|
ActiveRecord::Base.establish_connection(db_config)
|
|
108
116
|
```
|
|
109
117
|
|
|
118
|
+
#### Extending Team
|
|
119
|
+
|
|
120
|
+
The installation model is the top-level `::Team` class for both adapters, not `SlackRubyBotServer::Models::Team`. Load `slack-ruby-bot-server` before extending it. The model already includes its database adapter and timestamps; do not include them again or declare a different superclass.
|
|
121
|
+
|
|
122
|
+
##### Mongoid Attributes
|
|
123
|
+
|
|
124
|
+
Add fields by reopening the existing class in an application file, for example `lib/team_extensions.rb`:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
require 'slack-ruby-bot-server'
|
|
128
|
+
|
|
129
|
+
class Team
|
|
130
|
+
field :subscription_plan, type: String, default: 'free'
|
|
131
|
+
|
|
132
|
+
def paid_plan?
|
|
133
|
+
subscription_plan == 'paid'
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Require that file from your startup code after loading the framework and before serving requests or starting the service. Mongoid does not require a schema migration for the new field.
|
|
139
|
+
|
|
140
|
+
##### Active Record Attributes
|
|
141
|
+
|
|
142
|
+
Add a column to the existing `teams` table with an application migration. Use your application's migration version; this example is also compatible with older supported Active Record versions:
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
class AddSubscriptionPlanToTeams < ActiveRecord::Migration[6.0]
|
|
146
|
+
def change
|
|
147
|
+
add_column :teams, :subscription_plan, :string, default: 'free', null: false
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Run the migration before code uses the attribute. Active Record provides its reader and writer from the column; do not use `attr_accessor`, which would bypass persistence. Add methods by reopening `class Team` after loading the framework, as above, but omit the Mongoid-only `field` declaration.
|
|
153
|
+
|
|
154
|
+
##### Rails Loading and Reloading
|
|
155
|
+
|
|
156
|
+
Putting another `app/models/team.rb` in a Rails app is not a reliable way to extend a class already loaded by a gem. Put reloadable behavior in a concern instead:
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
# app/models/concerns/team_extensions.rb
|
|
160
|
+
module TeamExtensions
|
|
161
|
+
extend ActiveSupport::Concern
|
|
162
|
+
|
|
163
|
+
def paid_plan?
|
|
164
|
+
subscription_plan == 'paid'
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Include it in `::Team` from a reloader callback, rather than autoloading the concern directly during initializer execution:
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
# config/initializers/team_extensions.rb
|
|
173
|
+
require 'slack-ruby-bot-server'
|
|
174
|
+
|
|
175
|
+
if SlackRubyBotServer.config.mongoid?
|
|
176
|
+
::Team.field :subscription_plan, type: String, default: 'free'
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
Rails.application.config.to_prepare do
|
|
180
|
+
::Team.include(TeamExtensions)
|
|
181
|
+
end
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
With Active Record, use the migration above instead of declaring a field in the initializer. The callback runs at boot and after development reloads; including the same concern more than once is safe. Keep one-time field declarations outside the callback. Do not query the database or swallow database errors just to attach methods: the extension can be loaded before the `teams` table exists.
|
|
185
|
+
|
|
186
|
+
The framework currently owns the global `Team` constant. An unrelated application model with the same name is a separate [name-collision limitation](https://github.com/slack-ruby/slack-ruby-bot-server/pull/83), not an extension mechanism.
|
|
187
|
+
|
|
110
188
|
### OAuth Version and Scopes
|
|
111
189
|
|
|
112
190
|
Configure your app's [OAuth version](https://api.slack.com/authentication/oauth-v2) and [scopes](https://api.slack.com/legacy/oauth-scopes) as needed by your application.
|
|
@@ -151,6 +229,58 @@ Navigate to either [localhost:9292](http://localhost:9292) or the ngrok URL abov
|
|
|
151
229
|
|
|
152
230
|
This library implements an app, [SlackRubyBotServer::App](lib/slack-ruby-bot-server/app.rb) and a service manager, [SlackRubyBotServer::Service](lib/slack-ruby-bot-server/service.rb). It also provides [default HTML templates and JS scripts](public) for Slack integration.
|
|
153
231
|
|
|
232
|
+
#### Custom Endpoints
|
|
233
|
+
|
|
234
|
+
Set `config.api` to a Rack application, typically a `Grape::API` class, to customize the API without reopening the framework's classes. The default is `nil`, which uses `SlackRubyBotServer::Api::Endpoints::RootEndpoint`, including extensions mounted there by other gems. `SlackRubyBotServer::Config.reset!` restores this default.
|
|
235
|
+
|
|
236
|
+
To add an endpoint alongside the default API:
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
class ExtraApi < Grape::API
|
|
240
|
+
prefix 'api'
|
|
241
|
+
format :json
|
|
242
|
+
|
|
243
|
+
get :foobar do
|
|
244
|
+
{ ok: true }
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
class ApplicationApi < Grape::API
|
|
249
|
+
mount SlackRubyBotServer::Api::Endpoints::RootEndpoint
|
|
250
|
+
mount ExtraApi
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
SlackRubyBotServer.configure do |config|
|
|
254
|
+
config.api = ApplicationApi
|
|
255
|
+
end
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Configure this before starting the application. In `config.ru`, continue running `SlackRubyBotServer::Api::Middleware.instance`; both `/api/foobar` and the existing `/api` routes then share the framework's CORS, URL-rewrite, and page middleware. A separate `Rack::Cascade` stack is unnecessary.
|
|
259
|
+
|
|
260
|
+
In Rails, require the file defining `ApplicationApi` from your initializer before assigning `config.api`. For reloadable classes, make the assignment inside `Rails.application.config.to_prepare` so it references the current class. Mount `SlackRubyBotServer::Api::Middleware.instance` in your routes, for example `mount SlackRubyBotServer::Api::Middleware.instance => '/'`, rather than bypassing that middleware stack.
|
|
261
|
+
|
|
262
|
+
#### Team Endpoint Visibility
|
|
263
|
+
|
|
264
|
+
The default API exposes `GET /api/teams` and `GET /api/teams/:id` publicly. To retain OAuth registration without those read endpoints, use the separately mountable `TeamsEndpoint::Create`. `TeamsEndpoint::Read` contains the list and detail routes; `TeamsEndpoint` continues to mount both for backward compatibility.
|
|
265
|
+
|
|
266
|
+
```ruby
|
|
267
|
+
class RegistrationApi < Grape::API
|
|
268
|
+
include SlackRubyBotServer::Api::Helpers::ErrorHelpers
|
|
269
|
+
|
|
270
|
+
prefix 'api'
|
|
271
|
+
format :json
|
|
272
|
+
formatter :json, Grape::Formatter::Roar
|
|
273
|
+
|
|
274
|
+
mount SlackRubyBotServer::Api::Endpoints::TeamsEndpoint::Create
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
SlackRubyBotServer.configure do |config|
|
|
278
|
+
config.api = RegistrationApi
|
|
279
|
+
end
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
This replaces the default root API: it does not expose its status, root discovery, Swagger, or team-read routes. Do not also mount `RootEndpoint` or `TeamsEndpoint`, since either would restore the public team reads. If you use the Events extension or other custom endpoints, explicitly mount their routes with their required signature verification too. Add your own authentication before mounting `TeamsEndpoint::Read` if you need protected team queries. OAuth registration still requires a valid Slack authorization code; this configuration does not add a general authentication layer.
|
|
283
|
+
|
|
154
284
|
#### App
|
|
155
285
|
|
|
156
286
|
The app instance checks for a working database connection, ensures indexes, performs migrations, sets up bot aliases and log levels. You can introduce custom behavior into the app lifecycle by subclassing `SlackRubyBotServer::App` and creating an instance of the child class in `config.ru`.
|
|
@@ -307,10 +437,51 @@ SlackRubyBotServer.configure do |config|
|
|
|
307
437
|
end
|
|
308
438
|
```
|
|
309
439
|
|
|
440
|
+
#### Page Middleware Configuration
|
|
441
|
+
|
|
442
|
+
Use `config.server_pages` to configure the underlying [`Rack::ServerPages`](https://github.com/migrs/rack-server-pages) middleware in an initializer, before `SlackRubyBotServer::Api::Middleware.instance` is first accessed. The block receives its configuration object after the framework applies `view_paths`, so it can customize any option supported by your installed `rack-server-pages` version.
|
|
443
|
+
|
|
444
|
+
For example, with `rack-server-pages` 0.3.0 or newer:
|
|
445
|
+
|
|
446
|
+
```ruby
|
|
447
|
+
SlackRubyBotServer.configure do |config|
|
|
448
|
+
config.server_pages do |pages|
|
|
449
|
+
pages.cache_control = 'private, no-cache'
|
|
450
|
+
pages.static_cache_control = 'public, no-cache'
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
`cache_control` sets the default `Cache-Control` header for rendered templates, including generated CSS. `static_cache_control` sets it for static files. `no-cache` allows storage but requires revalidation before reuse; use `private, no-store` for sensitive pages. You can also configure `static_etags` and `static_timestamps` with version 0.3.0 or newer. These settings do not change downstream API responses.
|
|
456
|
+
|
|
457
|
+
Without a block, the framework preserves the installed library's defaults and existing `view_paths` behavior. Registering another block replaces the previous one, and `SlackRubyBotServer::Config.reset!` clears it. The block runs when the middleware stack is built, not on each request. If you change configuration after initialization, rebuild the stack with `SlackRubyBotServer::Api::Middleware.reset!`; configure it before startup in production.
|
|
458
|
+
|
|
310
459
|
### Access Tokens
|
|
311
460
|
|
|
312
461
|
By default the implementation of [Team](lib/slack-ruby-bot-server/models/team) stores the value of the token with all the requested OAuth scopes in both `token` and `activated_user_access_token` (for backwards compatibility), along with `oauth_version` and `oauth_scope`. If a legacy Slack bot integration `bot_access_token` is present, it is stored as `token`, and `activated_user_access_token` is the token that has all the requested OAuth scopes.
|
|
313
462
|
|
|
463
|
+
## Testing
|
|
464
|
+
|
|
465
|
+
The optional test helpers use [Fabrication](https://fabricationgem.org/) and Faker. Add `fabrication` and `faker` to your application's test dependencies; neither is a runtime dependency of this gem.
|
|
466
|
+
|
|
467
|
+
To use the framework's installation defaults without reserving your application's `:team` fabricator name, load the prefixed-only helper:
|
|
468
|
+
|
|
469
|
+
```ruby
|
|
470
|
+
require 'slack-ruby-bot-server/rspec/fabricators'
|
|
471
|
+
|
|
472
|
+
team = Fabricate(:slack_ruby_bot_server_team)
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
This fabricator creates the existing `::Team` model with unique token/team ID sequences and a generated name. Applications can register their own `:team` before or after this helper, or inherit the framework defaults:
|
|
476
|
+
|
|
477
|
+
```ruby
|
|
478
|
+
Fabricator(:team, from: :slack_ruby_bot_server_team) do
|
|
479
|
+
name { 'My application team' }
|
|
480
|
+
end
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
The existing `require 'slack-ruby-bot-server/rspec'` remains supported. It loads the prefixed fabricator and registers the legacy `:team` alias only when no `:team` definition already exists. If your application defines `:team` later, replace the legacy require with `slack-ruby-bot-server/rspec/fabricators`; loading both entry points still opts into the legacy alias.
|
|
484
|
+
|
|
314
485
|
## Sample Bots Using Slack Ruby Bot Server
|
|
315
486
|
|
|
316
487
|
* [slack-ruby-bot-server-events-sample](https://github.com/slack-ruby/slack-ruby-bot-server-events-sample), a generic sample
|
data/UPGRADING.md
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
Upgrading Slack-Ruby-Bot-Server
|
|
2
2
|
===============================
|
|
3
3
|
|
|
4
|
+
### Upgrading to >= 2.3.0
|
|
5
|
+
|
|
6
|
+
#### Recommended Test Helper Migration
|
|
7
|
+
|
|
8
|
+
If your application defines its own `:team` fabricator, replace `require 'slack-ruby-bot-server/rspec'` with `require 'slack-ruby-bot-server/rspec/fabricators'`. This is recommended, not required: the new entry point registers only `:slack_ruby_bot_server_team`, leaving `:team` available regardless of whether your application's fabricator is loaded before or after the helper.
|
|
9
|
+
|
|
10
|
+
Use `Fabricate(:slack_ruby_bot_server_team)` for the framework defaults, or inherit them in your application's fabricator:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
require 'slack-ruby-bot-server/rspec/fabricators'
|
|
14
|
+
|
|
15
|
+
Fabricator(:team, from: :slack_ruby_bot_server_team) do
|
|
16
|
+
name { 'My application team' }
|
|
17
|
+
end
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The legacy helper remains supported and registers `:team` only if no definition already exists. Replace the old require rather than adding the new one alongside it; loading both still opts into the legacy alias and can conflict with a `:team` definition loaded later.
|
|
21
|
+
|
|
22
|
+
#### Status Checks and Inactive Installations
|
|
23
|
+
|
|
24
|
+
`GET /api/status` now pings only active installations. When Slack returns `account_inactive` or `invalid_auth`, the installation is deactivated through `Service#deactivate!`, and the endpoint returns HTTP `200` with `ping: { "ok": false, "error": "account_inactive" }` (or `"invalid_auth"`). The active-team count reflects that deactivation. Monitoring should inspect the failed `ping` as well as the HTTP status.
|
|
25
|
+
|
|
26
|
+
When no active installations remain, no Slack ping is attempted. Other Slack errors retain their existing error response and do not deactivate installations.
|
|
27
|
+
|
|
4
28
|
### Upgrading to >= 2.2.0
|
|
5
29
|
|
|
6
30
|
#### Cursor Helper Iterator Changes
|
|
@@ -2,118 +2,129 @@ module SlackRubyBotServer
|
|
|
2
2
|
module Api
|
|
3
3
|
module Endpoints
|
|
4
4
|
class TeamsEndpoint < Grape::API
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
namespace :teams do
|
|
11
|
-
desc 'Get a team.'
|
|
12
|
-
params do
|
|
13
|
-
requires :id, type: String, desc: 'Team ID.'
|
|
14
|
-
end
|
|
15
|
-
get ':id' do
|
|
16
|
-
team = Team.find(params[:id]) || error!('Not Found', 404)
|
|
17
|
-
present team, with: Presenters::TeamPresenter
|
|
18
|
-
end
|
|
5
|
+
class Read < Grape::API
|
|
6
|
+
format :json
|
|
7
|
+
helpers Helpers::CursorHelpers
|
|
8
|
+
helpers Helpers::SortHelpers
|
|
9
|
+
helpers Helpers::PaginationParameters
|
|
19
10
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
teams = paginate_and_sort_by_cursor(teams, default_sort_order: '-id')
|
|
30
|
-
present teams, with: Presenters::TeamsPresenter
|
|
31
|
-
end
|
|
11
|
+
namespace :teams do
|
|
12
|
+
desc 'Get a team.'
|
|
13
|
+
params do
|
|
14
|
+
requires :id, type: String, desc: 'Team ID.'
|
|
15
|
+
end
|
|
16
|
+
get ':id' do
|
|
17
|
+
team = Team.find(params[:id]) || error!('Not Found', 404)
|
|
18
|
+
present team, with: Presenters::TeamPresenter
|
|
19
|
+
end
|
|
32
20
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
client_id: ENV.fetch('SLACK_CLIENT_ID', nil),
|
|
45
|
-
client_secret: ENV.fetch('SLACK_CLIENT_SECRET', nil),
|
|
46
|
-
code: params[:code]
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
rc = client.send(SlackRubyBotServer.config.oauth_access_method, options)
|
|
50
|
-
|
|
51
|
-
token = nil
|
|
52
|
-
access_token = nil
|
|
53
|
-
user_id = nil
|
|
54
|
-
bot_user_id = nil
|
|
55
|
-
team_id = nil
|
|
56
|
-
team_name = nil
|
|
57
|
-
oauth_scope = nil
|
|
58
|
-
oauth_version = SlackRubyBotServer::Config.oauth_version
|
|
59
|
-
|
|
60
|
-
case oauth_version
|
|
61
|
-
when :v2
|
|
62
|
-
access_token = rc.access_token
|
|
63
|
-
token = rc.access_token
|
|
64
|
-
user_id = rc.authed_user&.id
|
|
65
|
-
bot_user_id = rc.bot_user_id
|
|
66
|
-
team_id = rc.team&.id
|
|
67
|
-
team_name = rc.team&.name
|
|
68
|
-
oauth_scope = rc.scope
|
|
69
|
-
when :v1
|
|
70
|
-
access_token = rc.access_token
|
|
71
|
-
bot = rc.bot if rc.key?(:bot)
|
|
72
|
-
token = bot ? bot.bot_access_token : access_token
|
|
73
|
-
user_id = rc.user_id
|
|
74
|
-
bot_user_id = bot ? bot.bot_user_id : nil
|
|
75
|
-
team_id = rc.team_id
|
|
76
|
-
team_name = rc.team_name
|
|
77
|
-
oauth_scope = rc.scope
|
|
21
|
+
desc 'Get all the teams.'
|
|
22
|
+
params do
|
|
23
|
+
optional :active, type: ::Grape::API::Boolean, desc: 'Return active teams only.'
|
|
24
|
+
use :pagination
|
|
25
|
+
end
|
|
26
|
+
sort Team::SORT_ORDERS
|
|
27
|
+
get do
|
|
28
|
+
teams = Team.all
|
|
29
|
+
teams = teams.active if params[:active]
|
|
30
|
+
teams = paginate_and_sort_by_cursor(teams, default_sort_order: '-id')
|
|
31
|
+
present teams, with: Presenters::TeamsPresenter
|
|
78
32
|
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Create < Grape::API
|
|
37
|
+
format :json
|
|
79
38
|
|
|
80
|
-
|
|
81
|
-
team
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
team.ping_if_active!
|
|
86
|
-
|
|
87
|
-
team.update!(
|
|
88
|
-
oauth_version: oauth_version,
|
|
89
|
-
oauth_scope: oauth_scope,
|
|
90
|
-
activated_user_id: user_id,
|
|
91
|
-
activated_user_access_token: access_token,
|
|
92
|
-
bot_user_id: bot_user_id
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
raise "Team #{team.name} is already registered." if team.active?
|
|
96
|
-
|
|
97
|
-
team.activate!(token)
|
|
98
|
-
else
|
|
99
|
-
team = Team.create!(
|
|
100
|
-
token: token,
|
|
101
|
-
oauth_version: oauth_version,
|
|
102
|
-
oauth_scope: oauth_scope,
|
|
103
|
-
team_id: team_id,
|
|
104
|
-
name: team_name,
|
|
105
|
-
activated_user_id: user_id,
|
|
106
|
-
activated_user_access_token: access_token,
|
|
107
|
-
bot_user_id: bot_user_id
|
|
108
|
-
)
|
|
39
|
+
namespace :teams do
|
|
40
|
+
desc 'Create a team using an OAuth token.'
|
|
41
|
+
params do
|
|
42
|
+
requires :code, type: String
|
|
43
|
+
optional :state, type: String
|
|
109
44
|
end
|
|
45
|
+
post do
|
|
46
|
+
client = Slack::Web::Client.new
|
|
47
|
+
|
|
48
|
+
raise 'Missing SLACK_CLIENT_ID or SLACK_CLIENT_SECRET.' unless ENV.key?('SLACK_CLIENT_ID') && ENV.key?('SLACK_CLIENT_SECRET')
|
|
110
49
|
|
|
111
|
-
|
|
50
|
+
options = {
|
|
51
|
+
client_id: ENV.fetch('SLACK_CLIENT_ID', nil),
|
|
52
|
+
client_secret: ENV.fetch('SLACK_CLIENT_SECRET', nil),
|
|
53
|
+
code: params[:code]
|
|
54
|
+
}
|
|
112
55
|
|
|
113
|
-
|
|
114
|
-
|
|
56
|
+
rc = client.send(SlackRubyBotServer.config.oauth_access_method, options)
|
|
57
|
+
|
|
58
|
+
token = nil
|
|
59
|
+
access_token = nil
|
|
60
|
+
user_id = nil
|
|
61
|
+
bot_user_id = nil
|
|
62
|
+
team_id = nil
|
|
63
|
+
team_name = nil
|
|
64
|
+
oauth_scope = nil
|
|
65
|
+
oauth_version = SlackRubyBotServer::Config.oauth_version
|
|
66
|
+
|
|
67
|
+
case oauth_version
|
|
68
|
+
when :v2
|
|
69
|
+
access_token = rc.access_token
|
|
70
|
+
token = rc.access_token
|
|
71
|
+
user_id = rc.authed_user&.id
|
|
72
|
+
bot_user_id = rc.bot_user_id
|
|
73
|
+
team_id = rc.team&.id
|
|
74
|
+
team_name = rc.team&.name
|
|
75
|
+
oauth_scope = rc.scope
|
|
76
|
+
when :v1
|
|
77
|
+
access_token = rc.access_token
|
|
78
|
+
bot = rc.bot if rc.key?(:bot)
|
|
79
|
+
token = bot ? bot.bot_access_token : access_token
|
|
80
|
+
user_id = rc.user_id
|
|
81
|
+
bot_user_id = bot ? bot.bot_user_id : nil
|
|
82
|
+
team_id = rc.team_id
|
|
83
|
+
team_name = rc.team_name
|
|
84
|
+
oauth_scope = rc.scope
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
team = Team.where(token: token).first
|
|
88
|
+
team ||= Team.where(team_id: team_id, oauth_version: oauth_version).first
|
|
89
|
+
team ||= Team.where(team_id: team_id).first
|
|
90
|
+
|
|
91
|
+
if team
|
|
92
|
+
team.ping_if_active!
|
|
93
|
+
|
|
94
|
+
team.update!(
|
|
95
|
+
oauth_version: oauth_version,
|
|
96
|
+
oauth_scope: oauth_scope,
|
|
97
|
+
activated_user_id: user_id,
|
|
98
|
+
activated_user_access_token: access_token,
|
|
99
|
+
bot_user_id: bot_user_id
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
raise "Team #{team.name} is already registered." if team.active?
|
|
103
|
+
|
|
104
|
+
team.activate!(token)
|
|
105
|
+
else
|
|
106
|
+
team = Team.create!(
|
|
107
|
+
token: token,
|
|
108
|
+
oauth_version: oauth_version,
|
|
109
|
+
oauth_scope: oauth_scope,
|
|
110
|
+
team_id: team_id,
|
|
111
|
+
name: team_name,
|
|
112
|
+
activated_user_id: user_id,
|
|
113
|
+
activated_user_access_token: access_token,
|
|
114
|
+
bot_user_id: bot_user_id
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
options = params.slice(:state)
|
|
119
|
+
|
|
120
|
+
Service.instance.create!(team, options)
|
|
121
|
+
present team, with: Presenters::TeamPresenter
|
|
122
|
+
end
|
|
115
123
|
end
|
|
116
124
|
end
|
|
125
|
+
|
|
126
|
+
mount Read
|
|
127
|
+
mount Create
|
|
117
128
|
end
|
|
118
129
|
end
|
|
119
130
|
end
|
|
@@ -30,6 +30,7 @@ module SlackRubyBotServer
|
|
|
30
30
|
|
|
31
31
|
use Rack::ServerPages do |config|
|
|
32
32
|
config.view_path = SlackRubyBotServer::Config.view_paths
|
|
33
|
+
SlackRubyBotServer::Config.server_pages&.call(config)
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
run Middleware.new
|
|
@@ -37,7 +38,7 @@ module SlackRubyBotServer
|
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
def call(env)
|
|
40
|
-
Endpoints::RootEndpoint.call(env)
|
|
41
|
+
(SlackRubyBotServer::Config.api || Endpoints::RootEndpoint).call(env)
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
end
|
|
@@ -10,21 +10,27 @@ module SlackRubyBotServer
|
|
|
10
10
|
"#{base_url(opts)}/status"
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
property :ping
|
|
13
14
|
property :teams_count
|
|
14
15
|
property :active_teams_count
|
|
15
|
-
property :ping
|
|
16
16
|
|
|
17
17
|
def ping
|
|
18
18
|
if SlackRubyBotServer::Config.mongoid?
|
|
19
|
-
team = Team.asc(:_id).first
|
|
19
|
+
team = Team.active.asc(:_id).first
|
|
20
20
|
elsif SlackRubyBotServer::Config.activerecord?
|
|
21
|
-
team = Team.last
|
|
21
|
+
team = Team.active.last
|
|
22
22
|
else
|
|
23
23
|
raise 'Unsupported database driver.'
|
|
24
24
|
end
|
|
25
25
|
return unless team
|
|
26
26
|
|
|
27
27
|
team.ping!
|
|
28
|
+
rescue Slack::Web::Api::Errors::SlackError => e
|
|
29
|
+
raise unless %w[account_inactive invalid_auth].include?(e.message)
|
|
30
|
+
|
|
31
|
+
Middleware.logger.warn "Status ping failed for #{team}: #{e.message}; deactivating installation."
|
|
32
|
+
Service.instance.deactivate!(team)
|
|
33
|
+
{ ok: false, error: e.message }
|
|
28
34
|
end
|
|
29
35
|
|
|
30
36
|
def teams_count
|
|
@@ -8,12 +8,15 @@ module SlackRubyBotServer
|
|
|
8
8
|
attr_accessor :view_paths
|
|
9
9
|
attr_accessor :oauth_scope
|
|
10
10
|
attr_accessor :oauth_version
|
|
11
|
+
attr_accessor :api
|
|
11
12
|
|
|
12
13
|
def reset!
|
|
13
14
|
self.logger = nil
|
|
14
15
|
self.service_class = SlackRubyBotServer::Service
|
|
15
16
|
self.oauth_scope = nil
|
|
16
17
|
self.oauth_version = :v2
|
|
18
|
+
@server_pages = nil
|
|
19
|
+
self.api = nil
|
|
17
20
|
|
|
18
21
|
self.view_paths = [
|
|
19
22
|
'views',
|
|
@@ -56,6 +59,11 @@ module SlackRubyBotServer
|
|
|
56
59
|
oauth_scope&.join(',')
|
|
57
60
|
end
|
|
58
61
|
|
|
62
|
+
def server_pages(&block)
|
|
63
|
+
@server_pages = block if block
|
|
64
|
+
@server_pages
|
|
65
|
+
end
|
|
66
|
+
|
|
59
67
|
def activerecord?
|
|
60
68
|
database_adapter == :activerecord
|
|
61
69
|
end
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
name { Faker::Lorem.word }
|
|
5
|
-
end
|
|
1
|
+
require_relative 'slack_ruby_bot_server_team'
|
|
2
|
+
|
|
3
|
+
Fabricator(:team, from: :slack_ruby_bot_server_team) unless Fabrication.manager[:team]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: slack-ruby-bot-server
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Doubrovkine
|
|
@@ -225,6 +225,8 @@ files:
|
|
|
225
225
|
- lib/slack-ruby-bot-server/models/team/methods.rb
|
|
226
226
|
- lib/slack-ruby-bot-server/models/team/mongoid.rb
|
|
227
227
|
- lib/slack-ruby-bot-server/rspec.rb
|
|
228
|
+
- lib/slack-ruby-bot-server/rspec/fabricators.rb
|
|
229
|
+
- lib/slack-ruby-bot-server/rspec/fabricators/slack_ruby_bot_server_team.rb
|
|
228
230
|
- lib/slack-ruby-bot-server/rspec/fabricators/team.rb
|
|
229
231
|
- lib/slack-ruby-bot-server/service.rb
|
|
230
232
|
- lib/slack-ruby-bot-server/version.rb
|