require 'date' require 'agoo' Agoo::Log.configure(dir: '', console: true, classic: true, colorize: true, states: { INFO: true, DEBUG: true, connect: true, request: true, response: true, eval: true, push: false, }) # Next implement the Ruby classes to support the API. The type and class names # are the same in this example to make it easier to follow. class Artist attr_reader :name attr_reader :songs attr_reader :origin def initialize(name, origin) @name = name @songs = [] @origin = origin end def song(args, req) n = args['name'] @songs.each { |s| return s if n == s.name } nil end end class Song attr_reader :name # string attr_reader :artist # reference attr_reader :duration # integer attr_reader :release # date attr_accessor :likes # integer def initialize(name, artist, duration, release) @name = name @artist = artist @duration = duration @release = release @likes = 0 artist.songs << self end end # This is the class that implements the root query operation. class Query attr_reader :artists def initialize(artists) @artists = artists end def artist(args={}) n = args['name'] @artists.each { |a| return a if n == a.name } nil end end class Mutation def initialize(artists) @artists = artists @lock = Mutex.new end def like(args={}) an = args['artist'] sn = args['song'] @lock.synchronize { @artists.each {|a| if an == a.name a.songs.each { |s| if s.name == sn; s.likes += 1; return s; end } end } } nil end end class Schema #attr_reader :query attr_reader :mutation attr_reader :subscription def initialize(q) @query = q @mutation = Mutation.new(@query.artists) end def query(_, req) # req will contain the request parameters which can be used to determine # if the query should continue. For example the req["QUERY_STRING"] could # be checked for an occurance of "__" and rejected by raising an # exception. If the exception responds to :code then that code will be # used as the HTTP status code. # puts "*** query #{req}" @query end end # Populate the library. fazerdaze = Artist.new('Fazerdaze', ['Morningside', 'Auckland', 'New Zealand']) Song.new('Jennifer', fazerdaze, 240, Date.new(2017, 5, 5)) Song.new('Lucky Girl', fazerdaze, 170, Date.new(2017, 5, 5)) Song.new('Friends', fazerdaze, 194, Date.new(2017, 5, 5)) Song.new('Reel', fazerdaze, 193, Date.new(2015, 11, 2)) boys = Artist.new('Viagra Boys', ['Stockholm', 'Sweden']) Song.new('Down In The Basement', boys, 216, Date.new(2018, 9, 28)) Song.new('Frogstrap', boys, 195, Date.new(2018, 9, 28)) Song.new('Worms', boys, 208, Date.new(2018, 9, 28)) Song.new('Amphetanarchy', boys, 346, Date.new(2018, 9, 28)) $schema = Schema.new(Query.new([fazerdaze, boys])) puts %^\nopen 'localhost:6464/graphql?query={artist(name:"Fazerdaze"){name,songs{name,duration}}}&indent=2' in a browser.\n\n^ #http://localhost:6464/graphql?query={artists{name,origin,songs{name,duration,likes}},__schema{types{name,kind,fields{name}}}} Agoo::Server.init(6464, 'root', thread_count: 1, graphql: '/graphql') Agoo::Server.start Agoo::GraphQL.schema($schema) {Agoo::GraphQL.load_file('song.graphql')} # Use CORS to allow GraphiQL to connect. Agoo::GraphQL.build_headers = proc{ |req| origin = req.headers['HTTP_ORIGIN'] || '*' { 'Access-Control-Allow-Origin' => origin, 'Access-Control-Allow-Headers' => '*', 'Access-Control-Allow-Credentials' => true, 'Access-Control-Max-Age' => 3600, } } # When starting with a thread_count over 0 just sleep until a ^C is # signalled. Agoo must be running to load the SDL. sleep # To run this example type the following then go to a browser and enter a URL of # localhost:6464/graphql?query={artist(name:"Fazerdaze"){name}}&indent=2. For # a more complex query try # localhost:6464/graphql?query={artist(name:"Fazerdaze"){name,songs{name,duration}}}&indent=2. # ruby song.rb