Implementing union type with graphql-ruby - union

I'm trying to implement a union type with graphql-ruby.
I followed the official documentation but got the error listed below.
Here is my current code.
module Types
class AudioClipType < Types::BaseObject
field :id, Int, null: false
field :duration, Int, null: false
end
end
module Types
class MovieClipType < Types::BaseObject
field :id, Int, null: false
field :previewURL, String, null: false
field :resolution, Int, null: false
end
end
module Types
class MediaItemType < Types::BaseUnion
possible_types Types::AudioClipType, Types::MovieClipType
def self.resolve_type(object, context)
if object.is_a?(AudioClip)
Types::AudioClipType
else
Types::MovieClipType
end
end
end
end
module Types
class PostType < Types::BaseObject
description 'Post'
field :id, Int, null: false
field :media_item, Types::MediaItemType, null: true
end
end
And here is the graphql query.
{
posts {
id
mediaItem {
__typename
... on AudioClip {
id
duration
}
... on MovieClip {
id
previewURL
resolution
}
}
}
}
When I send the query I got the following error.
Failed to implement Post.mediaItem, tried:
- `Types::PostType#media_item`, which did not exist
- `Post#media_item`, which did not exist
- Looking up hash key `:media_item` or `"media_item"` on `#<Post:0x007fb385769428>`, but it wasn't a Hash
To implement this field, define one of the methods above (and check for typos
Couldn't find any typo or anything.
Am I missing something??

You didn't define parent type (superclass of your union).
So add
class Types::BaseUnion < GraphQL::Schema::Union
end
Now your inheritance chain will consistent.

Related

Scala, Collection.searching with user-defined implicit Ordering[]

I need to perform binary search on an array of custom case class. This should be as simple as calling the search function defined in scala.collection.Searching:
As you can see, if the collection on which I call the search method is an indexed sequence, the binary search is performed.
Now, I need to create my custom Ordering[B] parameter and I want to pass it explicitly to the search function (I don't want for it to take any implicit parameter inferred from context).
I have the following code:
// File 1
case class Person(name: String, id: Int)
object Person{
val orderingById: Ordering[Person] = Ordering.by(e => e.id)
}
// File 2 (same package)
for(i <- orderedId.indices) {
// orderedId is an array of Int
// listings is an array of Person
val listingIndex = listings.search(orderedId(i))(Person.orderingById)
...
}
I get the following error:
Type mismatch. Required: Ordering[Any], found: Ordering[Nothing]
So, I tried change the implementation in this way:
// file 1
object Person{
implicit def orderingById[A <: Person] : Ordering[A] = {
Ordering.by(e => e.id)
}
}
//file 2 as before
This time getting the following error:
Type mismatch. Required: Ordering[Any], found: Ordering[Person]
Why does it happen? At least in the second case, should it convert from Any to Person?
Follow the type specifications.
If you want to .search() on a collection of Person elements then the first search parameter should be a Person (or a super-class thereof).
val listingIndex =
listings.search(Person("",orderedId(i)))(Person.orderingById)
Or, to put it in a more complete and succinct context:
import scala.collection.Searching.SearchResult
case class Person(name: String, id: Int)
val listings: Array[Person] = ...
val orderedId: Array[Int] = ...
for(id <- orderedId) {
val listingIndex: SearchResult =
listings.search(Person("",id))(Ordering.by(_.id))
}
I'll add a bit just to elaborate about your error. First, please note that Searching.search is deprecated, with deprecation message:
Search methods are defined directly on SeqOps and do not require scala.collection.Searching any more.
search is now defined on IndexedSeqOps. Let's look at the signature:
final def search[B >: A](elem: B)(implicit ord: Ordering[B])
When you call:
listings.search(orderedId(i))(Person.orderingById)
The result of orderedId(i) is Int. Therefore, B in the signature above is Int. The definition of Int is:
final abstract class Int private extends AnyVal
A is Person, because listing is of type Array[Person]. Therefore, search, is looking for a common root for both Int and Person. This common root is Any, hence you are getting this error. One way to overcome it, is to define an implicit conversion from Int to Person:
object Person{
val orderingById: Ordering[Person] = Ordering.by(e => e.id)
implicit def apply(id: Int): Person = {
Person("not defined", id)
}
}
Then the following:
val listings = Array(Person("aa", 1), Person("bb", 2), Person("dd", 4))
val orderedId = 1.to(6).toArray
for(i <- orderedId.indices) {
// orderedId is an array of Int
// listings is an array of Person
listings.search[Person](orderedId(i))(Person.orderingById) match {
case Found(foundIndex) =>
println("foundIndex: " + foundIndex)
case InsertionPoint(insertionPoint) =>
println("insertionPoint: " + insertionPoint)
}
}
will produce:
foundIndex: 0
foundIndex: 1
insertionPoint: 2
foundIndex: 2
insertionPoint: 3
insertionPoint: 3
Code run in Scastie.

Validating array insertion in Rails

I have Table1 with attribute attribute1, which is an array of integer. My controller allows for one insertion at a time. So, in the controller:
def add_attribute1_item
table1 = Table1.find(params[:id])
table1.attribute1 << add_params[:attribute1_item]
table1.save!
render json: table1
rescue
render_errors_for(table1)
end
I want to validate this attribute1_item value, ignoring the old values that have been stored in attribute1 array, e.g. if table1.attribute1 contains 99 and I call the controller add_attribute1_item to add 100, I only want to check whether 100 is valid or not, ignoring 99.
class Task < ApplicationRecord
.
.
.
validate :attribute1_item_is_valid, if: -> { attribute1.present? }
.
.
.
def attribute1_item_is_valid
# validate the item by accessing attribute1
end
I am unsure with this approach because when I access attribute1 in attribute1_item_is_valid, it is the whole array instead of the new item. Is this approach good enough by calling attribute1.last() or is there a more correct method?
thank you
Instead of trying to validate this in the model, validate the form entry.
Create a model for the form and use normal validations.
class SomeForm
include ActiveModel::Model
attr_accessor :id, :attribute1_item
validate :id, presence: true, numericality: { only_integer: true }
validate :attribute1_item, presence: true
end
def add_attribute1_item
form = SomeForm.new(params)
if form.invalid?
# render form.errors
return
end
table1 = Table1.find(form.id)
table1.attribute1 << form.attribute1_item
table1.save!
render json: table1
rescue
render_errors_for(table1)
end

Restrictions on fields of case class as per db spec

I am creating bunch of case classes in scala which I will use to write to db. As the columns in the db has certain restrictions(Length, type, null/not null, etc). How can I enforce the length restriction on my case object fields without checking every field one by one?
This is how you can put the restructions on the fields of case class
object Solution1 extends App {
case class Payload(name: String, id: Int, address: String) {
require(name.length < 10)
require(address.length <= 50)
}
println(Payload("name5678910", 120, "earth")) // this will give you an erro
println(Payload("name", 121, "earth"))
}

Insert data into database with a method of a class

I am trying to create a method to insert data to the database but it does not work. I am trying to insert data by using add_person. Here is my code. The transparent table name is ZPERSON_20.
CLASS lcl_person DEFINITION.
PUBLIC SECTION.
METHODS:
add_person
IMPORTING
im_id TYPE zperson_20-person_id
im_name TYPE zperson_20-person_name
im_add TYPE zperson_20-person_address
im_type TYPE zperson_20-person_type.
PRIVATE SECTION.
DATA:
c_id TYPE zperson_20-person_id,
c_name TYPE zperson_20-person_name,
c_add TYPE zperson_20-person_address,
c_type TYPE zperson_20-person_type.
ENDCLASS.
CLASS lcl_person IMPLEMENTATION.
METHOD add_person.
DATA: it_emp TYPE STANDARD TABLE OF zperson_20.
DATA: wa_emp LIKE LINE OF it_emp.
wa_emp-person_id = c_id.
wa_emp-person_name = c_name.
wa_emp-person_add = c_add.
wa_emp-person_type = c_type.
INSERT INTO zperson_20 VALUES wa_emp.
ENDMETHOD.
ENDCLASS.
PARAMETERS:
v_id TYPE zperson_20-person_id,
v_name TYPE zperson_20-person_name,
v_add TYPE zperson_20-person_address,
v_type TYPE zperson_20-person_type.
DATA: lv_ref_person TYPE REF TO lcl_person.
START-OF-SELECTION.
CREATE OBJECT lv_ref_person.
CALL METHOD lv_ref_person->add_person(
im_id = v_id
im_name = v_name
im_add = v_add
im_type = v_type
).
I would say your issue is inside the add_person method, as you are reading the values from the member variables, instead of the method parameters. Those member variables will probably be empty in this case, leading to inserting errors due to an empty or duplicated id.
I believe that the code you are trying to achieve is the following:
REPORT ZRKD_BASE_WSLOADER.
CLASS lcl_person DEFINITION.
PUBLIC SECTION.
METHODS:
add_person
IMPORTING
im_id TYPE zperson_20-person_id
im_name TYPE zperson_20-person_name
im_add TYPE zperson_20-person_address
im_type TYPE zperson_20-person_type.
ENDCLASS.
CLASS lcl_person IMPLEMENTATION.
METHOD add_person.
DATA: wa_emp TYPE zperson_20.
//----> Here are the modifications <-------
wa_emp-person_id = im_id.
wa_emp-person_name = im_name.
wa_emp-person_address = im_add.
wa_emp-person_type = im_type.
INSERT INTO zperson_20 VALUES wa_emp.
ENDMETHOD.
ENDCLASS.
PARAMETERS:
v_id TYPE zperson_20-person_id,
v_name TYPE zperson_20-person_name,
v_add TYPE zperson_20-person_address,
v_type TYPE zperson_20-person_type.
DATA: lv_ref_person TYPE REF TO lcl_person.
START-OF-SELECTION.
CREATE OBJECT lv_ref_person.
CALL METHOD lv_ref_person->add_person(
im_id = v_id
im_name = v_name
im_add = v_add
im_type = v_type
).
Would be enough to create a commit method, which you then call, if You are really sure to save on db, or add this parameter as a flag to add person. If set, commit the transaction. The instruction for this is "commit work." and it is missing in the entire snippet. And without it nothing moves from Your actual LUW commit buffer to db.
Your method add_person is defined to take no arguments and you are passing it arguments. Basically there is a disagreement in call and the definition.
Can you post the whole code here?

How to use Slick's mapped tables with foreign keys?

I'm struggling with Slick's lifted embedding and mapped tables. The API feels strange to me, maybe just because it is structured in a way that's unfamiliar to me.
I want to build a Task/Todo-List. There are two entities:
Task: Each task has a an optional reference to the next task. That way a linked list is build. The intention is that the user can order the tasks by his priority. This order is represented by the references from task to task.
TaskList: Represents a TaskList with a label and a reference to the first Task of the list.
case class Task(id: Option[Long], title: String, nextTask: Option[Task])
case class TaskList(label: String, firstTask: Option[Task])
Now I tried to write a data access object (DAO) for these two entities.
import scala.slick.driver.H2Driver.simple._
import slick.lifted.MappedTypeMapper
implicit val session: Session = Database.threadLocalSession
val queryById = Tasks.createFinderBy( t => t.id )
def task(id: Long): Option[Task] = queryById(id).firstOption
private object Tasks extends Table[Task]("TASKS") {
def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
def title = column[String]("TITLE")
def nextTaskId = column[Option[Long]]("NEXT_TASK_ID")
def nextTask = foreignKey("NEXT_TASK_FK", nextTaskId, Tasks)(_.id)
def * = id ~ title ~ nextTask <> (Task, Task.unapply _)
}
private object TaskLists extends Table[TaskList]("TASKLISTS") {
def label = column[String]("LABEL", O.PrimaryKey)
def firstTaskId = column[Option[Long]]("FIRST_TASK_ID")
def firstTask = foreignKey("FIRST_TASK_FK", firstTaskId, Tasks)(_.id)
def * = label ~ firstTask <> (Task, Task.unapply _)
}
Unfortunately it does not compile. The problems are in the * projection of both tables at nextTask respective firstTask.
could not find implicit value for evidence parameter of type
scala.slick.lifted.TypeMapper[scala.slick.lifted.ForeignKeyQuery[SlickTaskRepository.this.Tasks.type,justf0rfun.bookmark.model.Task]]
could not find implicit value for evidence parameter of type scala.slick.lifted.TypeMapper[scala.slick.lifted.ForeignKeyQuery[SlickTaskRepository.this.Tasks.type,justf0rfun.bookmark.model.Task]]
I tried to solve that with the following TypeMapper but that does not compile, too.
implicit val taskMapper = MappedTypeMapper.base[Option[Long], Option[Task]](
option => option match {
case Some(id) => task(id)
case _ => None
},
option => option match {
case Some(task) => task.id
case _ => None
})
could not find implicit value for parameter tm: scala.slick.lifted.TypeMapper[Option[justf0rfun.bookmark.model.Task]]
not enough arguments for method base: (implicit tm: scala.slick.lifted.TypeMapper[Option[justf0rfun.bookmark.model.Task]])scala.slick.lifted.BaseTypeMapper[Option[Long]]. Unspecified value parameter tm.
Main question: How to use Slick's lifted embedding and mapped tables the right way? How to I get this to work?
Thanks in advance.
The short answer is: Use ids instead of object references and use Slick queries to dereference ids. You can put the queries into methods for re-use.
That would make your case classes look like this:
case class Task(id: Option[Long], title: String, nextTaskId: Option[Long])
case class TaskList(label: String, firstTaskId: Option[Long])
I'll publish an article about this topic at some point and link it here.

Resources