@@ 0,0 1,95 @@
+package valid
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/99designs/gqlgen/graphql"
+ "github.com/vektah/gqlparser/v2/gqlerror"
+)
+
+type Validation struct {
+ ctx context.Context
+ input map[string]interface{}
+}
+
+type ValidationError struct {
+ valid *Validation
+ err *gqlerror.Error
+}
+
+// Creates a new validation context.
+func New(ctx context.Context) *Validation {
+ return &Validation{
+ ctx: ctx,
+ }
+}
+
+// Adds an input map to a validation context.
+func (valid *Validation) WithInput(input map[string]interface{}) *Validation {
+ valid.input = input
+ return valid
+}
+
+// Returns true if no errors were found.
+func (valid *Validation) Ok() bool {
+ return len(graphql.GetErrors(valid.ctx)) == 0
+}
+
+// Fetches a string from the validation context, which must have an input
+// registered. If the field is not present, the callback is not run. If
+// present, but not a string, an error is recorded. Otherwise, the function is
+// called with the string for the user to conduct further validation with.
+func (valid *Validation) OptionalString(name string, fn func(s string)) {
+ if valid.input == nil {
+ panic("Attempted to validate fields without input")
+ }
+ if o, ok := valid.input[name]; ok {
+ s, ok := o.(string)
+ valid.
+ Expect(ok, fmt.Sprintf("Expected {} to be a string", name)).
+ WithField(name)
+ if ok {
+ fn(s)
+ }
+ }
+}
+
+// Asserts that a condition is true, recording a GraphQL error with the given
+// message if not.
+func (valid *Validation) Expect(cond bool, msg string) *ValidationError {
+ if cond {
+ return &ValidationError{valid: valid}
+ }
+ err := &gqlerror.Error{
+ Path: graphql.GetPath(valid.ctx),
+ Message: msg,
+ }
+ graphql.AddError(valid.ctx, err)
+ return &ValidationError{
+ valid: valid,
+ err: err,
+ }
+}
+
+// Associates a field name with an error.
+func (err *ValidationError) WithField(field string) *ValidationError {
+ if err.err == nil {
+ return err
+ }
+ if err.err.Extensions == nil {
+ err.err.Extensions = make(map[string]interface{})
+ }
+ err.err.Extensions["field"] = field
+ return err
+}
+
+// Composes another assertion onto the same validation context which initially
+// created an error. Short-circuiting is used, such that if the earlier
+// condition failed, the new condition is not considered.
+func (err *ValidationError) And(cond bool, msg string) *ValidationError {
+ if err.err != nil {
+ return err
+ }
+ return err.valid.Expect(cond, msg)
+}