-
Notifications
You must be signed in to change notification settings - Fork 112
Allow schema.Reference to validate as normal field #100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
schema/reference.go
Outdated
return v.rl.ValidateID(v.Path, value) | ||
} | ||
|
||
// CompileReference validates v.Path against rl and stores rl for later use by v.Validate. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be just Compile in this implementation example. This is leftover code that was not updated.
schema/reference.go
Outdated
} | ||
|
||
// CompileReference validates v.Path against rl and stores rl for later use by v.Validate. | ||
func (v Reference) CompileReference(rl ResourceLookup) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method should be named Compile(rl ResourceLookup)
One of my big question with this design idea, is weather there will be more objects that various FieldValidator's (or Field's) Compile methods needs access to in the future... If no, then this design might be OK. |
TL;DR: I will complete this design to facilitate discussion. It's either this or passing context.Context to the Validate method (where the context would contain the same wrapped resource.Index that in effect implements ResourceLookup). Design discussion: I had been hoping from some comments by now, but I suppose my code was not complete enough to facilitate a full understanding of the intended design here; especially as I have not implemented the part that must wrap resource.Index into an implementation of the ResourceLookup interface before passing that in to the I have one thing that talks in favor of this desing atm., and that is that it makes sense to validate that the linked Reference resource exist at compile time. It can even help users avoid some bugs from reaching production. I mentioned on #57 that I have considered replacing the The Validate function however, could serve well as a method where I will complete the current design to facilitate discussion, but it's probably a viable alternative to consider going for a |
I am not to far away from pushing and update... almost got there in the weekend, just need to update some remaining tests... |
Sorry if I'm not very responsive. I'll wrap my head around the subject when you send the PR. |
No problem @rs, I have been very busy as well, hench the long delay in pushing any more code. |
@rs, There is still some tests that needs updating, but now at least the tests within the schema package pass. At least tests within the schema/encoding and resource packages needs updating. |
resource/resource.go
Outdated
// Compile the resource graph and report any error | ||
func (r *Resource) Compile() error { | ||
// Compile the resource graph and report any error. | ||
func (r *Resource) Compile(rl schema.ReferenceChecker) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sholud be rc for consistency. the rl name is leftover from when it was called ReferenceLookup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
UPDATE: jsonschema tests fixed, although I will add better support for JSON Schema encoding of Reference later now that we can actually access the reference validator after Compile is called. rest package tests are still failing though, with the following:
I am not quite sure why, but presumably it's somehow related to issues with the new index.Compile() ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great.
resource/index.go
Outdated
@@ -5,22 +5,22 @@ import ( | |||
"net/url" | |||
"strings" | |||
|
|||
"golang.org/x/net/context" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrong context
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
schema/reference.go
Outdated
if v := rc.ReferenceChecker(r.Path); v != nil { | ||
r.validator = v | ||
return nil | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extra empty line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
0665f2b
to
8a671f3
Compare
@rs, thanks for the review, and sorry for my slow update time on this. Pushing a minor update to resolve identified issues, but there is still one major bug that surface in the resource package that needs some debugging. Reproduce by running tests. If someone with some time on their hans want to try and debug it, feel welcome. Otherwise I will get to it eventually. |
58160d4
to
2af67f1
Compare
Sorry for the the delay in looking into this. I think most of the errors looks pretty reasonable, since the code generating the error texts / status codes we test against here, is part of what has been removed. There is however one failing test that is not expected, and that's this one: --- FAIL: TestHandlerPostListWithSubSchemaReference (0.00s)
Error Trace: method_post_test.go:370
Error: Not equal: 201 (expected)
!= 422 (actual)
Error Trace: method_post_test.go:371
Error: Object expected to be of type *resource.Item, but was *rest.Error |
2af67f1
to
48f25de
Compare
Right, so the 201 issues are solved by adding an explicit Compile to This leaves the remaining issues with unexpected status codes / error messages when POSTing documents due to changed semantics. These tests also lacks an explicit call to index.Compile(), so when correcting the tests, I think we can write them to be go 1.7 sub-tests, so that there is less code duplication. @rs, what's your opinion on changing the behaviour for "invalid reference" to all return a 422 (Unprocessable Entity), rather than different type of errors based on what's wrong with the Reference field. The reason that the status code is changing, is that invalid Reference fields now follow the same code pattern as other validation issues.
|
Yes I think it make sense. And for Go 1.7 sub-test, I'm all for it. |
I have set the sub-tests to run in Parallel. This appears to uncover a data race within The bug goes away if I remove the parallel execution of the sub-tests. I think the bug was just masked before because we didn't do to many parallel tests... |
It's these lines: func (f DefaultResponseFormatter) FormatError(ctx context.Context, headers http.Header, err error, skipBody bool) (context.Context, interface{}) {
oldLogger := resource.Logger
resource.Logger = nil
defer func() { resource.Logger = oldLogger }() It think this is pretty ugly:-/ If we really need to do this, we need to use a mutex. What's the benfit of silencing the logger? @rs, do you want to fix it in a separate commit? This PR should be otherwise good to go in now :-) |
I did that in some tests to remove error output while testing error cases? Did I do that in actual code?? |
Well, I didn't run a git blame on it yet, but someone did... if it's by mistake I can add a separate commit to remove it in this PR so that the tests pass. And we can of course silence the loggers in test code (or re-route it to write to testing.T). |
The Git-blame btw: |
@@ -25,8 +25,7 @@ func (v AnyOf) Compile(rc ReferenceChecker) error { | |||
// Validate ensures that at least one sub-validator validates. | |||
func (v AnyOf) Validate(value interface{}) (interface{}, error) { | |||
for _, validator := range v { | |||
var err error | |||
if value, err = validator.Validate(value); err == nil { | |||
if value, err := validator.Validate(value); err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The initial problem here is that we where overwriting the value
functional parameter, so that the next iteration of the for-loop would use the value returned by the previous (failing) validator, which in most cases would be nil due to the returned error.
With this fix, we generate a new (shadowing) value variable for each iteration. This variable is not passed on to the next iteration.
I will rebase one more time to put the bug-fixes in front of the feature, leaving all three commits with passing tests. |
DataResponseFormatter was modifying the global response Logger instance in a non-threadsafe way, presumably to supress logging during testing. This code has now been removed.
@rs, ready for final review. The two commits before the feature are (unrelated) bugfixes that where needed for the (updated) tests to pass, hench I recommend a rebase and merge so that they end up as separate commits on master :-) Note that the commit ordering showed on the PR is a bit weird... It's the one with the green tick that's last. The git log on the branch is correct. |
} | ||
return nil | ||
|
||
_, err = rsc.Get(context.TODO(), id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why TODO
over Background
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the docs on context.TODO()
:
Code should use context.TODO when it's unclear which Context to use or it is not yet available (because the surrounding function has not yet been extended to accept a Context parameter).
From the docs on context.Background()
:
It is typically used by the main function, initialization, and tests, and as the top-level Context for incoming requests.
I feel this case is closer to the description offered for context.TODO()
than what's offered by context.Background()
. I think if we where to use the right context here, we should get it passed in from somewhere so that when using the rest
package, we use the context created for a new request, and not a new context created later.
} | ||
|
||
// Validate ensures that at least one sub-validator validates. | ||
func (v AnyOf) Validate(value interface{}) (interface{}, error) { | ||
for _, validator := range v { | ||
var err error | ||
if value, err = validator.Validate(value); err == nil { | ||
if value, err := validator.Validate(value); err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch.
schema/field.go
Outdated
if err := c.Compile(rc); err != nil { | ||
return fmt.Errorf(": %v", err) | ||
} | ||
} else if c, ok := f.Validator.(Compiler); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be simpler to only have the ReferenceCompiler
interface and remove the Compiler
interface altogether? It is up to the type to use the rc
or not.
Having both interfaces available is not clear on what happen if a given type implement both interfaces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not possible for one type to implement both interfaces since method overloading is not legal in Go, and the methods have the same name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you are right. But having a single interface would be simpler and make sure we don’t forget to always check for both interfaces everywhere it’s needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Compiler
interface is also used within the resource package, e.g. for the index.
I can check if it's feasible to move the Compiler
interface to the resource
package, and only use the new ReferenceCompiler
interface within the schema
package. It's an even bigger change, especially when it commes to updating tests, but should be mostly search/replace.
@rs, I have pushed rename schema.ReferenceCompiler -> Compiler as a new commit for easier review only. When you are happy with the PR, I would like to do a manual rebase to fold this, as well as any other requested changes into the feature commit :-) |
Allows for schema.Reference to be validated in the same way all other field validators are validated. To achive this, the resource.Index will pass on a new schema.ReferenceChecker implementation when calling Compile on each resource. To achive this, schema.Schema and each schema.FieldValidator implementation that may hold a reference, has been changed to implement a new interface schema.ReferenceCompiler in place of the schema.Compiler interface. This is a breaking change to the public API, but simple programs that don't rely on calling Compile on Schema or Resource, are likely not affected.
ab343d9
to
07df663
Compare
rebased (squashing the two last commits). |
\o/ |
Solves issue #57 by allowing schema.Reference to be validated as a normal field. See commit messages for more details!