caliban-extras

Build Status Sonatype Nexus (Releases) Sonatype Nexus (Snapshots) Scala Steward badge

caliban-refined

A tiny module to add refined and newtype support for Schema and ArgBuilder

Add the following line in build.sbt

libraryDependencies += "com.github.niqdev" %% "caliban-refined" % "0.1.5"

Replace all the custom implementations like

implicit val nonEmptyStringSchema: Schema[Any, NonEmptyString] =
  Schema.stringSchema.contramap(_.value)

implicit val nonNegIntArgBuilder: ArgBuilder[NonNegInt] = {	
  case value: IntValue =>	
    NonNegInt.from(value.toInt).leftMap(CalibanError.ExecutionError(_))	
  case other =>
    Left(CalibanError.ExecutionError(s"Can't build a NonNegInt from input $other"))	
}

with an import

// add import
import caliban.refined._

@newtype case class Id(int: PosInt)
@newtype case class Name(string: NonEmptyString)
case class User(id: Id, name: Name)
case class UserArg(id: Id)
case class Query(user: UserArg => User)

val resolver = Query(arg => User(arg.id, Name("myName")))
val api      = GraphQL.graphQL(RootResolver(resolver))

See the tests for a complete example

Example

A minimalistic version of GraphQL GitHub api with pagination, filters and authentication (TODO)

# run example
sbt -jvm-debug 5005 "examples/runMain com.github.niqdev.caliban.Main"

# verify endpoint
http -v :8080/api/graphql query='{users(first:1){totalCount}}'

Sample queries

query counts {
  countUsers: users(first: 1) {
    totalCount
  }
  countRepositories: repositories(first: 1) {
    totalCount
  }
  countIssues: issues(first: 1) {
    totalCount
  }
}
{
  "data": {
    "countUsers": {
      "totalCount": 2
    },
    "countRepositories": {
      "totalCount": 40
    },
    "countIssues": {
      "totalCount": 40
    }
  }
}
query nodes {
  userNodes: users(first: 1) {
    nodes {
      id
      name
      createdAt
      updatedAt
      #repository > issue | issues
      #repositories > issue | issues
    }
  }
  repositoryNodes: repositories(first: 1) {
    nodes {
      id
      name
      url
      isFork
      createdAt
      updatedAt
      #issue
      #issues
    }
  }
  issueNodes: issues(first: 1) {
    nodes {
      id
      number
      status
      title
      body
      createdAt
      updatedAt
    }
  }
}
{
  "data": {
    "userNodes": {
      "nodes": [
        {
          "id": "dXNlcjp2MTpmMGZiZTEzMS0zZjY1LTQxNDUtYjM3My01YmJmYzFjOWExYWU=",
          "name": "zio",
          "createdAt": "2020-08-11T18:25:25.411363Z",
          "updatedAt": "2020-08-11T18:25:25.411363Z"
        }
      ]
    },
    "repositoryNodes": {
      "nodes": [
        {
          "id": "cmVwb3NpdG9yeTp2MToxOTQ2MDQ4Ny01ZmZkLTRkMzgtYjBlOS0xNmRiNDQ4NDYxNTU=",
          "name": "zio-s3",
          "url": "https://github.com/zio/zio-s3",
          "isFork": false,
          "createdAt": "2020-08-11T18:25:25.496188Z",
          "updatedAt": "2020-08-11T18:25:25.496188Z"
        }
      ]
    },
    "issueNodes": {
      "nodes": [
        {
          "id": "aXNzdWU6djE6MjMzNTlhMjktZDQxYy00ODAxLWE2MjMtNGM2YzNmMGU5NjMy",
          "number": 27,
          "status": "CLOSE",
          "title": "title6",
          "body": "body6",
          "createdAt": "2020-08-11T18:25:25.571263Z",
          "updatedAt": "2020-08-11T18:25:25.571263Z"
        }
      ]
    }
  }
}
# user:v1:f0fbe131-3f65-4145-b373-5bbfc1c9a1ae
echo "dXNlcjp2MTpmMGZiZTEzMS0zZjY1LTQxNDUtYjM3My01YmJmYzFjOWExYWU=" | base64 --decode

# repository:v1:19460487-5ffd-4d38-b0e9-16db44846155
echo "cmVwb3NpdG9yeTp2MToxOTQ2MDQ4Ny01ZmZkLTRkMzgtYjBlOS0xNmRiNDQ4NDYxNTU=" | base64 --decode

# issue:v1:23359a29-d41c-4801-a623-4c6c3f0e9632
echo "aXNzdWU6djE6MjMzNTlhMjktZDQxYy00ODAxLWE2MjMtNGM2YzNmMGU5NjMy" | base64 --decode
query findByParameter {
  user(name: "zio") {
    name
    # it doesn't verify user ownership
    repository(name: "shapeless") {
      name
      url
      isFork
      # it doesn't verify repository ownership
      issue(number: 1) {
        number
        status
        title
        body
      }
    }
  }
}
{
  "data": {
    "user": {
      "name": "zio",
      "repository": {
        "name": "shapeless",
        "url": "https://github.com/milessabin/shapeless",
        "isFork": false,
        "issue": {
          "number": 1,
          "status": "OPEN",
          "title": "title0",
          "body": "body0"
        }
      }
    }
  }
}
query findUserPaginated {
  users(first: 10, after: "opaqueCursor") {
    edges {
      cursor
      node {
        name
        repositories(first: 4) {
          nodes {
            name
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      hasPreviousPage
      startCursor
      endCursor
    }
    totalCount
  }
}
{
  "data": {
    "users": {
      "edges": [
        {
          "cursor": "Y3Vyc29yOnYxOjI=",
          "node": {
            "name": "zio",
            "repositories": {
              "nodes": [
                {
                  "name": "zio-lambda"
                },
                {
                  "name": "zio-web"
                },
                {
                  "name": "zio-ftp"
                },
                {
                  "name": "zio-actors"
                }
              ]
            }
          }
        },
        {
          "cursor": "Y3Vyc29yOnYxOjE=",
          "node": {
            "name": "typelevel",
            "repositories": {
              "nodes": [
                {
                  "name": "scodec"
                },
                {
                  "name": "ciris"
                },
                {
                  "name": "refined"
                },
                {
                  "name": "shapeless"
                }
              ]
            }
          }
        }
      ],
      "pageInfo": {
        "hasNextPage": false,
        "hasPreviousPage": false,
        "startCursor": "Y3Vyc29yOnYxOjI=",
        "endCursor": "Y3Vyc29yOnYxOjE="
      },
      "totalCount": 2
    }
  }
}
query findByNodeIds {
  nodes(
    ids: [
      "dXNlcjp2MTpmMGZiZTEzMS0zZjY1LTQxNDUtYjM3My01YmJmYzFjOWExYWU=",
      "cmVwb3NpdG9yeTp2MToyOTBlYzI2NS1lYzkxLTRhOWItYmRkYS03YjA5NzBkYjk5Y2I=",
      "aXNzdWU6djE6MjMzNTlhMjktZDQxYy00ODAxLWE2MjMtNGM2YzNmMGU5NjMy"
    ]
  ) {
    id
    ... on User {
      name
    }
    ... on Repository {
      name
      url
      isFork
    }
    ... on Issue {
      number
      status
      title
      body
    }
  }
}
{
  "data": {
    "nodes": [
      {
        "id": "dXNlcjp2MTpmMGZiZTEzMS0zZjY1LTQxNDUtYjM3My01YmJmYzFjOWExYWU=",
        "name": "zio"
      },
      {
        "id": "cmVwb3NpdG9yeTp2MToyOTBlYzI2NS1lYzkxLTRhOWItYmRkYS03YjA5NzBkYjk5Y2I=",
        "name": "refined",
        "url": "https://github.com/fthomas/refined",
        "isFork": false
      },
      {
        "id": "aXNzdWU6djE6MjMzNTlhMjktZDQxYy00ODAxLWE2MjMtNGM2YzNmMGU5NjMy",
        "number": 27,
        "status": "CLOSE",
        "title": "title6",
        "body": "body6"
      }
    ]
  }
}

Resources

TODO