Skip to content

Commit

Permalink
Add validation rule for argument uniqueness
Browse files Browse the repository at this point in the history
  • Loading branch information
leebyron committed Aug 7, 2015
1 parent cab585f commit e7f7fee
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 0 deletions.
139 changes: 139 additions & 0 deletions src/validation/__tests__/UniqueArgumentNames.js
@@ -0,0 +1,139 @@
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

import { describe, it } from 'mocha';
import { expectPassesRule, expectFailsRule } from './harness';
import UniqueArgumentNames from '../rules/UniqueArgumentNames';
import { duplicateArgMessage } from '../errors';

function duplicateArg(argName, l1, c1, l2, c2) {
return {
message: duplicateArgMessage(argName),
locations: [ { line: l1, column: c1 }, { line: l2, column: c2 } ],
};
}

describe('Validate: Unique argument names', () => {

it('no arguments on field', () => {
expectPassesRule(UniqueArgumentNames, `
{
field
}
`);
});

it('no arguments on directive', () => {
expectPassesRule(UniqueArgumentNames, `
{
field
}
`);
});

it('argument on field', () => {
expectPassesRule(UniqueArgumentNames, `
{
field(arg: "value")
}
`);
});

it('argument on directive', () => {
expectPassesRule(UniqueArgumentNames, `
{
field @directive(arg: "value")
}
`);
});

it('same argument on two fields', () => {
expectPassesRule(UniqueArgumentNames, `
{
one: field(arg: "value")
two: field(arg: "value")
}
`);
});

it('same argument on field and directive', () => {
expectPassesRule(UniqueArgumentNames, `
{
field(arg: "value") @directive(arg: "value")
}
`);
});

it('same argument on two directives', () => {
expectPassesRule(UniqueArgumentNames, `
{
field @directive1(arg: "value") @directive2(arg: "value")
}
`);
});

it('multiple field arguments', () => {
expectPassesRule(UniqueArgumentNames, `
{
field(arg1: "value", arg2: "value", arg3: "value")
}
`);
});

it('multiple directive arguments', () => {
expectPassesRule(UniqueArgumentNames, `
{
field @directive(arg1: "value", arg2: "value", arg3: "value")
}
`);
});

it('duplicate field arguments', () => {
expectFailsRule(UniqueArgumentNames, `
{
field(arg1: "value", arg1: "value")
}
`, [
duplicateArg('arg1', 3, 15, 3, 30)
]);
});

it('many duplicate field arguments', () => {
expectFailsRule(UniqueArgumentNames, `
{
field(arg1: "value", arg1: "value", arg1: "value")
}
`, [
duplicateArg('arg1', 3, 15, 3, 30),
duplicateArg('arg1', 3, 15, 3, 45)
]);
});

it('duplicate directive arguments', () => {
expectFailsRule(UniqueArgumentNames, `
{
field @directive(arg1: "value", arg1: "value")
}
`, [
duplicateArg('arg1', 3, 26, 3, 41)
]);
});

it('many duplicate directive arguments', () => {
expectFailsRule(UniqueArgumentNames, `
{
field @directive(arg1: "value", arg1: "value", arg1: "value")
}
`, [
duplicateArg('arg1', 3, 26, 3, 41),
duplicateArg('arg1', 3, 26, 3, 56)
]);
});

});
3 changes: 3 additions & 0 deletions src/validation/allRules.js
Expand Up @@ -40,6 +40,8 @@ import NoUnusedVariables from './rules/NoUnusedVariables';
import KnownDirectives from './rules/KnownDirectives';
// Spec Section: "Argument Names"
import KnownArgumentNames from './rules/KnownArgumentNames';
// Spec Section: "Argument Uniqueness"
import UniqueArgumentNames from './rules/UniqueArgumentNames';
// Spec Section: "Argument Values Type Correctness"
import ArgumentsOfCorrectType from './rules/ArgumentsOfCorrectType';
// Spec Section: "Argument Optionality"
Expand Down Expand Up @@ -73,6 +75,7 @@ export var allRules = [
NoUnusedVariables,
KnownDirectives,
KnownArgumentNames,
UniqueArgumentNames,
ArgumentsOfCorrectType,
ProvidedNonNullArguments,
DefaultValuesOfCorrectType,
Expand Down
4 changes: 4 additions & 0 deletions src/validation/errors.js
Expand Up @@ -54,6 +54,10 @@ export function unknownDirectiveArgMessage(argName, directiveName) {
return `Unknown argument "${argName}" on directive "@${directiveName}".`;
}

export function duplicateArgMessage(argName) {
return `There can be only one argument named "${argName}".`;
}

export function unknownTypeMessage(type) {
return `Unknown type "${type}".`;
}
Expand Down
41 changes: 41 additions & 0 deletions src/validation/rules/UniqueArgumentNames.js
@@ -0,0 +1,41 @@
/* @flow */
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

import { GraphQLError } from '../../error';
import { duplicateArgMessage } from '../errors';


/**
* Unique argument names
*
* A GraphQL field or directive is only valid if all supplied arguments are
* uniquely named.
*/
export default function UniqueArgumentNames(): any {
var knownArgNames = Object.create(null);
return {
Field() {
knownArgNames = Object.create(null);
},
Directive() {
knownArgNames = Object.create(null);
},
Argument(node) {
var argName = node.name.value;
if (knownArgNames[argName]) {
return new GraphQLError(
duplicateArgMessage(argName),
[knownArgNames[argName], node.name]
);
}
knownArgNames[argName] = node.name;
}
};
}

0 comments on commit e7f7fee

Please sign in to comment.