Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/deploy-website.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Deploy to GitHub Pages

on:
push:
branches:
- develop
# Review gh actions docs if you want to further define triggers, paths, etc
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on

jobs:
deploy:
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 18
cache: yarn

- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build website
run: yarn build

# Popular action to deploy to GitHub Pages:
# Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Build output to publish to the `gh-pages` branch:
publish_dir: ./build

publish_branch: master

# The following lines assign commit authorship to the official
# GH-Actions bot for deploys to `gh-pages` branch:
# https://github.com/actions/checkout/issues/13#issuecomment-724415212
# The GH actions bot is used by default if you didn't specify the two fields.
# You can swap them out with your own user credentials.
# user_name: github-actions[bot]
# user_email: 41898282+github-actions[bot]@users.noreply.github.com
24 changes: 24 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: PR Build

on:
pull_request:
branches:
- develop
# Review gh actions docs if you want to further define triggers, paths, etc
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on

jobs:
test-deploy:
name: Test deployment
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 18
cache: yarn

- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Test build website
run: yarn build
26 changes: 17 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
.DS_Store
# Dependencies
/node_modules

# Production
/build

node_modules
# Generated files
.docusaurus
.cache-loader

lib/core/metadata.js
lib/core/MetadataBlog.js
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

website/translated_docs
website/build/
website/yarn.lock
website/node_modules
website/i18n/*
npm-debug.log*
yarn-debug.log*
yarn-error.log*
6 changes: 0 additions & 6 deletions .prettierrc

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
This repository contains the source code for the GraphQL ASP.NET documentation website. Clone the `develop` branch to view the code and update the documentation. The `master` branch contains the compiled static site served by github pages.


_Documentation created using [Docusaurus v1.14.0](https://docusaurus.io)_
_Documentation created using [Docusaurus v2](https://docusaurus.io)_
3 changes: 3 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};
38 changes: 0 additions & 38 deletions build/publish-website.yaml

This file was deleted.

5 changes: 5 additions & 0 deletions docs/advanced/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"label": "Advanced",
"position": 4,
"collapsed": false
}
86 changes: 35 additions & 51 deletions docs/advanced/custom-scalars.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
id: custom-scalars
title: Custom Scalars
sidebar_label: Custom Scalars
sidebar_position: 3
---

Scalars are the most basic, fundamental unit of content in GraphQL. It is one of two leaf types (the other being [ENUMS](../types/enums)). When a query is resolved the returned data will be a set of nested key/value pairs where every key is a string and every value is either another set of key/value pairs, an enum or a scalar.
Expand All @@ -12,11 +13,7 @@ This can be done for any value that can be represented as a simple set of charac

Lets say we wanted to build a scalar called `Money` that can handle both an amount and currency symbol. We might accept it in a query like this:

<div class="sideBySideCode hljs">
<div>

```csharp
// C# Controller
public class InventoryController : GraphController
{
[QueryRoot("search")]
Expand All @@ -42,10 +39,7 @@ public class Money
}
```

</div>
<div>

```javascript
```graphql title="Using the Money Scalar"
query {
search(minPrice: "$18.45"){
id
Expand All @@ -54,44 +48,38 @@ query {
}
```

</div>
</div>
<br/>

The query supplies the data as a quoted string, `"$18.45"`, but our action method receives a `Money` object. Internally, GraphQL senses that the value should be `Money` from the schema definition and invokes the correct resolver to parse the value and generate the .NET object that can be passed to our action method.

## Implement IScalarGraphType

To create a scalar graph type we need to implement `IScalarGraphType` and register it with GraphQL. The methods and properties of `IScalarGraphType` are as follows:

```csharp
namespace GraphQL.AspNet.Interfaces.TypeSystem
```csharp title="IScalarGraphType.cs"
public interface IScalarGraphType
{
public interface IScalarGraphType
{
string Name { get; }
string InternalName { get; }
string Description { get; }
TypeKind Kind { get; }
bool Publish { get; }
ScalarValueType ValueType { get; }
Type ObjectType { get; }
TypeCollection OtherKnownTypes { get; }
ILeafValueResolver SourceResolver { get; }
IScalarValueSerializer Serializer { get; }

bool ValidateObject(object item);
}
string Name { get; }
string InternalName { get; }
string Description { get; }
TypeKind Kind { get; }
bool Publish { get; }
ScalarValueType ValueType { get; }
Type ObjectType { get; }
TypeCollection OtherKnownTypes { get; }
ILeafValueResolver SourceResolver { get; }
IScalarValueSerializer Serializer { get; }

bool ValidateObject(object item);
}

public interface ILeafValueResolver
{
object Resolve(ReadOnlySpan<char> data);
}
public interface ILeafValueResolver
{
object Resolve(ReadOnlySpan<char> data);
}

public interface IScalarValueSerializer
{
object Serialize(object item);
}
public interface IScalarValueSerializer
{
object Serialize(object item);
}
```

Expand Down Expand Up @@ -148,7 +136,7 @@ If you throw `UnresolvedValueException` your error message will be delivered ver

Taking a look at the at the serializer for the `Guid` scalar type we can see that while internally the `System.Guid` struct represents the value we convert it to a string when serializing it. Most scalar implementations will serialize to a string.

```csharp
```csharp title="GuidScalarSerializer.cs"
public class GuidScalarSerializer : IScalarValueSerializer
{
public object Serialize(object item)
Expand Down Expand Up @@ -231,27 +219,25 @@ The completed Money custom scalar type

The last step in declaring a scalar is to register it with the runtime. Scalars are schema agnostic. They sit outside of any dependency injection context and must be registered directly with GraphQL.

```csharp
// Startup.cs (other code)
public void ConfigureServices(IServiceCollection services)
{
// register the scalar type to the global provider
// BEFORE calling .AddGraphQL()
GraphQLProviders.ScalarProvider.RegisterCustomScalar(typeof(MoneyScalarType));
```csharp title="Register The Money Scalar "
// register the scalar type to the global provider
// BEFORE calling .AddGraphQL()
GraphQLProviders.ScalarProvider.RegisterCustomScalar(typeof(MoneyScalarType));

services.AddGraphQL();
}
services.AddGraphQL();
```

:::info
Since our scalar is represented by a .NET class, if we don't pre-register it GraphQL will attempt to parse the `Money` class as an object graph type. Once registered as a scalar, any attempt to use `Money` as an object graph type will cause an exception.
:::

## @specifiedBy Directive

GraphQL provides a special, built-in directive called `@specifiedBy` that allows you to supply a URL pointing to a the specification for your custom scalar. This url is used by various tools to additional data to your customers so they know how to interact with your scalar type. It is entirely optional.

The @specifiedBy directive can be applied to a scalar in all the same ways as other type system directives or by use of the special `[SpecifiedBy]` attribute.

```csharp
```csharp title="Apply the @specifiedBy"
// apply the directive to a single schema
GraphQLProviders.ScalarProvider.RegisterCustomScalar(typeof(MoneyScalarType));
services.AddGraphQL(o => {
Expand All @@ -275,7 +261,6 @@ public class MoneyScalarType : IScalarType
{
// ...
}

```

## Tips When Developing a Scalar
Expand All @@ -297,8 +282,7 @@ Avoid the urge to start declaring a lot of custom scalars. In fact, chances are
<div class="sideBySideCode hljs">
<div>

```csharp
// C# Controller
```csharp title="Money as an Input Object Graph Type"
public class InventoryController : GraphController
{
[QueryRoot("search")]
Expand All @@ -321,7 +305,7 @@ public class Money
</div>
<div>

```javascript
```graphql title="Using the Money Input Object"
query {
search(minPrice: {
symbol: "$"
Expand Down
Loading