• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

graphql-go / graphql / 1518

25 Feb 2023 06:27PM UTC coverage: 92.051%. Remained the same
1518

push

circle-ci

GitHub
Merge pull request #653 from dariuszkuc/fix_object_description

1 of 1 new or added line in 1 file covered. (100.0%)

5454 of 5925 relevant lines covered (92.05%)

308.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.46
/definition.go
1
package graphql
2

3
import (
4
        "context"
5
        "fmt"
6
        "reflect"
7
        "regexp"
8

9
        "github.com/graphql-go/graphql/language/ast"
10
)
11

12
// Type interface for all of the possible kinds of GraphQL types
13
type Type interface {
14
        Name() string
15
        Description() string
16
        String() string
17
        Error() error
18
}
19

20
var _ Type = (*Scalar)(nil)
21
var _ Type = (*Object)(nil)
22
var _ Type = (*Interface)(nil)
23
var _ Type = (*Union)(nil)
24
var _ Type = (*Enum)(nil)
25
var _ Type = (*InputObject)(nil)
26
var _ Type = (*List)(nil)
27
var _ Type = (*NonNull)(nil)
28
var _ Type = (*Argument)(nil)
29

30
// Input interface for types that may be used as input types for arguments and directives.
31
type Input interface {
32
        Name() string
33
        Description() string
34
        String() string
35
        Error() error
36
}
37

38
var _ Input = (*Scalar)(nil)
39
var _ Input = (*Enum)(nil)
40
var _ Input = (*InputObject)(nil)
41
var _ Input = (*List)(nil)
42
var _ Input = (*NonNull)(nil)
43

44
// IsInputType determines if given type is a GraphQLInputType
45
func IsInputType(ttype Type) bool {
67✔
46
        switch GetNamed(ttype).(type) {
67✔
47
        case *Scalar, *Enum, *InputObject:
53✔
48
                return true
53✔
49
        default:
14✔
50
                return false
14✔
51
        }
52
}
53

54
// IsOutputType determines if given type is a GraphQLOutputType
55
func IsOutputType(ttype Type) bool {
18✔
56
        switch GetNamed(ttype).(type) {
18✔
57
        case *Scalar, *Object, *Interface, *Union, *Enum:
15✔
58
                return true
15✔
59
        default:
3✔
60
                return false
3✔
61
        }
62
}
63

64
// Leaf interface for types that may be leaf values
65
type Leaf interface {
66
        Name() string
67
        Description() string
68
        String() string
69
        Error() error
70
        Serialize(value interface{}) interface{}
71
}
72

73
var _ Leaf = (*Scalar)(nil)
74
var _ Leaf = (*Enum)(nil)
75

76
// IsLeafType determines if given type is a leaf value
77
func IsLeafType(ttype Type) bool {
352✔
78
        switch GetNamed(ttype).(type) {
352✔
79
        case *Scalar, *Enum:
233✔
80
                return true
233✔
81
        default:
119✔
82
                return false
119✔
83
        }
84
}
85

86
// Output interface for types that may be used as output types as the result of fields.
87
type Output interface {
88
        Name() string
89
        Description() string
90
        String() string
91
        Error() error
92
}
93

94
var _ Output = (*Scalar)(nil)
95
var _ Output = (*Object)(nil)
96
var _ Output = (*Interface)(nil)
97
var _ Output = (*Union)(nil)
98
var _ Output = (*Enum)(nil)
99
var _ Output = (*List)(nil)
100
var _ Output = (*NonNull)(nil)
101

102
// Composite interface for types that may describe the parent context of a selection set.
103
type Composite interface {
104
        Name() string
105
        Description() string
106
        String() string
107
        Error() error
108
}
109

110
var _ Composite = (*Object)(nil)
111
var _ Composite = (*Interface)(nil)
112
var _ Composite = (*Union)(nil)
113

114
// IsCompositeType determines if given type is a GraphQLComposite type
115
func IsCompositeType(ttype interface{}) bool {
1,351✔
116
        switch ttype.(type) {
1,351✔
117
        case *Object, *Interface, *Union:
1,150✔
118
                return true
1,150✔
119
        default:
201✔
120
                return false
201✔
121
        }
122
}
123

124
// Abstract interface for types that may describe the parent context of a selection set.
125
type Abstract interface {
126
        Name() string
127
}
128

129
var _ Abstract = (*Interface)(nil)
130
var _ Abstract = (*Union)(nil)
131

132
func IsAbstractType(ttype interface{}) bool {
×
133
        switch ttype.(type) {
×
134
        case *Interface, *Union:
×
135
                return true
×
136
        default:
×
137
                return false
×
138
        }
139
}
140

141
// Nullable interface for types that can accept null as a value.
142
type Nullable interface {
143
}
144

145
var _ Nullable = (*Scalar)(nil)
146
var _ Nullable = (*Object)(nil)
147
var _ Nullable = (*Interface)(nil)
148
var _ Nullable = (*Union)(nil)
149
var _ Nullable = (*Enum)(nil)
150
var _ Nullable = (*InputObject)(nil)
151
var _ Nullable = (*List)(nil)
152

153
// GetNullable returns the Nullable type of the given GraphQL type
154
func GetNullable(ttype Type) Nullable {
11✔
155
        if ttype, ok := ttype.(*NonNull); ok {
11✔
156
                return ttype.OfType
×
157
        }
×
158
        return ttype
11✔
159
}
160

161
// Named interface for types that do not include modifiers like List or NonNull.
162
type Named interface {
163
        String() string
164
}
165

166
var _ Named = (*Scalar)(nil)
167
var _ Named = (*Object)(nil)
168
var _ Named = (*Interface)(nil)
169
var _ Named = (*Union)(nil)
170
var _ Named = (*Enum)(nil)
171
var _ Named = (*InputObject)(nil)
172

173
// GetNamed returns the Named type of the given GraphQL type
174
func GetNamed(ttype Type) Named {
1,847✔
175
        unmodifiedType := ttype
1,847✔
176
        for {
4,030✔
177
                switch typ := unmodifiedType.(type) {
2,183✔
178
                case *List:
150✔
179
                        unmodifiedType = typ.OfType
150✔
180
                case *NonNull:
186✔
181
                        unmodifiedType = typ.OfType
186✔
182
                default:
1,847✔
183
                        return unmodifiedType
1,847✔
184
                }
185
        }
186
}
187

188
// Scalar Type Definition
189
//
190
// The leaf values of any request and input values to arguments are
191
// Scalars (or Enums) and are defined with a name and a series of functions
192
// used to parse input from ast or variables and to ensure validity.
193
//
194
// Example:
195
//
196
//    var OddType = new Scalar({
197
//      name: 'Odd',
198
//      serialize(value) {
199
//        return value % 2 === 1 ? value : null;
200
//      }
201
//    });
202
//
203
type Scalar struct {
204
        PrivateName        string `json:"name"`
205
        PrivateDescription string `json:"description"`
206

207
        scalarConfig ScalarConfig
208
        err          error
209
}
210

211
// SerializeFn is a function type for serializing a GraphQLScalar type value
212
type SerializeFn func(value interface{}) interface{}
213

214
// ParseValueFn is a function type for parsing the value of a GraphQLScalar type
215
type ParseValueFn func(value interface{}) interface{}
216

217
// ParseLiteralFn is a function type for parsing the literal value of a GraphQLScalar type
218
type ParseLiteralFn func(valueAST ast.Value) interface{}
219

220
// ScalarConfig options for creating a new GraphQLScalar
221
type ScalarConfig struct {
222
        Name         string `json:"name"`
223
        Description  string `json:"description"`
224
        Serialize    SerializeFn
225
        ParseValue   ParseValueFn
226
        ParseLiteral ParseLiteralFn
227
}
228

229
// NewScalar creates a new GraphQLScalar
230
func NewScalar(config ScalarConfig) *Scalar {
14✔
231
        st := &Scalar{}
14✔
232
        err := invariant(config.Name != "", "Type must be named.")
14✔
233
        if err != nil {
14✔
234
                st.err = err
×
235
                return st
×
236
        }
×
237

238
        err = assertValidName(config.Name)
14✔
239
        if err != nil {
14✔
240
                st.err = err
×
241
                return st
×
242
        }
×
243

244
        st.PrivateName = config.Name
14✔
245
        st.PrivateDescription = config.Description
14✔
246

14✔
247
        err = invariantf(
14✔
248
                config.Serialize != nil,
14✔
249
                `%v must provide "serialize" function. If this custom Scalar is `+
14✔
250
                        `also used as an input type, ensure "parseValue" and "parseLiteral" `+
14✔
251
                        `functions are also provided.`, st,
14✔
252
        )
14✔
253
        if err != nil {
15✔
254
                st.err = err
1✔
255
                return st
1✔
256
        }
1✔
257
        if config.ParseValue != nil || config.ParseLiteral != nil {
24✔
258
                err = invariantf(
11✔
259
                        config.ParseValue != nil && config.ParseLiteral != nil,
11✔
260
                        `%v must provide both "parseValue" and "parseLiteral" functions.`, st,
11✔
261
                )
11✔
262
                if err != nil {
13✔
263
                        st.err = err
2✔
264
                        return st
2✔
265
                }
2✔
266
        }
267

268
        st.scalarConfig = config
11✔
269
        return st
11✔
270
}
271
func (st *Scalar) Serialize(value interface{}) interface{} {
810✔
272
        if st.scalarConfig.Serialize == nil {
810✔
273
                return value
×
274
        }
×
275
        return st.scalarConfig.Serialize(value)
810✔
276
}
277
func (st *Scalar) ParseValue(value interface{}) interface{} {
57✔
278
        if st.scalarConfig.ParseValue == nil {
57✔
279
                return value
×
280
        }
×
281
        return st.scalarConfig.ParseValue(value)
57✔
282
}
283
func (st *Scalar) ParseLiteral(valueAST ast.Value) interface{} {
229✔
284
        if st.scalarConfig.ParseLiteral == nil {
229✔
285
                return nil
×
286
        }
×
287
        return st.scalarConfig.ParseLiteral(valueAST)
229✔
288
}
289
func (st *Scalar) Name() string {
18,272✔
290
        return st.PrivateName
18,272✔
291
}
18,272✔
292
func (st *Scalar) Description() string {
×
293
        return st.PrivateDescription
×
294

×
295
}
×
296
func (st *Scalar) String() string {
2,662✔
297
        return st.PrivateName
2,662✔
298
}
2,662✔
299
func (st *Scalar) Error() error {
385✔
300
        return st.err
385✔
301
}
385✔
302

303
// Object Type Definition
304
//
305
// Almost all of the GraphQL types you define will be object  Object types
306
// have a name, but most importantly describe their fields.
307
// Example:
308
//
309
//    var AddressType = new Object({
310
//      name: 'Address',
311
//      fields: {
312
//        street: { type: String },
313
//        number: { type: Int },
314
//        formatted: {
315
//          type: String,
316
//          resolve(obj) {
317
//            return obj.number + ' ' + obj.street
318
//          }
319
//        }
320
//      }
321
//    });
322
//
323
// When two types need to refer to each other, or a type needs to refer to
324
// itself in a field, you can use a function expression (aka a closure or a
325
// thunk) to supply the fields lazily.
326
//
327
// Example:
328
//
329
//    var PersonType = new Object({
330
//      name: 'Person',
331
//      fields: () => ({
332
//        name: { type: String },
333
//        bestFriend: { type: PersonType },
334
//      })
335
//    });
336
//
337
// /
338
type Object struct {
339
        PrivateName        string `json:"name"`
340
        PrivateDescription string `json:"description"`
341
        IsTypeOf           IsTypeOfFn
342

343
        typeConfig            ObjectConfig
344
        initialisedFields     bool
345
        fields                FieldDefinitionMap
346
        initialisedInterfaces bool
347
        interfaces            []*Interface
348
        // Interim alternative to throwing an error during schema definition at run-time
349
        err error
350
}
351

352
// IsTypeOfParams Params for IsTypeOfFn()
353
type IsTypeOfParams struct {
354
        // Value that needs to be resolve.
355
        // Use this to decide which GraphQLObject this value maps to.
356
        Value interface{}
357

358
        // Info is a collection of information about the current execution state.
359
        Info ResolveInfo
360

361
        // Context argument is a context value that is provided to every resolve function within an execution.
362
        // It is commonly
363
        // used to represent an authenticated user, or request-specific caches.
364
        Context context.Context
365
}
366

367
type IsTypeOfFn func(p IsTypeOfParams) bool
368

369
type InterfacesThunk func() []*Interface
370

371
type ObjectConfig struct {
372
        Name        string      `json:"name"`
373
        Interfaces  interface{} `json:"interfaces"`
374
        Fields      interface{} `json:"fields"`
375
        IsTypeOf    IsTypeOfFn  `json:"isTypeOf"`
376
        Description string      `json:"description"`
377
}
378

379
type FieldsThunk func() Fields
380

381
func NewObject(config ObjectConfig) *Object {
479✔
382
        objectType := &Object{}
479✔
383

479✔
384
        err := invariant(config.Name != "", "Type must be named.")
479✔
385
        if err != nil {
479✔
386
                objectType.err = err
×
387
                return objectType
×
388
        }
×
389
        err = assertValidName(config.Name)
479✔
390
        if err != nil {
479✔
391
                objectType.err = err
×
392
                return objectType
×
393
        }
×
394

395
        objectType.PrivateName = config.Name
479✔
396
        objectType.PrivateDescription = config.Description
479✔
397
        objectType.IsTypeOf = config.IsTypeOf
479✔
398
        objectType.typeConfig = config
479✔
399

479✔
400
        return objectType
479✔
401
}
402

403
// ensureCache ensures that both fields and interfaces have been initialized properly,
404
// to prevent races.
405
func (gt *Object) ensureCache() {
6✔
406
        gt.Fields()
6✔
407
        gt.Interfaces()
6✔
408
}
6✔
409
func (gt *Object) AddFieldConfig(fieldName string, fieldConfig *Field) {
51✔
410
        if fieldName == "" || fieldConfig == nil {
51✔
411
                return
×
412
        }
×
413
        if fields, ok := gt.typeConfig.Fields.(Fields); ok {
102✔
414
                fields[fieldName] = fieldConfig
51✔
415
                gt.initialisedFields = false
51✔
416
        }
51✔
417
}
418
func (gt *Object) Name() string {
15,208✔
419
        return gt.PrivateName
15,208✔
420
}
15,208✔
421
func (gt *Object) Description() string {
×
422
        return gt.PrivateDescription
×
423
}
×
424
func (gt *Object) String() string {
6,886✔
425
        return gt.PrivateName
6,886✔
426
}
6,886✔
427
func (gt *Object) Fields() FieldDefinitionMap {
5,283✔
428
        if gt.initialisedFields {
10,098✔
429
                return gt.fields
4,815✔
430
        }
4,815✔
431

432
        var configureFields Fields
468✔
433
        switch fields := gt.typeConfig.Fields.(type) {
468✔
434
        case Fields:
454✔
435
                configureFields = fields
454✔
436
        case FieldsThunk:
13✔
437
                configureFields = fields()
13✔
438
        }
439

440
        gt.fields, gt.err = defineFieldMap(gt, configureFields)
468✔
441
        gt.initialisedFields = true
468✔
442
        return gt.fields
468✔
443
}
444

445
func (gt *Object) Interfaces() []*Interface {
6,328✔
446
        if gt.initialisedInterfaces {
12,192✔
447
                return gt.interfaces
5,864✔
448
        }
5,864✔
449

450
        var configInterfaces []*Interface
464✔
451
        switch iface := gt.typeConfig.Interfaces.(type) {
464✔
452
        case InterfacesThunk:
6✔
453
                configInterfaces = iface()
6✔
454
        case []*Interface:
41✔
455
                configInterfaces = iface
41✔
456
        case nil:
417✔
457
        default:
×
458
                gt.err = fmt.Errorf("Unknown Object.Interfaces type: %T", gt.typeConfig.Interfaces)
×
459
                gt.initialisedInterfaces = true
×
460
                return nil
×
461
        }
462

463
        gt.interfaces, gt.err = defineInterfaces(gt, configInterfaces)
464✔
464
        gt.initialisedInterfaces = true
464✔
465
        return gt.interfaces
464✔
466
}
467

468
func (gt *Object) Error() error {
817✔
469
        return gt.err
817✔
470
}
817✔
471

472
func defineInterfaces(ttype *Object, interfaces []*Interface) ([]*Interface, error) {
464✔
473
        ifaces := []*Interface{}
464✔
474

464✔
475
        if len(interfaces) == 0 {
881✔
476
                return ifaces, nil
417✔
477
        }
417✔
478
        for _, iface := range interfaces {
101✔
479
                err := invariantf(
54✔
480
                        iface != nil,
54✔
481
                        `%v may only implement Interface types, it cannot implement: %v.`, ttype, iface,
54✔
482
                )
54✔
483
                if err != nil {
55✔
484
                        return ifaces, err
1✔
485
                }
1✔
486
                if iface.ResolveType != nil {
86✔
487
                        err = invariantf(
33✔
488
                                iface.ResolveType != nil,
33✔
489
                                `Interface Type %v does not provide a "resolveType" function `+
33✔
490
                                        `and implementing Type %v does not provide a "isTypeOf" `+
33✔
491
                                        `function. There is no way to resolve this implementing type `+
33✔
492
                                        `during execution.`, iface, ttype,
33✔
493
                        )
33✔
494
                        if err != nil {
33✔
495
                                return ifaces, err
×
496
                        }
×
497
                }
498
                ifaces = append(ifaces, iface)
53✔
499
        }
500

501
        return ifaces, nil
46✔
502
}
503

504
func defineFieldMap(ttype Named, fieldMap Fields) (FieldDefinitionMap, error) {
533✔
505
        resultFieldMap := FieldDefinitionMap{}
533✔
506

533✔
507
        err := invariantf(
533✔
508
                len(fieldMap) > 0,
533✔
509
                `%v fields must be an object with field names as keys or a function which return such an object.`, ttype,
533✔
510
        )
533✔
511
        if err != nil {
535✔
512
                return resultFieldMap, err
2✔
513
        }
2✔
514

515
        for fieldName, field := range fieldMap {
1,328✔
516
                if field == nil {
797✔
517
                        continue
×
518
                }
519
                err = invariantf(
797✔
520
                        field.Type != nil,
797✔
521
                        `%v.%v field type must be Output Type but got: %v.`, ttype, fieldName, field.Type,
797✔
522
                )
797✔
523
                if err != nil {
799✔
524
                        return resultFieldMap, err
2✔
525
                }
2✔
526
                if field.Type.Error() != nil {
800✔
527
                        return resultFieldMap, field.Type.Error()
5✔
528
                }
5✔
529
                if err = assertValidName(fieldName); err != nil {
791✔
530
                        return resultFieldMap, err
1✔
531
                }
1✔
532
                fieldDef := &FieldDefinition{
789✔
533
                        Name:              fieldName,
789✔
534
                        Description:       field.Description,
789✔
535
                        Type:              field.Type,
789✔
536
                        Resolve:           field.Resolve,
789✔
537
                        Subscribe:         field.Subscribe,
789✔
538
                        DeprecationReason: field.DeprecationReason,
789✔
539
                }
789✔
540

789✔
541
                fieldDef.Args = []*Argument{}
789✔
542
                for argName, arg := range field.Args {
934✔
543
                        if err = assertValidName(argName); err != nil {
146✔
544
                                return resultFieldMap, err
1✔
545
                        }
1✔
546
                        if err = invariantf(
144✔
547
                                arg != nil,
144✔
548
                                `%v.%v args must be an object with argument names as keys.`, ttype, fieldName,
144✔
549
                        ); err != nil {
144✔
550
                                return resultFieldMap, err
×
551
                        }
×
552
                        if err = invariantf(
144✔
553
                                arg.Type != nil,
144✔
554
                                `%v.%v(%v:) argument type must be Input Type but got: %v.`, ttype, fieldName, argName, arg.Type,
144✔
555
                        ); err != nil {
145✔
556
                                return resultFieldMap, err
1✔
557
                        }
1✔
558
                        fieldArg := &Argument{
143✔
559
                                PrivateName:        argName,
143✔
560
                                PrivateDescription: arg.Description,
143✔
561
                                Type:               arg.Type,
143✔
562
                                DefaultValue:       arg.DefaultValue,
143✔
563
                        }
143✔
564
                        fieldDef.Args = append(fieldDef.Args, fieldArg)
143✔
565
                }
566
                resultFieldMap[fieldName] = fieldDef
787✔
567
        }
568
        return resultFieldMap, nil
521✔
569
}
570

571
// ResolveParams Params for FieldResolveFn()
572
type ResolveParams struct {
573
        // Source is the source value
574
        Source interface{}
575

576
        // Args is a map of arguments for current GraphQL request
577
        Args map[string]interface{}
578

579
        // Info is a collection of information about the current execution state.
580
        Info ResolveInfo
581

582
        // Context argument is a context value that is provided to every resolve function within an execution.
583
        // It is commonly
584
        // used to represent an authenticated user, or request-specific caches.
585
        Context context.Context
586
}
587

588
type FieldResolveFn func(p ResolveParams) (interface{}, error)
589

590
type ResolveInfo struct {
591
        FieldName      string
592
        FieldASTs      []*ast.Field
593
        Path           *ResponsePath
594
        ReturnType     Output
595
        ParentType     Composite
596
        Schema         Schema
597
        Fragments      map[string]ast.Definition
598
        RootValue      interface{}
599
        Operation      ast.Definition
600
        VariableValues map[string]interface{}
601
}
602

603
type Fields map[string]*Field
604

605
type Field struct {
606
        Name              string              `json:"name"` // used by graphlql-relay
607
        Type              Output              `json:"type"`
608
        Args              FieldConfigArgument `json:"args"`
609
        Resolve           FieldResolveFn      `json:"-"`
610
        Subscribe         FieldResolveFn      `json:"-"`
611
        DeprecationReason string              `json:"deprecationReason"`
612
        Description       string              `json:"description"`
613
}
614

615
type FieldConfigArgument map[string]*ArgumentConfig
616

617
type ArgumentConfig struct {
618
        Type         Input       `json:"type"`
619
        DefaultValue interface{} `json:"defaultValue"`
620
        Description  string      `json:"description"`
621
}
622

623
type FieldDefinitionMap map[string]*FieldDefinition
624
type FieldDefinition struct {
625
        Name              string         `json:"name"`
626
        Description       string         `json:"description"`
627
        Type              Output         `json:"type"`
628
        Args              []*Argument    `json:"args"`
629
        Resolve           FieldResolveFn `json:"-"`
630
        Subscribe         FieldResolveFn `json:"-"`
631
        DeprecationReason string         `json:"deprecationReason"`
632
}
633

634
type FieldArgument struct {
635
        Name         string      `json:"name"`
636
        Type         Type        `json:"type"`
637
        DefaultValue interface{} `json:"defaultValue"`
638
        Description  string      `json:"description"`
639
}
640

641
type Argument struct {
642
        PrivateName        string      `json:"name"`
643
        Type               Input       `json:"type"`
644
        DefaultValue       interface{} `json:"defaultValue"`
645
        PrivateDescription string      `json:"description"`
646
}
647

648
func (st *Argument) Name() string {
699✔
649
        return st.PrivateName
699✔
650
}
699✔
651
func (st *Argument) Description() string {
×
652
        return st.PrivateDescription
×
653

×
654
}
×
655
func (st *Argument) String() string {
×
656
        return st.PrivateName
×
657
}
×
658
func (st *Argument) Error() error {
×
659
        return nil
×
660
}
×
661

662
// Interface Type Definition
663
//
664
// When a field can return one of a heterogeneous set of types, a Interface type
665
// is used to describe what types are possible, what fields are in common across
666
// all types, as well as a function to determine which type is actually used
667
// when the field is resolved.
668
//
669
// Example:
670
//
671
//     var EntityType = new Interface({
672
//       name: 'Entity',
673
//       fields: {
674
//         name: { type: String }
675
//       }
676
//     });
677
//
678
//
679
type Interface struct {
680
        PrivateName        string `json:"name"`
681
        PrivateDescription string `json:"description"`
682
        ResolveType        ResolveTypeFn
683

684
        typeConfig        InterfaceConfig
685
        initialisedFields bool
686
        fields            FieldDefinitionMap
687
        err               error
688
}
689
type InterfaceConfig struct {
690
        Name        string      `json:"name"`
691
        Fields      interface{} `json:"fields"`
692
        ResolveType ResolveTypeFn
693
        Description string `json:"description"`
694
}
695

696
// ResolveTypeParams Params for ResolveTypeFn()
697
type ResolveTypeParams struct {
698
        // Value that needs to be resolve.
699
        // Use this to decide which GraphQLObject this value maps to.
700
        Value interface{}
701

702
        // Info is a collection of information about the current execution state.
703
        Info ResolveInfo
704

705
        // Context argument is a context value that is provided to every resolve function within an execution.
706
        // It is commonly
707
        // used to represent an authenticated user, or request-specific caches.
708
        Context context.Context
709
}
710

711
type ResolveTypeFn func(p ResolveTypeParams) *Object
712

713
func NewInterface(config InterfaceConfig) *Interface {
66✔
714
        it := &Interface{}
66✔
715

66✔
716
        if it.err = invariant(config.Name != "", "Type must be named."); it.err != nil {
66✔
717
                return it
×
718
        }
×
719
        if it.err = assertValidName(config.Name); it.err != nil {
66✔
720
                return it
×
721
        }
×
722
        it.PrivateName = config.Name
66✔
723
        it.PrivateDescription = config.Description
66✔
724
        it.ResolveType = config.ResolveType
66✔
725
        it.typeConfig = config
66✔
726

66✔
727
        return it
66✔
728
}
729

730
func (it *Interface) AddFieldConfig(fieldName string, fieldConfig *Field) {
1✔
731
        if fieldName == "" || fieldConfig == nil {
1✔
732
                return
×
733
        }
×
734
        if fields, ok := it.typeConfig.Fields.(Fields); ok {
2✔
735
                fields[fieldName] = fieldConfig
1✔
736
                it.initialisedFields = false
1✔
737
        }
1✔
738
}
739

740
func (it *Interface) Name() string {
641✔
741
        return it.PrivateName
641✔
742
}
641✔
743

744
func (it *Interface) Description() string {
×
745
        return it.PrivateDescription
×
746
}
×
747

748
func (it *Interface) Fields() (fields FieldDefinitionMap) {
216✔
749
        if it.initialisedFields {
367✔
750
                return it.fields
151✔
751
        }
151✔
752

753
        var configureFields Fields
65✔
754
        switch fields := it.typeConfig.Fields.(type) {
65✔
755
        case Fields:
63✔
756
                configureFields = fields
63✔
757
        case FieldsThunk:
2✔
758
                configureFields = fields()
2✔
759
        }
760

761
        it.fields, it.err = defineFieldMap(it, configureFields)
65✔
762
        it.initialisedFields = true
65✔
763
        return it.fields
65✔
764
}
765

766
func (it *Interface) String() string {
41✔
767
        return it.PrivateName
41✔
768
}
41✔
769

770
func (it *Interface) Error() error {
112✔
771
        return it.err
112✔
772
}
112✔
773

774
// Union Type Definition
775
//
776
// When a field can return one of a heterogeneous set of types, a Union type
777
// is used to describe what types are possible as well as providing a function
778
// to determine which type is actually used when the field is resolved.
779
//
780
// Example:
781
//
782
//     var PetType = new Union({
783
//       name: 'Pet',
784
//       types: [ DogType, CatType ],
785
//       resolveType(value) {
786
//         if (value instanceof Dog) {
787
//           return DogType;
788
//         }
789
//         if (value instanceof Cat) {
790
//           return CatType;
791
//         }
792
//       }
793
//     });
794
type Union struct {
795
        PrivateName        string `json:"name"`
796
        PrivateDescription string `json:"description"`
797
        ResolveType        ResolveTypeFn
798

799
        typeConfig      UnionConfig
800
        initalizedTypes bool
801
        types           []*Object
802
        possibleTypes   map[string]bool
803

804
        err error
805
}
806

807
type UnionTypesThunk func() []*Object
808

809
type UnionConfig struct {
810
        Name        string      `json:"name"`
811
        Types       interface{} `json:"types"`
812
        ResolveType ResolveTypeFn
813
        Description string `json:"description"`
814
}
815

816
func NewUnion(config UnionConfig) *Union {
21✔
817
        objectType := &Union{}
21✔
818

21✔
819
        if objectType.err = invariant(config.Name != "", "Type must be named."); objectType.err != nil {
21✔
820
                return objectType
×
821
        }
×
822
        if objectType.err = assertValidName(config.Name); objectType.err != nil {
21✔
823
                return objectType
×
824
        }
×
825
        objectType.PrivateName = config.Name
21✔
826
        objectType.PrivateDescription = config.Description
21✔
827
        objectType.ResolveType = config.ResolveType
21✔
828

21✔
829
        objectType.typeConfig = config
21✔
830

21✔
831
        return objectType
21✔
832
}
833

834
func (ut *Union) Types() []*Object {
59✔
835
        if ut.initalizedTypes {
98✔
836
                return ut.types
39✔
837
        }
39✔
838

839
        var unionTypes []*Object
20✔
840
        switch utype := ut.typeConfig.Types.(type) {
20✔
841
        case UnionTypesThunk:
1✔
842
                unionTypes = utype()
1✔
843
        case []*Object:
17✔
844
                unionTypes = utype
17✔
845
        case nil:
1✔
846
        default:
1✔
847
                ut.err = fmt.Errorf("Unknown Union.Types type: %T", ut.typeConfig.Types)
1✔
848
                ut.initalizedTypes = true
1✔
849
                return nil
1✔
850
        }
851

852
        ut.types, ut.err = defineUnionTypes(ut, unionTypes)
19✔
853
        ut.initalizedTypes = true
19✔
854
        return ut.types
19✔
855
}
856

857
func defineUnionTypes(objectType *Union, unionTypes []*Object) ([]*Object, error) {
19✔
858
        definedUnionTypes := []*Object{}
19✔
859

19✔
860
        if err := invariantf(
19✔
861
                len(unionTypes) > 0,
19✔
862
                `Must provide Array of types for Union %v.`, objectType.Name(),
19✔
863
        ); err != nil {
21✔
864
                return definedUnionTypes, err
2✔
865
        }
2✔
866

867
        for _, ttype := range unionTypes {
41✔
868
                if err := invariantf(
24✔
869
                        ttype != nil,
24✔
870
                        `%v may only contain Object types, it cannot contain: %v.`, objectType, ttype,
24✔
871
                ); err != nil {
26✔
872
                        return definedUnionTypes, err
2✔
873
                }
2✔
874
                if objectType.ResolveType == nil {
29✔
875
                        if err := invariantf(
7✔
876
                                ttype.IsTypeOf != nil,
7✔
877
                                `Union Type %v does not provide a "resolveType" function `+
7✔
878
                                        `and possible Type %v does not provide a "isTypeOf" `+
7✔
879
                                        `function. There is no way to resolve this possible type `+
7✔
880
                                        `during execution.`, objectType, ttype,
7✔
881
                        ); err != nil {
8✔
882
                                return definedUnionTypes, err
1✔
883
                        }
1✔
884
                }
885
                definedUnionTypes = append(definedUnionTypes, ttype)
21✔
886
        }
887

888
        return definedUnionTypes, nil
14✔
889
}
890

891
func (ut *Union) String() string {
24✔
892
        return ut.PrivateName
24✔
893
}
24✔
894

895
func (ut *Union) Name() string {
153✔
896
        return ut.PrivateName
153✔
897
}
153✔
898

899
func (ut *Union) Description() string {
×
900
        return ut.PrivateDescription
×
901
}
×
902

903
func (ut *Union) Error() error {
55✔
904
        return ut.err
55✔
905
}
55✔
906

907
// Enum Type Definition
908
//
909
// Some leaf values of requests and input values are Enums. GraphQL serializes
910
// Enum values as strings, however internally Enums can be represented by any
911
// kind of type, often integers.
912
//
913
// Example:
914
//
915
//     var RGBType = new Enum({
916
//       name: 'RGB',
917
//       values: {
918
//         RED: { value: 0 },
919
//         GREEN: { value: 1 },
920
//         BLUE: { value: 2 }
921
//       }
922
//     });
923
//
924
// Note: If a value is not provided in a definition, the name of the enum value
925
// will be used as its internal value.
926

927
type Enum struct {
928
        PrivateName        string `json:"name"`
929
        PrivateDescription string `json:"description"`
930

931
        enumConfig   EnumConfig
932
        values       []*EnumValueDefinition
933
        valuesLookup map[interface{}]*EnumValueDefinition
934
        nameLookup   map[string]*EnumValueDefinition
935

936
        err error
937
}
938
type EnumValueConfigMap map[string]*EnumValueConfig
939
type EnumValueConfig struct {
940
        Value             interface{} `json:"value"`
941
        DeprecationReason string      `json:"deprecationReason"`
942
        Description       string      `json:"description"`
943
}
944
type EnumConfig struct {
945
        Name        string             `json:"name"`
946
        Values      EnumValueConfigMap `json:"values"`
947
        Description string             `json:"description"`
948
}
949
type EnumValueDefinition struct {
950
        Name              string      `json:"name"`
951
        Value             interface{} `json:"value"`
952
        DeprecationReason string      `json:"deprecationReason"`
953
        Description       string      `json:"description"`
954
}
955

956
func NewEnum(config EnumConfig) *Enum {
14✔
957
        gt := &Enum{}
14✔
958
        gt.enumConfig = config
14✔
959

14✔
960
        if gt.err = assertValidName(config.Name); gt.err != nil {
14✔
961
                return gt
×
962
        }
×
963

964
        gt.PrivateName = config.Name
14✔
965
        gt.PrivateDescription = config.Description
14✔
966
        if gt.values, gt.err = gt.defineEnumValues(config.Values); gt.err != nil {
16✔
967
                return gt
2✔
968
        }
2✔
969

970
        return gt
12✔
971
}
972
func (gt *Enum) defineEnumValues(valueMap EnumValueConfigMap) ([]*EnumValueDefinition, error) {
14✔
973
        var err error
14✔
974
        values := []*EnumValueDefinition{}
14✔
975

14✔
976
        if err = invariantf(
14✔
977
                len(valueMap) > 0,
14✔
978
                `%v values must be an object with value names as keys.`, gt,
14✔
979
        ); err != nil {
16✔
980
                return values, err
2✔
981
        }
2✔
982

983
        for valueName, valueConfig := range valueMap {
63✔
984
                if err = invariantf(
51✔
985
                        valueConfig != nil,
51✔
986
                        `%v.%v must refer to an object with a "value" key `+
51✔
987
                                `representing an internal value but got: %v.`, gt, valueName, valueConfig,
51✔
988
                ); err != nil {
51✔
989
                        return values, err
×
990
                }
×
991
                if err = assertValidName(valueName); err != nil {
51✔
992
                        return values, err
×
993
                }
×
994
                value := &EnumValueDefinition{
51✔
995
                        Name:              valueName,
51✔
996
                        Value:             valueConfig.Value,
51✔
997
                        DeprecationReason: valueConfig.DeprecationReason,
51✔
998
                        Description:       valueConfig.Description,
51✔
999
                }
51✔
1000
                if value.Value == nil {
55✔
1001
                        value.Value = valueName
4✔
1002
                }
4✔
1003
                values = append(values, value)
51✔
1004
        }
1005
        return values, nil
12✔
1006
}
1007
func (gt *Enum) Values() []*EnumValueDefinition {
12✔
1008
        return gt.values
12✔
1009
}
12✔
1010
func (gt *Enum) Serialize(value interface{}) interface{} {
125✔
1011
        v := value
125✔
1012
        rv := reflect.ValueOf(v)
125✔
1013
        if kind := rv.Kind(); kind == reflect.Ptr && rv.IsNil() {
125✔
1014
                return nil
×
1015
        } else if kind == reflect.Ptr {
126✔
1016
                v = reflect.Indirect(reflect.ValueOf(v)).Interface()
1✔
1017
        }
1✔
1018
        if enumValue, ok := gt.getValueLookup()[v]; ok {
249✔
1019
                return enumValue.Name
124✔
1020
        }
124✔
1021
        return nil
1✔
1022
}
1023
func (gt *Enum) ParseValue(value interface{}) interface{} {
7✔
1024
        var v string
7✔
1025

7✔
1026
        switch value := value.(type) {
7✔
1027
        case string:
6✔
1028
                v = value
6✔
1029
        case *string:
×
1030
                v = *value
×
1031
        default:
1✔
1032
                return nil
1✔
1033
        }
1034
        if enumValue, ok := gt.getNameLookup()[v]; ok {
12✔
1035
                return enumValue.Value
6✔
1036
        }
6✔
1037
        return nil
×
1038
}
1039
func (gt *Enum) ParseLiteral(valueAST ast.Value) interface{} {
17✔
1040
        if valueAST, ok := valueAST.(*ast.EnumValue); ok {
28✔
1041
                if enumValue, ok := gt.getNameLookup()[valueAST.Value]; ok {
20✔
1042
                        return enumValue.Value
9✔
1043
                }
9✔
1044
        }
1045
        return nil
8✔
1046
}
1047
func (gt *Enum) Name() string {
1,766✔
1048
        return gt.PrivateName
1,766✔
1049
}
1,766✔
1050
func (gt *Enum) Description() string {
×
1051
        return gt.PrivateDescription
×
1052
}
×
1053
func (gt *Enum) String() string {
1,115✔
1054
        return gt.PrivateName
1,115✔
1055
}
1,115✔
1056
func (gt *Enum) Error() error {
18✔
1057
        return gt.err
18✔
1058
}
18✔
1059
func (gt *Enum) getValueLookup() map[interface{}]*EnumValueDefinition {
125✔
1060
        if len(gt.valuesLookup) > 0 {
247✔
1061
                return gt.valuesLookup
122✔
1062
        }
122✔
1063
        valuesLookup := map[interface{}]*EnumValueDefinition{}
3✔
1064
        for _, value := range gt.Values() {
32✔
1065
                valuesLookup[value.Value] = value
29✔
1066
        }
29✔
1067
        gt.valuesLookup = valuesLookup
3✔
1068
        return gt.valuesLookup
3✔
1069
}
1070

1071
func (gt *Enum) getNameLookup() map[string]*EnumValueDefinition {
17✔
1072
        if len(gt.nameLookup) > 0 {
32✔
1073
                return gt.nameLookup
15✔
1074
        }
15✔
1075
        nameLookup := map[string]*EnumValueDefinition{}
2✔
1076
        for _, value := range gt.Values() {
8✔
1077
                nameLookup[value.Name] = value
6✔
1078
        }
6✔
1079
        gt.nameLookup = nameLookup
2✔
1080
        return gt.nameLookup
2✔
1081
}
1082

1083
// InputObject Type Definition
1084
//
1085
// An input object defines a structured collection of fields which may be
1086
// supplied to a field argument.
1087
//
1088
// Using `NonNull` will ensure that a value must be provided by the query
1089
//
1090
// Example:
1091
//
1092
//     var GeoPoint = new InputObject({
1093
//       name: 'GeoPoint',
1094
//       fields: {
1095
//         lat: { type: new NonNull(Float) },
1096
//         lon: { type: new NonNull(Float) },
1097
//         alt: { type: Float, defaultValue: 0 },
1098
//       }
1099
//     });
1100
type InputObject struct {
1101
        PrivateName        string `json:"name"`
1102
        PrivateDescription string `json:"description"`
1103

1104
        typeConfig InputObjectConfig
1105
        fields     InputObjectFieldMap
1106
        init       bool
1107
        err        error
1108
}
1109
type InputObjectFieldConfig struct {
1110
        Type         Input       `json:"type"`
1111
        DefaultValue interface{} `json:"defaultValue"`
1112
        Description  string      `json:"description"`
1113
}
1114
type InputObjectField struct {
1115
        PrivateName        string      `json:"name"`
1116
        Type               Input       `json:"type"`
1117
        DefaultValue       interface{} `json:"defaultValue"`
1118
        PrivateDescription string      `json:"description"`
1119
}
1120

1121
func (st *InputObjectField) Name() string {
×
1122
        return st.PrivateName
×
1123
}
×
1124
func (st *InputObjectField) Description() string {
×
1125
        return st.PrivateDescription
×
1126
}
×
1127
func (st *InputObjectField) String() string {
×
1128
        return st.PrivateName
×
1129
}
×
1130
func (st *InputObjectField) Error() error {
×
1131
        return nil
×
1132
}
×
1133

1134
type InputObjectConfigFieldMap map[string]*InputObjectFieldConfig
1135
type InputObjectFieldMap map[string]*InputObjectField
1136
type InputObjectConfigFieldMapThunk func() InputObjectConfigFieldMap
1137
type InputObjectConfig struct {
1138
        Name        string      `json:"name"`
1139
        Fields      interface{} `json:"fields"`
1140
        Description string      `json:"description"`
1141
}
1142

1143
func NewInputObject(config InputObjectConfig) *InputObject {
33✔
1144
        gt := &InputObject{}
33✔
1145
        if gt.err = invariant(config.Name != "", "Type must be named."); gt.err != nil {
33✔
1146
                return gt
×
1147
        }
×
1148

1149
        gt.PrivateName = config.Name
33✔
1150
        gt.PrivateDescription = config.Description
33✔
1151
        gt.typeConfig = config
33✔
1152
        return gt
33✔
1153
}
1154

1155
func (gt *InputObject) defineFieldMap() InputObjectFieldMap {
32✔
1156
        var (
32✔
1157
                fieldMap InputObjectConfigFieldMap
32✔
1158
                err      error
32✔
1159
        )
32✔
1160
        switch fields := gt.typeConfig.Fields.(type) {
32✔
1161
        case InputObjectConfigFieldMap:
30✔
1162
                fieldMap = fields
30✔
1163
        case InputObjectConfigFieldMapThunk:
1✔
1164
                fieldMap = fields()
1✔
1165
        }
1166
        resultFieldMap := InputObjectFieldMap{}
32✔
1167

32✔
1168
        if gt.err = invariantf(
32✔
1169
                len(fieldMap) > 0,
32✔
1170
                `%v fields must be an object with field names as keys or a function which return such an object.`, gt,
32✔
1171
        ); gt.err != nil {
34✔
1172
                return resultFieldMap
2✔
1173
        }
2✔
1174

1175
        for fieldName, fieldConfig := range fieldMap {
72✔
1176
                if fieldConfig == nil {
42✔
1177
                        continue
×
1178
                }
1179
                if err = assertValidName(fieldName); err != nil {
42✔
1180
                        continue
×
1181
                }
1182
                if gt.err = invariantf(
42✔
1183
                        fieldConfig.Type != nil,
42✔
1184
                        `%v.%v field type must be Input Type but got: %v.`, gt, fieldName, fieldConfig.Type,
42✔
1185
                ); gt.err != nil {
43✔
1186
                        return resultFieldMap
1✔
1187
                }
1✔
1188
                field := &InputObjectField{}
41✔
1189
                field.PrivateName = fieldName
41✔
1190
                field.Type = fieldConfig.Type
41✔
1191
                field.PrivateDescription = fieldConfig.Description
41✔
1192
                field.DefaultValue = fieldConfig.DefaultValue
41✔
1193
                resultFieldMap[fieldName] = field
41✔
1194
        }
1195
        gt.init = true
29✔
1196
        return resultFieldMap
29✔
1197
}
1198

1199
func (gt *InputObject) AddFieldConfig(fieldName string, fieldConfig *InputObjectFieldConfig) {
1✔
1200
        if fieldName == "" || fieldConfig == nil {
1✔
1201
                return
×
1202
        }
×
1203
        fieldMap, ok := gt.typeConfig.Fields.(InputObjectConfigFieldMap)
1✔
1204
        if gt.err = invariant(ok, "Cannot add field to a thunk"); gt.err != nil {
1✔
1205
                return
×
1206
        }
×
1207
        fieldMap[fieldName] = fieldConfig
1✔
1208
        gt.fields = gt.defineFieldMap()
1✔
1209
}
1210

1211
func (gt *InputObject) Fields() InputObjectFieldMap {
91✔
1212
        if !gt.init {
122✔
1213
                gt.fields = gt.defineFieldMap()
31✔
1214
        }
31✔
1215
        return gt.fields
91✔
1216
}
1217
func (gt *InputObject) Name() string {
116✔
1218
        return gt.PrivateName
116✔
1219
}
116✔
1220
func (gt *InputObject) Description() string {
×
1221
        return gt.PrivateDescription
×
1222
}
×
1223
func (gt *InputObject) String() string {
16✔
1224
        return gt.PrivateName
16✔
1225
}
16✔
1226
func (gt *InputObject) Error() error {
×
1227
        return gt.err
×
1228
}
×
1229

1230
// List Modifier
1231
//
1232
// A list is a kind of type marker, a wrapping type which points to another
1233
// type. Lists are often created within the context of defining the fields of
1234
// an object type.
1235
//
1236
// Example:
1237
//
1238
//     var PersonType = new Object({
1239
//       name: 'Person',
1240
//       fields: () => ({
1241
//         parents: { type: new List(Person) },
1242
//         children: { type: new List(Person) },
1243
//       })
1244
//     })
1245
//
1246
type List struct {
1247
        OfType Type `json:"ofType"`
1248

1249
        err error
1250
}
1251

1252
func NewList(ofType Type) *List {
200✔
1253
        gl := &List{}
200✔
1254

200✔
1255
        gl.err = invariantf(ofType != nil, `Can only create List of a Type but got: %v.`, ofType)
200✔
1256
        if gl.err != nil {
201✔
1257
                return gl
1✔
1258
        }
1✔
1259

1260
        gl.OfType = ofType
199✔
1261
        return gl
199✔
1262
}
1263
func (gl *List) Name() string {
2,850✔
1264
        return fmt.Sprintf("%v", gl.OfType)
2,850✔
1265
}
2,850✔
1266
func (gl *List) Description() string {
×
1267
        return ""
×
1268
}
×
1269
func (gl *List) String() string {
1,411✔
1270
        if gl.OfType != nil {
2,822✔
1271
                return fmt.Sprintf("[%v]", gl.OfType)
1,411✔
1272
        }
1,411✔
1273
        return ""
×
1274
}
1275
func (gl *List) Error() error {
100✔
1276
        return gl.err
100✔
1277
}
100✔
1278

1279
// NonNull Modifier
1280
//
1281
// A non-null is a kind of type marker, a wrapping type which points to another
1282
// type. Non-null types enforce that their values are never null and can ensure
1283
// an error is raised if this ever occurs during a request. It is useful for
1284
// fields which you can make a strong guarantee on non-nullability, for example
1285
// usually the id field of a database row will never be null.
1286
//
1287
// Example:
1288
//
1289
//     var RowType = new Object({
1290
//       name: 'Row',
1291
//       fields: () => ({
1292
//         id: { type: new NonNull(String) },
1293
//       })
1294
//     })
1295
//
1296
// Note: the enforcement of non-nullability occurs within the executor.
1297
type NonNull struct {
1298
        OfType Type `json:"ofType"`
1299

1300
        err error
1301
}
1302

1303
func NewNonNull(ofType Type) *NonNull {
250✔
1304
        gl := &NonNull{}
250✔
1305

250✔
1306
        _, isOfTypeNonNull := ofType.(*NonNull)
250✔
1307
        gl.err = invariantf(ofType != nil && !isOfTypeNonNull, `Can only create NonNull of a Nullable Type but got: %v.`, ofType)
250✔
1308
        if gl.err != nil {
255✔
1309
                return gl
5✔
1310
        }
5✔
1311
        gl.OfType = ofType
245✔
1312
        return gl
245✔
1313
}
1314
func (gl *NonNull) Name() string {
11,855✔
1315
        return fmt.Sprintf("%v!", gl.OfType)
11,855✔
1316
}
11,855✔
1317
func (gl *NonNull) Description() string {
×
1318
        return ""
×
1319
}
×
1320
func (gl *NonNull) String() string {
4,133✔
1321
        if gl.OfType != nil {
8,266✔
1322
                return gl.Name()
4,133✔
1323
        }
4,133✔
1324
        return ""
×
1325
}
1326
func (gl *NonNull) Error() error {
139✔
1327
        return gl.err
139✔
1328
}
139✔
1329

1330
var NameRegExp = regexp.MustCompile("^[_a-zA-Z][_a-zA-Z0-9]*$")
1331

1332
func assertValidName(name string) error {
1,650✔
1333
        return invariantf(
1,650✔
1334
                NameRegExp.MatchString(name),
1,650✔
1335
                `Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "%v" does not.`, name)
1,650✔
1336

1,650✔
1337
}
1,650✔
1338

1339
type ResponsePath struct {
1340
        Prev *ResponsePath
1341
        Key  interface{}
1342
}
1343

1344
// WithKey returns a new responsePath containing the new key.
1345
func (p *ResponsePath) WithKey(key interface{}) *ResponsePath {
1,750✔
1346
        return &ResponsePath{
1,750✔
1347
                Prev: p,
1,750✔
1348
                Key:  key,
1,750✔
1349
        }
1,750✔
1350
}
1,750✔
1351

1352
// AsArray returns an array of path keys.
1353
func (p *ResponsePath) AsArray() []interface{} {
734✔
1354
        if p == nil {
925✔
1355
                return nil
191✔
1356
        }
191✔
1357
        return append(p.Prev.AsArray(), p.Key)
543✔
1358
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc