graphql-anycable 0.3.3 → 0.4.0

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: '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