Skip to content

Commit 5665408

Browse files
committed
Refactor
1 parent 4062cfb commit 5665408

22 files changed

+455
-315
lines changed

README.md

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,41 +32,42 @@ analyzer:
3232
- description: "constraint directive exists on the input field"
3333
directive: constraint
3434
kind: ['INPUT_OBJECT']
35+
field_parent_type: ['.+']
3536
field_type: ['^\[?Int\]?$', '^\[?Float\]?$', '^\[?String\]?$', '^\[?Decimal\]?$', '^\[?URL\]?$']
36-
ignore_field: ['^first$', '^last$', '^after$', '^before$']
37-
report_format: "%s.%s has no constraint directive"
37+
exclude_field: ['^first$', '^last$', '^after$', '^before$']
38+
report_format: "%s has no constraint directive"
3839
argument:
3940
- description: "constraint directive exists on the object field argument"
4041
directive: constraint
4142
kind: ['OBJECT']
4243
argument_type: ['^\[?Int\]?$', '^\[?Float\]?$', '^\[?String\]?$', '^\[?Decimal\]?$', '^\[?URL\]?$']
43-
ignore_argument: ['^first$', '^last$', '^after$', '^before$']
44-
report_format: "argument %s of %s has no constraint directive"
45-
44+
exclude_argument: ['^first$', '^last$', '^after$', '^before$']
45+
report_format: "argument %s has no constraint directive"
4646
```
4747

48-
- sample2: permission directive exists on the type
48+
- sample2: permission directive exists on the definition
4949

5050
```yaml
5151
---
5252
analyzer:
5353
- analyzer_name: "permission directive"
54-
description: "permission directive exists on the type"
55-
type:
56-
- description: "permission directive exists on the type"
54+
description: "permission directive exists on the definition"
55+
definition:
56+
- description: "permission directive exists on the definition"
5757
directive: permission
5858
kind: ['OBJECT', 'INTERFACE']
59-
type: ['.+']
60-
ignore_type: [ '^Query$', '^Mutation$', '^Subscription$', '^PageInfo$']
59+
definition: ['.+']
60+
exclude_definition: [ '^Query$', '^Mutation$', '^Subscription$', '^PageInfo$']
6161
report_format: "%s has no permission directive"
6262
field:
6363
- description: "permission directive exists on the mutation"
6464
directive: permission
6565
kind: ['OBJECT']
6666
field_parent_type: ['^Mutation$']
6767
field_type: ['.+']
68-
ignore_field:
69-
report_format: "%s.%s has no permission directive"
68+
exclude_field:
69+
report_format: "%s has no permission directive"
70+
7071
```
7172

7273
The `directive` command has a flag, `schema` which will be parsed and analyzed by directive's Analyzer.

analyzer.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package directive
2+
3+
import (
4+
"github.com/gqlgo/gqlanalysis"
5+
)
6+
7+
func NewAnalyzers(config *Config) []*gqlanalysis.Analyzer {
8+
analyzers := make([]*gqlanalysis.Analyzer, 0, len(config.Analyzer))
9+
for _, analyzerConfig := range config.Analyzer {
10+
analyzers = append(analyzers, NewAnalyzer(analyzerConfig))
11+
}
12+
return analyzers
13+
}
14+
15+
func NewAnalyzer(analyzerConfig *AnalyzerConfig) *gqlanalysis.Analyzer {
16+
return &gqlanalysis.Analyzer{
17+
Name: analyzerConfig.AnalyzerName,
18+
Doc: analyzerConfig.Description,
19+
Run: MergeAnalyzers(analyzerConfig),
20+
}
21+
}
22+
23+
func MergeAnalyzers(analyzerConfig *AnalyzerConfig) func(pass *gqlanalysis.Pass) (any, error) {
24+
var analyzers []*gqlanalysis.Analyzer
25+
for _, c := range analyzerConfig.DefinitionConfig {
26+
analyzer := DefinitionAnalyzer(c)
27+
analyzers = append(analyzers, analyzer)
28+
}
29+
for _, c := range analyzerConfig.FieldConfig {
30+
analyzer := FieldAnalyzer(c)
31+
analyzers = append(analyzers, analyzer)
32+
}
33+
for _, c := range analyzerConfig.ArgumentConfig {
34+
analyzer := ArgumentAnalyzer(c)
35+
analyzers = append(analyzers, analyzer)
36+
}
37+
return func(pass *gqlanalysis.Pass) (any, error) {
38+
for _, analyzer := range analyzers {
39+
if _, err := analyzer.Run(pass); err != nil {
40+
return nil, err
41+
}
42+
}
43+
return nil, nil
44+
}
45+
}
46+
47+
func DefinitionAnalyzer(config *DefinitionConfig) *gqlanalysis.Analyzer {
48+
return &gqlanalysis.Analyzer{
49+
Name: config.Directive,
50+
Doc: config.Description,
51+
Run: func(pass *gqlanalysis.Pass) (any, error) {
52+
definitions := NewDefinitionsByMap(pass.Schema.Types).
53+
NotBuildIn().
54+
FilterByKinds(config.Kinds).
55+
ExcludeByDefinitionName(config.ExcludeDefinitionPatterns).
56+
FilterByNotHasDirective(config.Directive).
57+
FilterByPositionNotNil()
58+
59+
for _, definition := range definitions {
60+
pass.Reportf(definition.Position, config.ReportFormat, definition.Name)
61+
}
62+
63+
return nil, nil
64+
},
65+
}
66+
}
67+
68+
func FieldAnalyzer(config *FieldConfig) *gqlanalysis.Analyzer {
69+
return &gqlanalysis.Analyzer{
70+
Name: config.Directive,
71+
Doc: config.Description,
72+
Run: func(pass *gqlanalysis.Pass) (any, error) {
73+
definitions := NewDefinitionsByMap(pass.Schema.Types).
74+
NotBuildIn().
75+
FilterByKinds(config.Kinds).
76+
FilterByDefinitionName(config.FieldParentTypePatterns)
77+
fields := definitions.Fields().
78+
FilterByFieldType(config.FieldTypePatterns).
79+
ExcludeByField(config.ExcludeFieldPatterns).
80+
FilterByNotHasDirective(config.Directive).
81+
FilterByPositionNotNil()
82+
83+
for _, field := range fields {
84+
pass.Reportf(field.Position, config.ReportFormat, field.Name)
85+
}
86+
87+
return nil, nil
88+
},
89+
}
90+
}
91+
92+
func ArgumentAnalyzer(config *ArgumentConfig) *gqlanalysis.Analyzer {
93+
return &gqlanalysis.Analyzer{
94+
Name: config.Directive,
95+
Doc: config.Description,
96+
Run: func(pass *gqlanalysis.Pass) (any, error) {
97+
definitions := NewDefinitionsByMap(pass.Schema.Types).NotBuildIn().FilterByKinds(config.Kinds)
98+
fields := definitions.Fields().FilterByNotNil()
99+
arguments := fields.Arguments().
100+
FilterByArgumentType(config.ArgumentTypePatterns).
101+
ExcludeByArgumentName(config.ExcludeArgumentPatterns).
102+
FilterByNotHasDirective(config.Directive).
103+
FilterByPositionNotNil()
104+
105+
for _, argument := range arguments {
106+
pass.Reportf(argument.Position, config.ReportFormat, argument.Name)
107+
}
108+
return nil, nil
109+
},
110+
}
111+
}
File renamed without changes.

argument.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package directive
2+
3+
import (
4+
"github.com/vektah/gqlparser/v2/ast"
5+
"slices"
6+
)
7+
8+
type ArgumentDefinitions ast.ArgumentDefinitionList
9+
10+
func (as ArgumentDefinitions) FilterByArgumentType(argumentTypePatterns []string) ArgumentDefinitions {
11+
var arguments ArgumentDefinitions
12+
for _, argument := range as {
13+
if slices.ContainsFunc(argumentTypePatterns, func(pattern string) bool { return isType(pattern, argument.Type) }) {
14+
arguments = append(arguments, argument)
15+
}
16+
}
17+
18+
return arguments
19+
}
20+
21+
func (as ArgumentDefinitions) ExcludeByArgumentName(excludeArgumentPatterns []string) ArgumentDefinitions {
22+
var arguments ArgumentDefinitions
23+
for _, argument := range as {
24+
if !slices.ContainsFunc(excludeArgumentPatterns, func(pattern string) bool { return isMatch(pattern, argument.Name) }) {
25+
arguments = append(arguments, argument)
26+
}
27+
}
28+
return arguments
29+
}
30+
31+
func (as ArgumentDefinitions) FilterByNotHasDirective(directive string) ArgumentDefinitions {
32+
var arguments ArgumentDefinitions
33+
for _, argument := range as {
34+
if !findDirectiveOnArgument(argument, directive) {
35+
arguments = append(arguments, argument)
36+
}
37+
}
38+
return arguments
39+
}
40+
41+
func (as ArgumentDefinitions) FilterByPositionNotNil() ArgumentDefinitions {
42+
var arguments ArgumentDefinitions
43+
for _, argument := range as {
44+
if argument.Position != nil {
45+
arguments = append(arguments, argument)
46+
}
47+
}
48+
return arguments
49+
}
50+
51+
func findDirectiveOnArgument(argument *ast.ArgumentDefinition, directiveName string) bool {
52+
return argument.Directives.ForName(directiveName) != nil
53+
}

common.go

Lines changed: 1 addition & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,9 @@
11
package directive
22

33
import (
4-
"fmt"
5-
"github.com/vektah/gqlparser/v2/ast"
64
"regexp"
7-
"slices"
85
)
96

10-
func targetTypeKind(types map[string]*ast.Definition, kinds []ast.DefinitionKind) []*ast.Definition {
11-
var targets []*ast.Definition
12-
for _, t := range types {
13-
if !t.BuiltIn && slices.Contains(kinds, t.Kind) {
14-
targets = append(targets, t)
15-
}
16-
}
17-
return targets
18-
}
19-
20-
func targetTypes(types ast.DefinitionList, fieldParentTypePatterns []string) ast.DefinitionList {
21-
var targets ast.DefinitionList
22-
for _, t := range types {
23-
if slices.ContainsFunc(fieldParentTypePatterns, func(pattern string) bool { return isNameMatch(pattern, t.Name) }) {
24-
targets = append(targets, t)
25-
}
26-
}
27-
return targets
28-
}
29-
30-
func targetFieldType(fields ast.FieldList, fieldTypePatterns []string) ast.FieldList {
31-
var targets ast.FieldList
32-
for _, field := range fields {
33-
if field != nil && field.Type != nil {
34-
if slices.ContainsFunc(fieldTypePatterns, func(pattern string) bool { return isType(pattern, field.Type) }) {
35-
targets = append(targets, field)
36-
}
37-
}
38-
}
39-
return targets
40-
}
41-
42-
func excludeTargetFieldTypeByTypeName(fields ast.FieldList, ignoreFiledNamePatterns []string) ast.FieldList {
43-
var targets ast.FieldList
44-
for _, field := range fields {
45-
if field != nil && field.Type != nil {
46-
if !slices.ContainsFunc(ignoreFiledNamePatterns, func(pattern string) bool { return isNameMatch(pattern, field.Name) }) {
47-
targets = append(targets, field)
48-
}
49-
}
50-
}
51-
return targets
52-
}
53-
54-
func targetFieldArgumentType(field *ast.FieldDefinition, fieldArgumentTypePatterns []string) ast.ArgumentDefinitionList {
55-
var targets ast.ArgumentDefinitionList
56-
if field != nil && field.Type != nil {
57-
for _, arg := range field.Arguments {
58-
if slices.ContainsFunc(fieldArgumentTypePatterns, func(pattern string) bool { return isType(pattern, arg.Type) }) {
59-
targets = append(targets, arg)
60-
}
61-
}
62-
}
63-
return targets
64-
}
65-
66-
func excludeTargetArgumentsByField(args ast.ArgumentDefinitionList, ignoreArgumentPatterns []string) ast.ArgumentDefinitionList {
67-
var targets ast.ArgumentDefinitionList
68-
for _, arg := range args {
69-
if !slices.ContainsFunc(ignoreArgumentPatterns, func(pattern string) bool { return isNameMatch(pattern, arg.Name) }) {
70-
targets = append(targets, arg)
71-
}
72-
}
73-
return targets
74-
}
75-
76-
func excludeTargetTypesByTypeName(types ast.DefinitionList, ignoreTypePatterns []string) ast.DefinitionList {
77-
var targets ast.DefinitionList
78-
for _, t := range types {
79-
if !slices.ContainsFunc(ignoreTypePatterns, func(pattern string) bool { return isNameMatch(pattern, t.Name) }) {
80-
targets = append(targets, t)
81-
}
82-
}
83-
return targets
84-
}
85-
86-
func findDirectiveOnDefinition(t *ast.Definition, directiveName string) bool {
87-
return t.Directives.ForName(directiveName) != nil
88-
}
89-
90-
func findDirectiveOnField(field *ast.FieldDefinition, directiveName string) bool {
91-
return field.Directives.ForName(directiveName) != nil
92-
}
93-
94-
func findDirectiveOnArg(arg *ast.ArgumentDefinition, directiveName string) bool {
95-
return arg.Directives.ForName(directiveName) != nil
96-
}
97-
98-
func isType(pattern string, t *ast.Type) bool {
99-
if t == nil {
100-
return false
101-
}
102-
return isNameMatch(pattern, typeName(t))
103-
}
104-
105-
func isNameMatch(pattern, name string) bool {
7+
func isMatch(pattern, name string) bool {
1068
return regexp.MustCompile(pattern).MatchString(name)
1079
}
108-
109-
func typeName(t *ast.Type) string {
110-
if isList(t) {
111-
return fmt.Sprintf("[%s]", t.Elem.NamedType)
112-
}
113-
return t.NamedType
114-
}
115-
116-
func isList(t *ast.Type) bool {
117-
return t.Elem != nil
118-
}

common_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func Test_isNameMatch(t *testing.T) {
105105
}
106106
for _, tt := range tests {
107107
t.Run(tt.name, func(t *testing.T) {
108-
if got := isNameMatch(tt.args.pattern, tt.args.typeName); got != tt.want {
108+
if got := isMatch(tt.args.pattern, tt.args.typeName); got != tt.want {
109109
t.Errorf("isTypeNameMatch() = %v, want %v", got, tt.want)
110110
}
111111
})

0 commit comments

Comments
 (0)