スキーマと型 

このページでは、GraphQL型システムをベースにしたContrabandの型システムについて説明します。

Contrabandは、既存のJSONベースのAPIにアクセスしたり、独自のサービスを実装したりするために使用できます。

Contrabandスキーマ言語 

特定のプログラミング言語の構文に依存したくないため、Contrabandスキーマについて説明するために、GraphQLのスキーマ言語を拡張します。

Contrabandスキーマは、ファイル拡張子*.contraで保存する必要があります。

レコード型とフィールド 

Contrabandスキーマの最も基本的な構成要素はレコード型であり、サービスから取得できるオブジェクトの種類と、そのオブジェクトが持つフィールドを表します。Contrabandスキーマ言語では、次のように表現できます。

package com.example
@target(Scala)

## Character represents the characters in Star Wars.
type Character {
  name: String!
  appearsIn: [com.example.Episode]!
}

共通の用語を使用できるように、詳しく見ていきましょう。

これで、Contrabandレコード型がどのようなものか、およびContrabandスキーマ言語の基本を理解する方法がわかりました。

sinceアノテーション 

スキーマの進化を可能にするために、Contrabandレコードのフィールドは、追加されたバージョンを宣言できます。

package com.example
@target(Scala)

type Greeting {
  value: String!
  x: Int @since("0.2.0")
}

これは、valueフィールドが最初("0.0.0")から存在しているが、オプションのxフィールドがバージョン"0.2.0"から追加されたことを意味します。Contrabandは、バイナリ互換性を維持するために複数のコンストラクターを生成します。

Intはオプションであるため、xのデフォルト値としてNoneが使用されます。他のデフォルト値を指定するには、次のように記述できます。

package com.example
@target(Scala)

type Greeting {
  value: String!
  x: Int = 0 @since("0.2.0")
  p: Person = { name: "Foo" } @since("0.2.0")
  z: Person = raw"Person(\"Foo\")"
}

0は自動的にオプションでラップされることに注意してください。

スカラー型 

Contrabandには、すぐに使用できるデフォルトのスカラー型のセットが付属しています。

java.io.FileなどのJavaおよびScalaのクラス名も使用できます。

java.io.Fileなどのクラス名を使用する場合は、型のシリアル化およびデシリアライズの方法も指定する必要があります。

列挙型 

Enumとも呼ばれる列挙型は、許可された値の特定のセットに制限された特殊な種類のスカラーです。これにより、次のことが可能になります。

  1. この型の引数が、許可された値のいずれかであることを検証します。
  2. フィールドが常に有限の値のセットの1つになることを型システムを通じて伝えます。

これが、Contrabandスキーマ言語でのenum定義の例です。

package com.example
@target(Scala)

## Star Wars trilogy.
enum Episode {
  NewHope
  Empire
  Jedi
}

これは、スキーマでEpisode型を使用する場所では常に、NewHopeEmpire、またはJediのいずれかになることを期待することを意味します。

必須型 

レコード型とenumは、Contrabandで定義できる唯一の型です。ただし、スキーマの他の部分で型を使用する場合は、それらの値の検証に影響を与える追加の型修飾子を適用できます。例を見てみましょう。

package com.example
@target(Scala)

## Character represents the characters in Star Wars.
type Character {
  name: String!
  appearsIn: [com.example.Episode]!
  friends: lazy [com.example.Character]
}

ここでは、String型を使用し、型名の後に感嘆符!を追加して、必須としてマークしています。

リスト型 

リストは同様の方法で機能します。型修飾子を使用して型をリストとしてマークできます。これは、このフィールドがその型のリストを返すことを示します。スキーマ言語では、型を角かっこ[]で囲むことで示されます。

遅延型 

遅延型は、フィールドの初期化を最初に使用するまで延期します。スキーマ言語では、キーワードlazyで示されます。

インターフェース 

多くの型システムと同様に、Contrabandはインターフェースをサポートしています。インターフェースは抽象型であり、型がインターフェースを実装するために含める必要のある特定のフィールドのセットが含まれています。

たとえば、スターウォーズ三部作のすべてのキャラクターを表すCharacterインターフェースを作成できます。

package com.example
@target(Scala)

## Character represents the characters in Star Wars.
interface Character {
  name: String!
  appearsIn: [com.example.Episode]!
  friends: lazy [com.example.Character]
}

これは、Character実装するすべての型が、これらの正確なフィールドを持つ必要があることを意味します。

たとえば、Characterを実装する可能性のある型を次に示します。

package com.example
@target(Scala)

type Human implements Character {
  name: String!
  appearsIn: [com.example.Episode]!
  friends: lazy [com.example.Character]
  starships: [com.example.Starship]
  totalCredits: Int
}

type Droid implements Character {
  name: String!
  appearsIn: [com.example.Episode]!
  friends: lazy [com.example.Character]
  primaryFunction: String
}

これらの両方の型がCharacterインターフェースのすべてのフィールドを持っているだけでなく、特定の種類のキャラクターに固有の追加フィールドtotalCreditsstarships、およびprimaryFunctionも取り込んでいることがわかります。

メッセージ 

フィールドに加えて、インターフェースはメッセージを宣言することもできます。

package com.example
@target(Scala)

## Starship represents the starships in Star Wars.
interface Starship {
  name: String!
  length(unit: com.example.LengthUnit): Double
}

これは、Starship実装するすべての型が、正確なフィールドとメッセージの両方を持つ必要があることを意味します。

追加コード 

生成されたコードにScalaまたはJavaコードを挿入するためのエスケープハッチとして、Contrabandは特別なコメント表記を提供します。

## Example of an interface
interface IntfExample {
  field: Int

  #x // Some extra code

  #xinterface Interface1
  #xinterface Interface2

  #xtostring return "custom";

  #xcompanion // Some extra companion code

  #xcompanioninterface CompanionInterface1
  #xcompanioninterface CompanionInterface2
}