I've been searching documentation but I don't see anything regarding unique constraints in GraphQL with Ruby.
How can you enforce a unique constraint on a string that you want to pass through a mutation?
module Types
class PersonType < Types::BaseObject
field :name, String, null:false
end
end
I've tried adding ", unique: true" to the field, but it doesn't recognize the keyword unique.
You can enforce the unique constraint rule on the resolver of the mutation:
const resolvers={
...
Mutation:{
someField:async(parent,{name},context)=>{
if(name === 'something'){
return context.dataSource.createUser(....)
}else(e){
throw new Error("This name is not allowed")
}
}
}
}
Related
I have been struggling with a Client request to filter out A model(Candidate) on the basis of Column Format
The issue I am facing is that column inputs either SSN or EIN.
The format for SSN is (xxx-xx-xxxx)
The format for EIN is (xx-xxxxxxx)
My candidates Table contains the field ssn_or_ein , which takes either one of these 2.
For ex:
candidate111.ssn_or_ein => 111-11-1111
candidate222.ssn_or_ein => 22-2222222
I have tried fetching all the 4000 accounts, but i suppose that's not how a developer's approach should be.
I am still learning Rails and any tip would be really helpful.
You can do this with a like query. Put it in a scope so it's easily available.
class Candidate < ApplicationRecord
scope with_ein -> { where( "ssn_or_ein like ?", "__-_______" }
scope with_ssn -> { where( "ssn_or_ein like ?", "___-__-____" }
end
However, this can get slow if ssn_or_ein is not properly indexed.
Consider storing them in two different columns. This makes validation and querying simpler. Bring them together only when you just need a TIN - Taxpayer Information Number.
class Candidate < ApplicationRecord
scope with_ein -> { where.not( ein: nil ) }
scope with_ssn -> { where.not( ssn: nil ) }
EIN_RE = %r{^\d{2}-\d{7}$}
SSN_RE = %r{^\d{3}-\d{2}-\d{4}$}
validates :ein, format: { with: EIN_RE }, allow_nil: true
validates :ssn, format: { with: SSN_RE }, allow_nil: true
def tin
ssn || ein
end
class << self
def find_by_tin(tin)
where( ssn: tin ).or( where(ein: tin) )
end
end
end
I would also suggest that you store the data "normalized", without the dashes, just the numbers. This is simpler to work with, and the accepted format can be changed without having to change all the data. Format them in a decorator.
module Types::ProgramType
include Types::BaseInterface
description "Objects which inherit from Program"
graphql_name "Program"
orphan_types Types::SomeProgramType, Types::AnotherProgramType, Types::ThirdProgramType
field :id, ID, null: false
field :type, Integer, null: false
field :name, String, null: false
definition_methods do
def self.resolve_type(object, _context)
case object.class
when SomeProgram then SomeProgramType
when AnotherProgram then AnotherProgramType
when ThirdProgram then ThirdProgramType
else
raise "Unknown program type"
end
end
end
end
module Types
class SomeProgramType < Types::BaseObject
implements Types:ProgramType
field :description, String, null: false
end
end
I also added queries for SomeProgram types in the query_type file. I was under the impression that adding "implement " to the object types, would allow them to inherit the fields from the interface (per this link: https://graphql-ruby.org/type_definitions/interfaces) and I would be able to query like this:
query {
someProgram(id: 1) {
name
type
description
}
}
But I am getting errors in graphiql, like "Field 'name' doesn't exist on type 'SomeProgram'". What am I missing?
Update:
My QueryType class:
class QueryType < Types::BaseObject
# Add `node(id: ID!) and `nodes(ids: [ID!]!)`
include GraphQL::Types::Relay::HasNodeField
include GraphQL::Types::Relay::HasNodesField
field :some_programs, [SomeProgramType], null: false,
description: "all some programs"
def some_programs
SomeProgram.all
end
field :some_program, SomeProgramType, null: false do
argument :id, ID, required: true
end
def some_program(id:)
SomeProgram.find(id)
end
end
Can you also share how you expose someProgram in your query type?
You can also debug this by looking into the schema if you are using GraphiQL app. You can see the return type of someProgram which should be type of ProgramType.
You can also update your query to include __typename so maybe start with
query {
someProgram(id: 1) {
__typename
}
}
First to see what is the return type and if it's being properly handled in the resolve_type
I figured out the issue. I had put implements Types:SomeProgram instead of implements Types::SomeProgram. I was missing a colon.
I'm trying to implement a query type that can search by name of the record instead of id. Here's its definition in query_type.rb.
# Get game by name
field :game_by_name, Types::GameType, null: false do
argument :name, String, required: true
end
def game_by_name(name:)
Game.where(name: name) //find a game using the name attribute
end
But when I run:
query {
gameByName(name: "League of Legends") {
id
name
}
}
I get the following error.
Failed to implement Game.id, tried:\n\n
- `Types::GameType#id`, which did not exist\n
- `Game::ActiveRecord_Relation#id`, which did not exist\n
- Looking up hash key `:id` or `\"id\"` on `#<Game::ActiveRecord_Relation:0x00007f5644442888>`, but it wasn't a Hash\n\n
To implement this field, define one of the methods above (and check for typos)\n
This is odd because the following query type works perfectly.
# Get game by ID
field :game_by_id, Types::GameType, null: false do
argument :id, ID, required: true
end
def game_by_id(id:)
Game.find(id)
end
Here's game_type.rb:
module Types
class GameType < Types::BaseObject
field :id, ID, null: false
field :name, String, null: false
end
end
How do I go about fixing this? Thank you!
I stumbled upon this, chasing a similar error. Not sure if you solved it or not, but I believe the issue is in the query type definition.
You're telling it to return a type of Types::GameType, but the Active Record query returns an Active Record Relation, which is a collection. So, graphql is expecting a single instance of Game, but is instead receiving a collection. Graphql is then trying to map the returned value from the query to the type definition, but is unable to. The best hint is from this line:
Looking up hash key `:id` or `\"id\"` on `#<Game::ActiveRecord_Relation:0x00007f5644442888>`, but it wasn't a Hash..
Graphql is trying to assign :id to the ActiveRecord_Relation and it can't do it.
Two paths forward, depending on how you want the API to behave. Do you want it to return 1 record or many?
Wrapping the Types::GameType within brackets will tell graphql it's a collection and to iterate over the records
# Get game by name
field :game_by_name, [Types::GameType], null: false do
argument :name, String, required: true
end
def game_by_name(name:)
Game.where(name: name) //find a game using the name attribute
end
or have Active Record return just 1 record, something like...
# Get game by name
field :game_by_name, Types::GameType, null: false do
argument :name, String, required: true
end
def game_by_name(name:)
Game.where(name: name).limit(1).first //find a game using the name attribute
end
I know this is months old, but just putting it out there for anyone else who stumbles upon this question, like I did!
I wanna override the kind of _id generation in mongoid (another app, which shares the db uses String instead of ObjectId()
I can do it for every model by adding this:
field :_id, type: String, default: -> { BSON::ObjectId.new.to_s }
But how to globally attach this to keep it DRY?
Very valid usecase but looking into the code you might be out of luck at Mongoid::Fields, but you could overwrite mongoize which should go like this
Item.new.id
=> BSON::ObjectId('56e727892ada693ea8000000')
class BSON::ObjectId
def self.mongoize(k)
k.to_s
end
end
Item.new.id
=> "56e7276f2ada693737000002"
I'm trying to follow the advice in Mongoid 3 - Check for uniqueness of composite key to have a model with a unique constraint on 2 fields.
The id declaration is this:
field :_id, type: String, default: &method(:generate_id)
private
def generate_id
user.id.to_s + offering.id.to_s
end
But if I do this, it has a conniption when I instantiate an object via new because it tries to generate the id before it has a user and offering and it (rightly) doesn't want to use the id of nil. I can pass in the user and offering as constructor parameters and everything is fine.
My question is, is this the right way of doing this? It feels dirty given all the obtuse wackyness I have to do just to get a unique constraint. The code isn't very intent revealing at all. Is there a better way?
With plain MongoDB you would create this index with JavaScript like so (assuming your collection name is registrations):
db.registrations.ensureIndex( { "user_id": 1, "offering_id": 1 }, { unique: true } )
To generate this index with Mongoid, add this to your model:
index({ user_id: 1, offering_id: 1 }, { unique: true })
And run rake db:mongoid:create_indexes.
If you want to keep generating your _id with generate_id, you could move the generation to a before_validate callback.
field :_id, type: String
before_validate :generate_id
private
def generate_id
self._id ||= "#{user.id}:#{offering}"
end