graphql-anycable 0.3.3 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '004247714780caa6cff73438d8cbb797c87aa7fd8136a302eb55ac0ce1ea8c69'
4
- data.tar.gz: 121edfdd3dbe9c8fd2fc6ac943c5bd0516f11ff98ff3e1aca3168bba102ba07e
3
+ metadata.gz: 2a2206509c7d0e76ae86f48a40a2983f4519f71be6e7ce040be69ffebc899c91
4
+ data.tar.gz: 218a906bfa8bc0349870051252d49451d4f3f0fe2441c179f84082b88ddf0b21
5
5
  SHA512:
6
- metadata.gz: b75a1491d23c2767ff6336db37613e54c056bbf8510b7ba08c7ccb988826ab28160d165a421821fb6e3da663d5341f4f86e40029c806658ac5941a897d19a326
7
- data.tar.gz: 852bb3c618ad311b42be61254e54f178fd578a32c7ef52f5de309899cf010a58c22b2ddc445ff364f4af4bc3a890eede21458425e0436ebc5f141bdc7398d414
6
+ metadata.gz: f5bd487aceb0b69041d5e2a5357f3477917a88e50b7569cf4c21ce3def5cf1c812e28675769dcedd53905d14423b2ace1eb88352ded215cdaa56fa1f6f417f96
7
+ data.tar.gz: 8913553cb1eb8211cedb941d782df676b4821287684de6f2c0029738313aa2b502503b01f75b1f0faad7a5bc0927a2a7893efe407b65d88b7ec83581afd68b52
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve graphql-anycable
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **Versions**
14
+ ruby:
15
+ rails (or other framework):
16
+ graphql:
17
+ graphql-anycable:
18
+ anycable:
19
+
20
+ **GraphQL schema**
21
+ Provide relevant details. Are you using [subscription classes](https://graphql-ruby.org/subscriptions/subscription_classes.html) or not (graphql-ruby behavior differs there)?
22
+
23
+ ```ruby
24
+ class Product < GraphQL::Schema::Object
25
+ field :id, ID, null: false, hash_key: :id
26
+ field :title, String, null: true, hash_key: :title
27
+ end
28
+
29
+ class SubscriptionType < GraphQL::Schema::Object
30
+ field :product_created, Product, null: false
31
+ field :product_updated, Product, null: false
32
+
33
+ def product_created; end
34
+ def product_updated; end
35
+ end
36
+
37
+ class ApplicationSchema < GraphQL::Schema
38
+ subscription SubscriptionType
39
+ end
40
+ ```
41
+
42
+ **GraphQL query**
43
+
44
+ How do you subscribe to subscriptions?
45
+
46
+ ```graphql
47
+ subscription {
48
+ productCreated { id title }
49
+ productUpdated { id }
50
+ }
51
+ ```
52
+
53
+ **Steps to reproduce**
54
+ Steps to reproduce the behavior
55
+
56
+ **Expected behavior**
57
+ A clear and concise description of what you expected to happen.
58
+
59
+ **Actual behavior**
60
+ What specifically went wrong?
61
+
62
+ **Additional context**
63
+ Add any other context about the problem here. Tracebacks, your thoughts. anything that may be useful.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
data/.travis.yml CHANGED
@@ -10,6 +10,8 @@ rvm:
10
10
  gemfile:
11
11
  - gemfiles/graphql_1.9.gemfile
12
12
  - gemfiles/graphql_1.10.gemfile
13
+ - gemfiles/anycable_0.6.gemfile
14
+ - gemfiles/anycable_1.0.gemfile
13
15
  before_install: gem install bundler -v "~> 2.0"
14
16
 
15
17
  matrix:
@@ -17,3 +19,17 @@ matrix:
17
19
  # grpc gem isn't ready for ruby 2.7 yet: https://github.com/grpc/grpc/issues/21514
18
20
  # goole-protobuf gem isn't ready for ruby 2.7 yet: https://github.com/protocolbuffers/protobuf/issues/7070
19
21
  - rvm: 2.7.0
22
+ # Exclude new dependencies on old rubies to run less jobs
23
+ - rvm: 2.4.9
24
+ gemfile: gemfiles/anycable_1.0.gemfile
25
+ - rvm: 2.4.9
26
+ gemfile: gemfiles/graphql_1.10.gemfile
27
+ - rvm: 2.5.7
28
+ gemfile: gemfiles/anycable_1.0.gemfile
29
+ - rvm: 2.5.7
30
+ gemfile: gemfiles/graphql_1.10.gemfile
31
+ # Exclude old dependencies on new rubies to run less jobs
32
+ - rvm: 2.6.5
33
+ gemfile: gemfiles/anycable_0.6.gemfile
34
+ - rvm: 2.6.5
35
+ gemfile: gemfiles/graphql_1.9.gemfile
data/Appraisals CHANGED
@@ -5,3 +5,11 @@ end
5
5
  appraise "graphql-1.10" do
6
6
  gem "graphql", "~> 1.10.0"
7
7
  end
8
+
9
+ appraise "anycable-0.6" do
10
+ gem "anycable", "~> 0.6.0"
11
+ end
12
+
13
+ appraise "anycable-1.0" do
14
+ gem "anycable", "~> 1.0.0.preview1"
15
+ end
data/CHANGELOG.md ADDED
@@ -0,0 +1,53 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
+ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
+
8
+ ## 0.4.0 - 2020-03-19
9
+
10
+ ### Added
11
+
12
+ - Ability to configure the gem via `configure` block, in addition to enironment variables and yaml files. [@gsamokovarov] ([#11](https://github.com/Envek/graphql-anycable/pull/11))
13
+ - Ability to run Redis cleaning operations outside of Rake. [@gsamokovarov] ([#11](https://github.com/Envek/graphql-anycable/pull/11))
14
+ - AnyCable 1.0 compatibility. [@bibendi], [@Envek] ([#10](https://github.com/Envek/graphql-anycable/pull/10))
15
+
16
+ ## 0.3.3 - 2020-03-03
17
+
18
+ ### Fixed
19
+
20
+ - Redis command error on subscription query with multiple fields. [@Envek] ([#9](https://github.com/Envek/graphql-anycable/issues/9))
21
+
22
+ ## 0.3.2 - 2020-03-02
23
+
24
+ ### Added
25
+
26
+ - Ability to skip some cleanup on restricted Redis instances (like Heroku). [@Envek] ([#8](https://github.com/Envek/graphql-anycable/issues/8))
27
+
28
+ ## 0.3.1 - 2019-06-13
29
+
30
+ ### Fixed
31
+
32
+ - Empty operation name handling. [@FX-HAO] ([#3](https://github.com/Envek/graphql-anycable/pull/3))
33
+
34
+ ## 0.3.0 - 2018-11-16
35
+
36
+ ### Added
37
+
38
+ - AnyCable 0.6 compatibility. [@Envek]
39
+
40
+ ## 0.2.0 - 2018-09-17
41
+
42
+ ### Added
43
+
44
+ - Subscription expiration and rake task for stale subscriptions cleanup. [@Envek]
45
+
46
+ ### 0.1.0 - 2018-08-26
47
+
48
+ Initial version: store subscriptions on redis, re-execute queries in sync. [@Envek]
49
+
50
+ [@gsamokovarov]: https://github.com/gsamokovarov "Genadi Samokovarov"
51
+ [@bibendi]: https://github.com/bibendi "Misha Merkushin"
52
+ [@FX-HAO]: https://github.com/FX-HAO "Fuxin Hao"
53
+ [@Envek]: https://github.com/Envek "Andrey Novikov"
data/README.md CHANGED
@@ -103,11 +103,41 @@ Or install it yourself as:
103
103
 
104
104
  To avoid filling Redis storage with stale subscription data:
105
105
 
106
- 1. Set `GRAPHQL_ANYCABLE_SUBSCRIPTION_EXPIRATION_SECONDS` environment variable to number of seconds (e.g. `604800` for 1 week). See [anyway_config] documentation to other ways of configuring this gem.
106
+ 1. Set `subscription_expiration_seconds` setting to number of seconds (e.g. `604800` for 1 week). See [configuration](#Configuration) section below for details.
107
107
 
108
108
  2. Execute `rake graphql:anycable:clean` once in a while to clean up stale subscription data.
109
109
 
110
- Heroku users should set up `GRAPHQL_ANYCABLE_USE_REDIS_OBJECT_ON_CLEANUP` environment variable to `false` due to [limitations in Heroku Redis](https://devcenter.heroku.com/articles/heroku-redis#connection-permissions).
110
+ Heroku users should set up `use_redis_object_on_cleanup` setting to `false` due to [limitations in Heroku Redis](https://devcenter.heroku.com/articles/heroku-redis#connection-permissions).
111
+
112
+ ## Configuration
113
+
114
+ GraphQL-Anycable uses [anyway_config] to configure itself. There are several possibilities to configure this gem:
115
+
116
+ 1. Environment variables:
117
+
118
+ ```.env
119
+ GRAPHQL_ANYCABLE_SUBSCRIPTION_EXPIRATION_SECONDS=604800
120
+ GRAPHQL_ANYCABLE_USE_REDIS_OBJECT_ON_CLEANUP=true
121
+ ```
122
+
123
+ 2. YAML configuration files:
124
+
125
+ ```yaml
126
+ # config/graphql_anycable.yml
127
+ production:
128
+ subscription_expiration_seconds: 300 # 5 minutes
129
+ use_redis_object_on_cleanup: false # For restricted redis installations
130
+ ```
131
+
132
+ 3. Configuration from your application code:
133
+
134
+ ```ruby
135
+ GraphQL::Anycable.configure do |config|
136
+ config.subscription_expiration_seconds = 3600 # 1 hour
137
+ end
138
+ ```
139
+
140
+ And any other way provided by [anyway_config]. Check its documentation!
111
141
 
112
142
  ## Development
113
143
 
@@ -0,0 +1,14 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "anycable", "~> 0.6.0"
6
+
7
+ group :development, :test do
8
+ gem "pry"
9
+ gem "pry-byebug", platform: :mri
10
+ gem "rubocop"
11
+ gem "rubocop-rspec"
12
+ end
13
+
14
+ gemspec path: "../"
@@ -0,0 +1,14 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "anycable", "~> 1.0.0.preview1"
6
+
7
+ group :development, :test do
8
+ gem "pry"
9
+ gem "pry-byebug", platform: :mri
10
+ gem "rubocop"
11
+ gem "rubocop-rspec"
12
+ end
13
+
14
+ gemspec path: "../"
@@ -3,6 +3,7 @@
3
3
  require "graphql"
4
4
 
5
5
  require_relative "graphql/anycable/version"
6
+ require_relative "graphql/anycable/cleaner"
6
7
  require_relative "graphql/anycable/config"
7
8
  require_relative "graphql/anycable/railtie" if defined?(Rails)
8
9
  require_relative "graphql/subscriptions/anycable_subscriptions"
@@ -13,13 +14,21 @@ module GraphQL
13
14
 
14
15
  def redis
15
16
  @redis ||= begin
16
- adapter = ::Anycable.broadcast_adapter
17
+ adapter = ::AnyCable.broadcast_adapter
17
18
  unless adapter.is_a?(::AnyCable::BroadcastAdapters::Redis)
18
19
  raise "Unsupported AnyCable adapter: #{adapter.class}. " \
19
20
  "graphql-anycable works only with redis broadcast adapter."
20
21
  end
21
- ::Anycable.broadcast_adapter.redis_conn
22
+ ::AnyCable.broadcast_adapter.redis_conn
22
23
  end
23
24
  end
25
+
26
+ def config
27
+ @config ||= GraphQL::Anycable::Config.new
28
+ end
29
+
30
+ def configure
31
+ yield(config) if block_given?
32
+ end
24
33
  end
25
34
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Anycable
5
+ module Cleaner
6
+ extend self
7
+
8
+ def clean
9
+ clean_channels
10
+ clean_subscriptions
11
+ clean_events
12
+ end
13
+
14
+ def clean_channels
15
+ return unless config.subscription_expiration_seconds
16
+ return unless config.use_redis_object_on_cleanup
17
+
18
+ redis.scan_each(match: "#{adapter::CHANNEL_PREFIX}*") do |key|
19
+ idle = redis.object("IDLETIME", key)
20
+ next if idle&.<= config.subscription_expiration_seconds
21
+
22
+ redis.del(key)
23
+ end
24
+ end
25
+
26
+ def clean_subscriptions
27
+ return unless config.subscription_expiration_seconds
28
+ return unless config.use_redis_object_on_cleanup
29
+
30
+ redis.scan_each(match: "#{adapter::SUBSCRIPTION_PREFIX}*") do |key|
31
+ idle = redis.object("IDLETIME", key)
32
+ next if idle&.<= config.subscription_expiration_seconds
33
+
34
+ redis.del(key)
35
+ end
36
+ end
37
+
38
+ def clean_events
39
+ redis.scan_each(match: "#{adapter::SUBSCRIPTION_EVENTS_PREFIX}*") do |key|
40
+ subscription_id = key.sub(/\A#{adapter::SUBSCRIPTION_EVENTS_PREFIX}/, "")
41
+ next if redis.exists(adapter::SUBSCRIPTION_PREFIX + subscription_id)
42
+
43
+ redis.smembers(key).each do |event_topic|
44
+ redis.srem(adapter::EVENT_PREFIX + event_topic, subscription_id)
45
+ end
46
+
47
+ redis.del(key)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def adapter
54
+ GraphQL::Subscriptions::AnyCableSubscriptions
55
+ end
56
+
57
+ def redis
58
+ GraphQL::Anycable.redis
59
+ end
60
+
61
+ def config
62
+ GraphQL::Anycable.config
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,7 +1,7 @@
1
- require "graphql-anycable"
2
-
3
1
  # frozen_string_literal: true
4
2
 
3
+ require "graphql-anycable"
4
+
5
5
  namespace :graphql do
6
6
  namespace :anycable do
7
7
  desc "Clean up stale graphql channels, subscriptions, and events from redis"
@@ -11,54 +11,20 @@ namespace :graphql do
11
11
  task clean_expired_subscriptions: :clean
12
12
 
13
13
  namespace :clean do
14
- KLASS = GraphQL::Subscriptions::AnyCableSubscriptions
15
-
16
14
  # Clean up old channels
17
15
  task :channels do
18
- next unless config.subscription_expiration_seconds
19
- next unless config.use_redis_object_on_cleanup
20
-
21
- redis.scan_each(match: "#{KLASS::CHANNEL_PREFIX}*") do |key|
22
- idle = redis.object("IDLETIME", key)
23
- next if idle&.<= config.subscription_expiration_seconds
24
-
25
- redis.del(key)
26
- end
16
+ GraphQL::Anycable::Cleaner.clean_channels
27
17
  end
28
18
 
29
19
  # Clean up old subscriptions (they should have expired by themselves)
30
20
  task :subscriptions do
31
- next unless config.subscription_expiration_seconds
32
- next unless config.use_redis_object_on_cleanup
33
-
34
- redis.scan_each(match: "#{KLASS::SUBSCRIPTION_PREFIX}*") do |key|
35
- idle = redis.object("IDLETIME", key)
36
- next if idle&.<= config.subscription_expiration_seconds
37
-
38
- redis.del(key)
39
- end
21
+ GraphQL::Anycable::Cleaner.clean_subscriptions
40
22
  end
41
23
 
42
24
  # Clean up subscription_ids from events for expired subscriptions
43
25
  task :events do
44
- redis.scan_each(match: "#{KLASS::SUBSCRIPTION_EVENTS_PREFIX}*") do |key|
45
- subscription_id = key.sub(/\A#{KLASS::SUBSCRIPTION_EVENTS_PREFIX}/, "")
46
- next if redis.exists(KLASS::SUBSCRIPTION_PREFIX + subscription_id)
47
-
48
- redis.smembers(key).each do |event_topic|
49
- redis.srem(KLASS::EVENT_PREFIX + event_topic, subscription_id)
50
- end
51
- redis.del(key)
52
- end
26
+ GraphQL::Anycable::Cleaner.clean_events
53
27
  end
54
28
  end
55
-
56
- def config
57
- @config ||= GraphQL::Anycable::Config.new
58
- end
59
-
60
- def redis
61
- GraphQL::Anycable.redis
62
- end
63
29
  end
64
30
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module Anycable
5
- VERSION = "0.3.3"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
@@ -54,7 +54,7 @@ module GraphQL
54
54
  class AnyCableSubscriptions < GraphQL::Subscriptions
55
55
  extend Forwardable
56
56
 
57
- def_delegators :"GraphQL::Anycable", :redis
57
+ def_delegators :"GraphQL::Anycable", :redis, :config
58
58
 
59
59
  SUBSCRIPTION_PREFIX = "graphql-subscription:"
60
60
  SUBSCRIPTION_EVENTS_PREFIX = "graphql-subscription-events:"
@@ -79,7 +79,7 @@ module GraphQL
79
79
  # This subscription was re-evaluated.
80
80
  # Send it to the specific stream where this client was waiting.
81
81
  def deliver(subscription_id, result)
82
- payload = { result: result.to_h, more: true }
82
+ payload = {result: result.to_h, more: true}
83
83
  anycable.broadcast(SUBSCRIPTION_PREFIX + subscription_id, payload.to_json)
84
84
  end
85
85
 
@@ -95,7 +95,7 @@ module GraphQL
95
95
  query_string: query.query_string,
96
96
  variables: query.provided_variables.to_json,
97
97
  context: @serializer.dump(context.to_h),
98
- operation_name: query.operation_name,
98
+ operation_name: query.operation_name
99
99
  }
100
100
 
101
101
  redis.multi do
@@ -115,9 +115,9 @@ module GraphQL
115
115
  def read_subscription(subscription_id)
116
116
  redis.mapped_hmget(
117
117
  "#{SUBSCRIPTION_PREFIX}#{subscription_id}",
118
- :query_string, :variables, :context, :operation_name,
118
+ :query_string, :variables, :context, :operation_name
119
119
  ).tap do |subscription|
120
- subscription[:context] = @serializer.load(subscription[:context])
120
+ subscription[:context] = @serializer.load(subscription[:context])
121
121
  subscription[:variables] = JSON.parse(subscription[:variables])
122
122
  subscription[:operation_name] = nil if subscription[:operation_name].strip == ""
123
123
  end
@@ -145,11 +145,7 @@ module GraphQL
145
145
  private
146
146
 
147
147
  def anycable
148
- @anycable ||= ::Anycable.broadcast_adapter
149
- end
150
-
151
- def config
152
- @config ||= GraphQL::Anycable::Config.new
148
+ @anycable ||= ::AnyCable.broadcast_adapter
153
149
  end
154
150
  end
155
151
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-anycable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Novikov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-03 00:00:00.000000000 Z
11
+ date: 2020-03-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anycable
@@ -141,11 +141,14 @@ executables: []
141
141
  extensions: []
142
142
  extra_rdoc_files: []
143
143
  files:
144
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
145
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
144
146
  - ".gitignore"
145
147
  - ".rspec"
146
148
  - ".rubocop.yml"
147
149
  - ".travis.yml"
148
150
  - Appraisals
151
+ - CHANGELOG.md
149
152
  - Gemfile
150
153
  - LICENSE.txt
151
154
  - README.md
@@ -153,12 +156,15 @@ files:
153
156
  - bin/console
154
157
  - bin/setup
155
158
  - gemfiles/.bundle/config
159
+ - gemfiles/anycable_0.6.gemfile
160
+ - gemfiles/anycable_1.0.gemfile
156
161
  - gemfiles/graphql_1.10.gemfile
157
162
  - gemfiles/graphql_1.9.gemfile
158
163
  - graphql-anycable.gemspec
159
164
  - lib/Rakefile
160
165
  - lib/graphql-anycable.rb
161
166
  - lib/graphql/anycable.rb
167
+ - lib/graphql/anycable/cleaner.rb
162
168
  - lib/graphql/anycable/config.rb
163
169
  - lib/graphql/anycable/railtie.rb
164
170
  - lib/graphql/anycable/tasks/clean_expired_subscriptions.rake