optics-agent 0.2.1 → 0.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 +5 -13
- data/README.md +22 -4
- data/lib/optics-agent/agent.rb +71 -9
- data/lib/optics-agent/graphql-middleware.rb +1 -1
- data/lib/optics-agent/rack-middleware.rb +17 -8
- data/lib/optics-agent/reporting/query-trace.rb +2 -3
- data/lib/optics-agent/reporting/report.rb +2 -3
- data/lib/optics-agent/reporting/report_job.rb +2 -2
- data/lib/optics-agent/reporting/schema.rb +2 -3
- data/lib/optics-agent/reporting/schema_job.rb +1 -1
- data/spec/benchmark/benchmark.rb +32 -0
- data/spec/graphql-middleware_spec.rb +2 -1
- data/spec/support/create_starwars_schema.rb +219 -0
- metadata +16 -13
- data/lib/optics-agent/reporting/send-message.rb +0 -23
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
OGFkMjdjNmJkNTI2M2U2N2NjMzYyMDAxNTdmNWJlOTA4NWNiNDJmZg==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 75440b51ee6807fa478559568ca0fc7934e38c5f
|
4
|
+
data.tar.gz: 6c7302b7b48ff60c89f92602663b680502318af0
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
ZTNmZWU3ODQwODI3YjU3YWFiNmM5NTE4ODBlYjQ3MWViZDlmZmRlYTQ1ZTM3
|
11
|
-
ZGUxYTczMjVmMzZlZmY0NDUxZWUwNWQ1NzgxNjJkZjEyMWFhYTM=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MmJmN2NhYTg2ZGRlNjFmYjRmMDFhYWYyZGQyMGUxMDIxZjZkZDE0MjdhZTg5
|
14
|
-
NGZkZmRkZmQ4OTIyNjdlODM0ZGI4OWViNGM2OWE4OWM0MmJiYzcxMzFhMzE2
|
15
|
-
NjY1NjdiMGJiNDc5NTE5Yjg4MjdiZTQ5MTIwZjAwY2U0ZGQ0NDM=
|
6
|
+
metadata.gz: d201e91158930766f7efd34210b9af68d4575c658672209363dc8614746577256a66da8e854f5b314b376988f72f65070aa9e898e93dc0e58b4485858d4ba728
|
7
|
+
data.tar.gz: 2593d4184b7766bd232f0d950a004ab70b8556af248c2bda482c1eb7e268b20b9f307c0bcc07dd61192b43821df57d492df24e6258e3d0bbf79f299769940696
|
data/README.md
CHANGED
@@ -20,7 +20,25 @@ To your `Gemfile`
|
|
20
20
|
|
21
21
|
### API key
|
22
22
|
|
23
|
-
You'll need to run your app with the `OPTICS_API_KEY` environment variable set to the API key of your Apollo Optics service;
|
23
|
+
You'll need to run your app with the `OPTICS_API_KEY` environment variable set (or set via options) to the API key of your Apollo Optics service; you can get an API key by setting up a service at https://optics.apollodata.com.
|
24
|
+
|
25
|
+
### Configuration
|
26
|
+
|
27
|
+
After creating an agent (see below), you can configure it with
|
28
|
+
|
29
|
+
```rb
|
30
|
+
agent.set_options(option: value)
|
31
|
+
```
|
32
|
+
|
33
|
+
Possible options are:
|
34
|
+
|
35
|
+
- `api_key` - Your API key for the Optics service. This defaults to the OPTICS_API_KEY environment variable, but can be overridden here.
|
36
|
+
- `endpoint_url ['https://optics-report.apollodata.com']` - Where to send the reports. Defaults to the production Optics endpoint, or the `OPTICS_ENDPOINT_URL` environment variable if it is set. You shouldn't need to set this unless you are debugging
|
37
|
+
- `debug [false]` - Log detailed debugging messages
|
38
|
+
- `disable_reporting [false]` - Don't report anything to Optics (useful for testing)
|
39
|
+
- `print_reports [false]` - Print JSON versions of the data sent to Optics to the log
|
40
|
+
- `schema_report_delay_ms [10000]` - How long to wait before sending a schema report after startup, in, milliseconds
|
41
|
+
- `report_interval_ms [60000]` - How often to send reports in milliseconds. Defaults to 1 minute. Minimum 10 seconds. You shouldn't need to set this unless you are debugging.
|
24
42
|
|
25
43
|
### Basic Rack/Sinatra
|
26
44
|
|
@@ -37,7 +55,7 @@ Register the Rack middleware (say in a `config.ru`):
|
|
37
55
|
use agent.rack_middleware
|
38
56
|
```
|
39
57
|
|
40
|
-
Register the
|
58
|
+
Register the GraphQL middleware:
|
41
59
|
|
42
60
|
```ruby
|
43
61
|
agent.instrument_schema(YourSchema)
|
@@ -66,7 +84,7 @@ end
|
|
66
84
|
|
67
85
|
The equivalent of the above for Rails is:
|
68
86
|
|
69
|
-
Create an agent in `config/
|
87
|
+
Create an agent in `config/application.rb`, and register the rack middleware:
|
70
88
|
|
71
89
|
```ruby
|
72
90
|
module YourApplicationRails
|
@@ -80,7 +98,7 @@ end
|
|
80
98
|
|
81
99
|
```
|
82
100
|
|
83
|
-
Register the
|
101
|
+
Register the GraphQL middleware when you create your schema:
|
84
102
|
|
85
103
|
```ruby
|
86
104
|
Rails.application.config.optics_agent.instrument_schema(YourSchema)
|
data/lib/optics-agent/agent.rb
CHANGED
@@ -4,6 +4,7 @@ require 'optics-agent/graphql-middleware'
|
|
4
4
|
require 'optics-agent/reporting/report_job'
|
5
5
|
require 'optics-agent/reporting/schema_job'
|
6
6
|
require 'optics-agent/reporting/query-trace'
|
7
|
+
require 'net/http'
|
7
8
|
|
8
9
|
module OpticsAgent
|
9
10
|
# XXX: this is a class but acts as a singleton right now.
|
@@ -20,27 +21,53 @@ module OpticsAgent
|
|
20
21
|
@query_queue = []
|
21
22
|
@semaphone = Mutex.new
|
22
23
|
|
23
|
-
#
|
24
|
-
|
25
|
-
|
24
|
+
# set defaults
|
25
|
+
self.set_options
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_options(
|
29
|
+
debug: false,
|
30
|
+
disable_reporting: false,
|
31
|
+
print_reports: false,
|
32
|
+
schema_report_delay_ms: 10 * 1000,
|
33
|
+
report_interval_ms: 60 * 1000,
|
34
|
+
api_key: ENV['OPTICS_API_KEY'],
|
35
|
+
endpoint_url: ENV['OPTICS_ENDPOINT_URL'] || 'https://optics-report.apollodata.com'
|
36
|
+
)
|
37
|
+
@debug = debug
|
38
|
+
@disable_reporting = disable_reporting || !endpoint_url || endpoint_url.nil?
|
39
|
+
@print_reports = print_reports
|
40
|
+
@schema_report_delay_ms = schema_report_delay_ms
|
41
|
+
@report_interval_ms = report_interval_ms
|
42
|
+
@api_key = api_key
|
43
|
+
@endpoint_url = endpoint_url
|
26
44
|
end
|
27
45
|
|
28
46
|
def instrument_schema(schema)
|
29
47
|
@schema = schema
|
48
|
+
debug "adding middleware to schema"
|
30
49
|
schema.middleware << graphql_middleware
|
31
50
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
51
|
+
unless @disable_reporting
|
52
|
+
debug "spawning schema thread"
|
53
|
+
Thread.new do
|
54
|
+
debug "schema thread spawned"
|
55
|
+
sleep @schema_report_delay_ms / 1000
|
56
|
+
debug "running schema job"
|
57
|
+
SchemaJob.new.perform(self)
|
58
|
+
end
|
36
59
|
|
37
|
-
|
60
|
+
schedule_report
|
61
|
+
end
|
38
62
|
end
|
39
63
|
|
40
64
|
def schedule_report
|
65
|
+
debug "spawning reporting thread"
|
41
66
|
Thread.new do
|
67
|
+
debug "reporting thread spawned"
|
42
68
|
while true
|
43
|
-
sleep @
|
69
|
+
sleep @report_interval_ms / 1000
|
70
|
+
debug "running reporting job"
|
44
71
|
ReportJob.new.perform(self)
|
45
72
|
end
|
46
73
|
end
|
@@ -48,12 +75,14 @@ module OpticsAgent
|
|
48
75
|
|
49
76
|
def add_query(query, rack_env, start_time, end_time)
|
50
77
|
@semaphone.synchronize {
|
78
|
+
debug { "adding query to queue, queue length was #{@query_queue.length}" }
|
51
79
|
@query_queue << [query, rack_env, start_time, end_time]
|
52
80
|
}
|
53
81
|
end
|
54
82
|
|
55
83
|
def clear_query_queue
|
56
84
|
@semaphone.synchronize {
|
85
|
+
debug { "clearing query queue, queue length was #{@query_queue.length}" }
|
57
86
|
queue = @query_queue
|
58
87
|
@query_queue = []
|
59
88
|
queue
|
@@ -68,5 +97,38 @@ module OpticsAgent
|
|
68
97
|
# graphql middleware doesn't seem to need the agent but certainly could have it
|
69
98
|
OpticsAgent::GraphqlMiddleware.new
|
70
99
|
end
|
100
|
+
|
101
|
+
def send_message(path, message)
|
102
|
+
req = Net::HTTP::Post.new(path)
|
103
|
+
req['x-api-key'] = @api_key
|
104
|
+
req['user-agent'] = "optics-agent-rb"
|
105
|
+
|
106
|
+
req.body = message.class.encode(message)
|
107
|
+
if @debug || @print_reports
|
108
|
+
log "sending message: #{message.class.encode_json(message)}"
|
109
|
+
end
|
110
|
+
|
111
|
+
uri = URI.parse(@endpoint_url)
|
112
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
113
|
+
http.use_ssl = true
|
114
|
+
res = http.request(req)
|
115
|
+
|
116
|
+
if @debug || @print_reports
|
117
|
+
log "got response: #{res.inspect}"
|
118
|
+
log "response body: #{res.body.inspect}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def log(message = nil)
|
123
|
+
message = yield unless message
|
124
|
+
puts "optics-agent: #{message}"
|
125
|
+
end
|
126
|
+
|
127
|
+
def debug(message = nil)
|
128
|
+
if @debug
|
129
|
+
message = yield unless message
|
130
|
+
log "DEBUG: #{message} <#{Process.pid} | #{Thread.current.object_id}>"
|
131
|
+
end
|
132
|
+
end
|
71
133
|
end
|
72
134
|
end
|
@@ -9,7 +9,7 @@ module OpticsAgent
|
|
9
9
|
result = next_middleware.call
|
10
10
|
end_time = Time.now
|
11
11
|
|
12
|
-
query = query_context[:optics_agent]
|
12
|
+
query = query_context[:optics_agent].query
|
13
13
|
query.report_field(parent_type.to_s, field_definition.name, start_time, end_time)
|
14
14
|
|
15
15
|
result
|
@@ -12,27 +12,36 @@ module OpticsAgent
|
|
12
12
|
|
13
13
|
# XXX: figure out a way to pass this in here
|
14
14
|
agent = OpticsAgent::Agent.instance
|
15
|
+
agent.debug { "rack-middleware: request started" }
|
15
16
|
query = OpticsAgent::Reporting::Query.new
|
16
17
|
|
17
18
|
# Attach so resolver middleware can access
|
18
|
-
env[:optics_agent] =
|
19
|
-
agent: agent,
|
20
|
-
query: query
|
21
|
-
}
|
22
|
-
env[:optics_agent].define_singleton_method(:with_document) do |document|
|
23
|
-
self[:query].document = document
|
24
|
-
self
|
25
|
-
end
|
19
|
+
env[:optics_agent] = RackAgent.new(agent, query)
|
26
20
|
|
27
21
|
result = @app.call(env)
|
28
22
|
|
29
23
|
# XXX: this approach means if the user forgets to call with_document
|
30
24
|
# we just never log queries. Can we detect if the request is a graphql one?
|
25
|
+
agent.debug { "rack-middleware: request finished" }
|
31
26
|
if (query.document)
|
27
|
+
agent.debug { "rack-middleware: adding query to agent" }
|
32
28
|
agent.add_query(query, env, start_time, Time.now)
|
33
29
|
end
|
34
30
|
|
35
31
|
result
|
36
32
|
end
|
37
33
|
end
|
34
|
+
|
35
|
+
class RackAgent
|
36
|
+
attr_reader :agent, :query
|
37
|
+
def initialize(agent, query)
|
38
|
+
@agent = agent
|
39
|
+
@query = query
|
40
|
+
end
|
41
|
+
|
42
|
+
def with_document(query_string)
|
43
|
+
@query.document = query_string
|
44
|
+
self
|
45
|
+
end
|
46
|
+
end
|
38
47
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'apollo/optics/proto/reports_pb'
|
2
2
|
require 'optics-agent/reporting/helpers'
|
3
|
-
require 'optics-agent/reporting/send-message'
|
4
3
|
|
5
4
|
module OpticsAgent::Reporting
|
6
5
|
# A trace is just a different view of a single query report, with full
|
@@ -49,8 +48,8 @@ module OpticsAgent::Reporting
|
|
49
48
|
})
|
50
49
|
end
|
51
50
|
|
52
|
-
def
|
53
|
-
send_message('/api/ss/traces', @report)
|
51
|
+
def send_with(agent)
|
52
|
+
agent.send_message('/api/ss/traces', @report)
|
54
53
|
end
|
55
54
|
end
|
56
55
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'apollo/optics/proto/reports_pb'
|
2
|
-
require 'optics-agent/reporting/send-message'
|
3
2
|
require 'optics-agent/reporting/helpers'
|
4
3
|
require 'optics-agent/normalization/latency'
|
5
4
|
|
@@ -29,9 +28,9 @@ module OpticsAgent::Reporting
|
|
29
28
|
@report.realtime_duration || duration_nanos(@report.start_time, @report.end_time)
|
30
29
|
end
|
31
30
|
|
32
|
-
def
|
31
|
+
def send_with(agent)
|
33
32
|
self.finish!
|
34
|
-
send_message('/api/ss/stats', @report)
|
33
|
+
agent.send_message('/api/ss/stats', @report)
|
35
34
|
end
|
36
35
|
|
37
36
|
# XXX: record timing / client
|
@@ -9,11 +9,11 @@ module OpticsAgent::Reporting
|
|
9
9
|
|
10
10
|
# XXX: don't send *every* trace
|
11
11
|
query_trace = QueryTrace.new(*item)
|
12
|
-
query_trace.
|
12
|
+
query_trace.send_with(agent)
|
13
13
|
end
|
14
14
|
|
15
15
|
report.decorate_from_schema(agent.schema)
|
16
|
-
report.
|
16
|
+
report.send_with(agent)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -3,7 +3,6 @@ require 'graphql'
|
|
3
3
|
|
4
4
|
require 'apollo/optics/proto/reports_pb'
|
5
5
|
require 'optics-agent/reporting/helpers'
|
6
|
-
require 'optics-agent/reporting/send-message'
|
7
6
|
require 'optics-agent/instrumentation/query-schema'
|
8
7
|
|
9
8
|
module OpticsAgent::Reporting
|
@@ -49,8 +48,8 @@ module OpticsAgent::Reporting
|
|
49
48
|
types
|
50
49
|
end
|
51
50
|
|
52
|
-
def
|
53
|
-
send_message('/api/ss/schema', @message)
|
51
|
+
def send_with(agent)
|
52
|
+
agent.send_message('/api/ss/schema', @message)
|
54
53
|
end
|
55
54
|
end
|
56
55
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'graphql'
|
3
|
+
require 'optics-agent'
|
4
|
+
|
5
|
+
require_relative '../support/create_starwars_schema.rb';
|
6
|
+
|
7
|
+
basic_schema = create_starwars_schema
|
8
|
+
|
9
|
+
agent = OpticsAgent::Agent.instance
|
10
|
+
instrumented_schema = create_starwars_schema
|
11
|
+
agent.instrument_schema(instrumented_schema)
|
12
|
+
|
13
|
+
# just drop reports on the floor
|
14
|
+
null_reporter = {}
|
15
|
+
null_reporter.define_singleton_method :report_field, lambda { |x,y,z,w| }
|
16
|
+
|
17
|
+
query_string = GraphQL::Introspection::INTROSPECTION_QUERY
|
18
|
+
|
19
|
+
Benchmark.bm(7) do |x|
|
20
|
+
x.report("No agent") do
|
21
|
+
20.times do
|
22
|
+
basic_schema.execute(query_string)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
x.report("With agent") do
|
26
|
+
20.times do
|
27
|
+
instrumented_schema.execute(query_string, context: {
|
28
|
+
optics_agent: { query: null_reporter }
|
29
|
+
})
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'ostruct'
|
1
2
|
require 'optics-agent/graphql-middleware'
|
2
3
|
require 'graphql'
|
3
4
|
|
@@ -32,7 +33,7 @@ describe GraphqlMiddleware do
|
|
32
33
|
|
33
34
|
query = spy("query")
|
34
35
|
schema.execute('{ person { firstName lastName } }', {
|
35
|
-
context: { optics_agent:
|
36
|
+
context: { optics_agent: OpenStruct.new(query: query) }
|
36
37
|
})
|
37
38
|
|
38
39
|
expect(query).to have_received(:report_field).exactly(3).times
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# Adapted from https://github.com/rmosolgo/graphql-ruby/blob/master/spec/support/star_wars_schema.rb
|
2
|
+
|
3
|
+
class CustomBaseEdge < GraphQL::Relay::Edge
|
4
|
+
def upcased_name
|
5
|
+
node.name.upcase
|
6
|
+
end
|
7
|
+
|
8
|
+
def upcased_parent_name
|
9
|
+
parent.name.upcase
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_starwars_schema
|
14
|
+
ship = GraphQL::ObjectType.define do
|
15
|
+
name "Ship"
|
16
|
+
interfaces [GraphQL::Relay::Node.interface]
|
17
|
+
global_id_field :id
|
18
|
+
field :name, types.String
|
19
|
+
end
|
20
|
+
|
21
|
+
baseType = GraphQL::ObjectType.define do
|
22
|
+
name "Base"
|
23
|
+
interfaces [GraphQL::Relay::Node.interface]
|
24
|
+
global_id_field :id
|
25
|
+
field :name, types.String
|
26
|
+
field :planet, types.String
|
27
|
+
end
|
28
|
+
|
29
|
+
# Use an optional block to add fields to the connection type:
|
30
|
+
baseConnectionWithTotalCountType = baseType.define_connection do
|
31
|
+
name "BasesConnectionWithTotalCount"
|
32
|
+
field :totalCount do
|
33
|
+
type types.Int
|
34
|
+
resolve ->(obj, args, ctx) { obj.nodes.count }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
customBaseEdgeType = baseType.define_edge do
|
39
|
+
name "CustomBaseEdge"
|
40
|
+
field :upcasedName, types.String, property: :upcased_name
|
41
|
+
field :upcasedParentName, types.String, property: :upcased_parent_name
|
42
|
+
field :edgeClassName, types.String do
|
43
|
+
resolve ->(obj, args, ctx) { obj.class.name }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
customEdgeBaseConnectionType = baseType.define_connection(edge_class: CustomBaseEdge, edge_type: customBaseEdgeType) do
|
48
|
+
name "CustomEdgeBaseConnection"
|
49
|
+
|
50
|
+
field :totalCountTimes100 do
|
51
|
+
type types.Int
|
52
|
+
resolve ->(obj, args, ctx) { obj.nodes.count * 100 }
|
53
|
+
end
|
54
|
+
|
55
|
+
field :fieldName, types.String, resolve: ->(obj, args, ctx) { obj.field.name }
|
56
|
+
end
|
57
|
+
|
58
|
+
faction = GraphQL::ObjectType.define do
|
59
|
+
name "Faction"
|
60
|
+
interfaces [GraphQL::Relay::Node.interface]
|
61
|
+
|
62
|
+
field :id, !types.ID, resolve: GraphQL::Relay::GlobalIdResolve.new(type: faction)
|
63
|
+
field :name, types.String
|
64
|
+
connection :ships, ship.connection_type do
|
65
|
+
resolve ->(obj, args, ctx) {
|
66
|
+
all_ships = obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
|
67
|
+
if args[:nameIncludes]
|
68
|
+
all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
|
69
|
+
end
|
70
|
+
all_ships
|
71
|
+
}
|
72
|
+
# You can define arguments here and use them in the connection
|
73
|
+
argument :nameIncludes, types.String
|
74
|
+
end
|
75
|
+
connection :shipsWithMaxPageSize, ship.connection_type, max_page_size: 2 do
|
76
|
+
resolve ->(obj, args, ctx) {
|
77
|
+
all_ships = obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
|
78
|
+
if args[:nameIncludes]
|
79
|
+
all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
|
80
|
+
end
|
81
|
+
all_ships
|
82
|
+
}
|
83
|
+
# You can define arguments here and use them in the connection
|
84
|
+
argument :nameIncludes, types.String
|
85
|
+
end
|
86
|
+
|
87
|
+
connection :bases, baseConnectionWithTotalCountType do
|
88
|
+
# Resolve field should return an Array, the Connection
|
89
|
+
# will do the rest!
|
90
|
+
resolve ->(obj, args, ctx) {
|
91
|
+
all_bases = Base.where(id: obj.bases)
|
92
|
+
if args[:nameIncludes]
|
93
|
+
all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
|
94
|
+
end
|
95
|
+
all_bases
|
96
|
+
}
|
97
|
+
argument :nameIncludes, types.String
|
98
|
+
end
|
99
|
+
|
100
|
+
connection :basesClone, baseType.connection_type
|
101
|
+
connection :basesByName, baseType.connection_type, property: :bases do
|
102
|
+
argument :order, types.String, default_value: "name"
|
103
|
+
resolve ->(obj, args, ctx) {
|
104
|
+
if args[:order].present?
|
105
|
+
obj.bases.order(args[:order])
|
106
|
+
else
|
107
|
+
obj.bases
|
108
|
+
end
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
connection :basesWithMaxLimitRelation, baseType.connection_type, max_page_size: 2 do
|
113
|
+
resolve ->(object, args, context) { Base.all }
|
114
|
+
end
|
115
|
+
|
116
|
+
connection :basesWithMaxLimitArray, baseType.connection_type, max_page_size: 2 do
|
117
|
+
resolve ->(object, args, context) { Base.all.to_a }
|
118
|
+
end
|
119
|
+
|
120
|
+
connection :basesAsSequelDataset, baseConnectionWithTotalCountType do
|
121
|
+
argument :nameIncludes, types.String
|
122
|
+
resolve ->(obj, args, ctx) {
|
123
|
+
all_bases = SequelBase.where(faction_id: obj.id)
|
124
|
+
if args[:nameIncludes]
|
125
|
+
all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
|
126
|
+
end
|
127
|
+
all_bases
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
connection :basesWithCustomEdge, customEdgeBaseConnectionType, property: :bases
|
132
|
+
end
|
133
|
+
|
134
|
+
# Define a mutation. It will also:
|
135
|
+
# - define a derived InputObjectType
|
136
|
+
# - define a derived ObjectType (for return)
|
137
|
+
# - define a field, accessible from {Mutation#field}
|
138
|
+
#
|
139
|
+
# The resolve proc takes `inputs, ctx`, where:
|
140
|
+
# - `inputs` has the keys defined with `input_field`
|
141
|
+
# - `ctx` is the Query context (like normal fields)
|
142
|
+
#
|
143
|
+
# Notice that you leave out clientMutationId.
|
144
|
+
introduceShipMutation = GraphQL::Relay::Mutation.define do
|
145
|
+
# Used as the root for derived types:
|
146
|
+
name "IntroduceShip"
|
147
|
+
description "Add a ship to this faction"
|
148
|
+
|
149
|
+
# Nested under `input` in the query:
|
150
|
+
input_field :shipName, !types.String
|
151
|
+
input_field :factionId, !types.ID
|
152
|
+
|
153
|
+
# Result may have access to these fields:
|
154
|
+
return_field :shipEdge, ship.edge_type
|
155
|
+
return_field :faction, faction
|
156
|
+
|
157
|
+
# Here's the mutation operation:
|
158
|
+
resolve ->(root_obj, inputs, ctx) {
|
159
|
+
faction_id = inputs["factionId"]
|
160
|
+
ship = STAR_WARS_DATA.create_ship(inputs["shipName"], faction_id)
|
161
|
+
faction = STAR_WARS_DATA["Faction"][faction_id]
|
162
|
+
connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(faction.ships)
|
163
|
+
ships_connection = connection_class.new(faction.ships, inputs)
|
164
|
+
ship_edge = GraphQL::Relay::Edge.new(ship, ships_connection)
|
165
|
+
{ shipEdge: ship_edge, faction: faction }
|
166
|
+
}
|
167
|
+
end
|
168
|
+
|
169
|
+
queryType = GraphQL::ObjectType.define do
|
170
|
+
name "Query"
|
171
|
+
field :rebels, faction do
|
172
|
+
resolve ->(obj, args, ctx) { STAR_WARS_DATA["Faction"]["1"]}
|
173
|
+
end
|
174
|
+
|
175
|
+
field :empire, faction do
|
176
|
+
resolve ->(obj, args, ctx) { STAR_WARS_DATA["Faction"]["2"]}
|
177
|
+
end
|
178
|
+
|
179
|
+
field :largestBase, baseType do
|
180
|
+
resolve ->(obj, args, ctx) { Base.find(3) }
|
181
|
+
end
|
182
|
+
|
183
|
+
field :node, GraphQL::Relay::Node.field
|
184
|
+
end
|
185
|
+
|
186
|
+
mutationType = GraphQL::ObjectType.define do
|
187
|
+
name "Mutation"
|
188
|
+
# The mutation object exposes a field:
|
189
|
+
field :introduceShip, field: introduceShipMutation.field
|
190
|
+
end
|
191
|
+
|
192
|
+
starWarsSchema = GraphQL::Schema.define do
|
193
|
+
query(queryType)
|
194
|
+
mutation(mutationType)
|
195
|
+
|
196
|
+
resolve_type ->(object, ctx) {
|
197
|
+
if object == :test_error
|
198
|
+
:not_a_type
|
199
|
+
elsif object.is_a?(Base)
|
200
|
+
baseType
|
201
|
+
elsif STAR_WARS_DATA["Faction"].values.include?(object)
|
202
|
+
faction
|
203
|
+
elsif STAR_WARS_DATA["Ship"].values.include?(object)
|
204
|
+
ship
|
205
|
+
else
|
206
|
+
nil
|
207
|
+
end
|
208
|
+
}
|
209
|
+
|
210
|
+
object_from_id ->(node_id, ctx) do
|
211
|
+
type_name, id = GraphQL::Schema::UniqueWithinType.decode(node_id)
|
212
|
+
STAR_WARS_DATA[type_name][id]
|
213
|
+
end
|
214
|
+
|
215
|
+
id_from_object ->(object, type, ctx) do
|
216
|
+
GraphQL::Schema::UniqueWithinType.encode(type.name, object.id)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: optics-agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- 'Tom Coleman '
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
@@ -14,56 +14,56 @@ dependencies:
|
|
14
14
|
name: graphql
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 0.19.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.19.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: google-protobuf
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 3.1.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 3.1.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 11.3.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 11.3.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - ~>
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 3.5.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - ~>
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 3.5.0
|
69
69
|
description: An Agent for Apollo Optics, http://optics.apollodata.com
|
@@ -90,7 +90,7 @@ files:
|
|
90
90
|
- lib/optics-agent/reporting/report_job.rb
|
91
91
|
- lib/optics-agent/reporting/schema.rb
|
92
92
|
- lib/optics-agent/reporting/schema_job.rb
|
93
|
-
-
|
93
|
+
- spec/benchmark/benchmark.rb
|
94
94
|
- spec/graphql-middleware_spec.rb
|
95
95
|
- spec/latency_spec.rb
|
96
96
|
- spec/query-normalization_spec.rb
|
@@ -99,6 +99,7 @@ files:
|
|
99
99
|
- spec/schema-introspection_spec.rb
|
100
100
|
- spec/schema_spec.rb
|
101
101
|
- spec/spec_helper.rb
|
102
|
+
- spec/support/create_starwars_schema.rb
|
102
103
|
homepage: http://rubygems.org/gems/optics-agent
|
103
104
|
licenses:
|
104
105
|
- MIT
|
@@ -109,12 +110,12 @@ require_paths:
|
|
109
110
|
- lib
|
110
111
|
required_ruby_version: !ruby/object:Gem::Requirement
|
111
112
|
requirements:
|
112
|
-
- -
|
113
|
+
- - ">="
|
113
114
|
- !ruby/object:Gem::Version
|
114
115
|
version: '0'
|
115
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
117
|
requirements:
|
117
|
-
- -
|
118
|
+
- - ">="
|
118
119
|
- !ruby/object:Gem::Version
|
119
120
|
version: '0'
|
120
121
|
requirements: []
|
@@ -124,6 +125,7 @@ signing_key:
|
|
124
125
|
specification_version: 4
|
125
126
|
summary: An Agent for Apollo Optics
|
126
127
|
test_files:
|
128
|
+
- spec/benchmark/benchmark.rb
|
127
129
|
- spec/graphql-middleware_spec.rb
|
128
130
|
- spec/latency_spec.rb
|
129
131
|
- spec/query-normalization_spec.rb
|
@@ -132,3 +134,4 @@ test_files:
|
|
132
134
|
- spec/schema-introspection_spec.rb
|
133
135
|
- spec/schema_spec.rb
|
134
136
|
- spec/spec_helper.rb
|
137
|
+
- spec/support/create_starwars_schema.rb
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
|
3
|
-
module OpticsAgent
|
4
|
-
module Reporting
|
5
|
-
OPTICS_URL = 'https://optics-report.apollodata.com'
|
6
|
-
def send_message(path, message)
|
7
|
-
|
8
|
-
req = Net::HTTP::Post.new(path)
|
9
|
-
req['x-api-key'] = ENV['OPTICS_API_KEY']
|
10
|
-
req['user-agent'] = "optics-agent-rb"
|
11
|
-
|
12
|
-
req.body = message.class.encode(message)
|
13
|
-
puts message.class.encode_json(message)
|
14
|
-
|
15
|
-
uri = URI.parse(OPTICS_URL)
|
16
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
17
|
-
http.use_ssl = true
|
18
|
-
res = http.request(req)
|
19
|
-
p res
|
20
|
-
p res.body
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|