restdocs-api-spec icon indicating copy to clipboard operation
restdocs-api-spec copied to clipboard

Improve failure output to make debugging /failure fixing in tests simpler

Open de-jcup opened this issue 5 years ago • 5 comments

Origin situation

I accidently defined a wrong type in my test (I used String.class instead of JsonFieldType.STRING) which led to a resources.json containing:

, {
      "attributes" : { },
      "description" : "my text...",
      "ignored" : false,
      "path" : "content[].metaData.xyz",
      "type" : "java.lang.String",
      "optional" : true
    }, {

which leads to gradle failure with:

 Task openapi3 FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task 'openapi3'.
> unknown field type java.lang.string

That was all output.

Of course this is my fault, but to find the problem wasn't easy - at the beginning I did not recognize that the resource.json output inside build/generated-snippets/myTargetFolder is full used to generate openapi parts and there you can find the location of the problem etc. etc.

Wanted

It would be nice to have

  • the location of the problematic resource.json or
  • the problematic test case, or
  • the path/attributes or
  • anything else that makes it simpler to locate the problematic part inside the gradle output in failure case.

de-jcup avatar Sep 22 '20 16:09 de-jcup

Hey @de-jcup ,

thanks for the report and sorry that this caused you trouble!

Yeah, it's an unfortunate "user" (developer) error. By design (using restdocs field descriptors to generate the raw format and using different gradle plugins to generate the respective target formats) there are some constraints for a solution:

  • Apparently, the restdocs FieldDescriptor doesn't have a problem with an arbitrary Java type. We value restdocs interface, so a test failure there wouldn't be appropriate.
  • Thus validation would need to happen in the respective gradle plugins. It already correctly fails, but the error message wasn't helpful enough
  • To include test/source line info in the gradle plugin error, we'd need to use reflection/debug symbols to store this information in the resource.json snippet

Imo, the last point would be overkill. I'd leave this open for further discussion/suggestions, but will eventually close it as "won't do" if no one steps up.

ozscheyge avatar Oct 02 '20 10:10 ozscheyge

Hello @ozscheyge ,

To include test/source line info in the gradle plugin error, we'd need to use reflection/debug symbols to store this information in the resource.json snippet Imo, the last point would be overkill.

I absolutely agree... this would be too much effort - at least for the error handling

Suggestions

Simple show file path

Scope: after resources.json has been generated and users do generate output from it (e.g. openapi3.yaml) - so inside processing existing 'resource.json' files

I haven't looked at the sources exactly and I have no real experience with Kotlin at the moment - but normally the currently fetched resources.json file path should be available while doing generation of output. So when just remembering last read resources.json file and showing the full path in case of error should be possible without much effort?

Show related JSON object as output / hint

Scope: after resources.json has been generated and users do generate output from it (e.g. openapi3.yaml) - so inside processing existing 'resource.json' files

In error case show complete JSON elements from current JSON object. (in my example before this would be

{
      "attributes" : { },
      "description" : "my text...",
      "ignored" : false,
      "path" : "content[].metaData.xyz",
      "type" : "java.lang.String",
      "optional" : true
}

So its more clear for users what resource part is the problem.

Together with the existing error message (+ maybe path information) it should be very easy to locate problematic part. This should be possible without additional debug meta data or reflection usage.

de-jcup avatar Oct 03 '20 19:10 de-jcup

I'm running into a similar problem right now, but I can't seem to figure out where the issue is orignating from:

Caused by: java.lang.IllegalArgumentException: unknown field type false
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator$FieldDescriptorWithSchemaType.typeToSchema(JsonSchemaFromFieldDescriptorsGenerator.kt:251)
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator$FieldDescriptorWithSchemaType.jsonSchemaType(JsonSchemaFromFieldDescriptorsGenerator.kt:206)
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator.handleEndOfPath(JsonSchemaFromFieldDescriptorsGenerator.kt:170)
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator.access$handleEndOfPath(JsonSchemaFromFieldDescriptorsGenerator.kt:26)
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator$traverse$1$1.apply(JsonSchemaFromFieldDescriptorsGenerator.kt:91)
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator$traverse$1$1.apply(JsonSchemaFromFieldDescriptorsGenerator.kt:26)
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator$traverse$1.accept(JsonSchemaFromFieldDescriptorsGenerator.kt:89)
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator$traverse$1.accept(JsonSchemaFromFieldDescriptorsGenerator.kt:26)
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator.traverse(JsonSchemaFromFieldDescriptorsGenerator.kt:83)
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator.generateSchema(JsonSchemaFromFieldDescriptorsGenerator.kt:32)
	at com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator.generateSchema$default(JsonSchemaFromFieldDescriptorsGenerator.kt:28)
	at com.epages.restdocs.apispec.openapi2.OpenApi20Generator.responseModel2Response(OpenApi20Generator.kt:480)
	at com.epages.restdocs.apispec.openapi2.OpenApi20Generator.resourceModels2Operation(OpenApi20Generator.kt:312)
	at com.epages.restdocs.apispec.openapi2.OpenApi20Generator.resourceModels2Path(OpenApi20Generator.kt:225)
	at com.epages.restdocs.apispec.openapi2.OpenApi20Generator.generatePaths(OpenApi20Generator.kt:182)
	at com.epages.restdocs.apispec.openapi2.OpenApi20Generator.generate$restdocs_api_spec_openapi_generator(OpenApi20Generator.kt:69)
	at com.epages.restdocs.apispec.openapi2.OpenApi20Generator.generateAndSerialize(OpenApi20Generator.kt:95)
	at com.epages.restdocs.apispec.gradle.OpenApiTask.generateSpecification(OpenApiTask.kt:28)
	at com.epages.restdocs.apispec.gradle.ApiSpecTask.aggregateResourceModels(ApiSpecTask.kt:49)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:48)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:41)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:28)
	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:704)
	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:671)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$2.run(ExecuteActionsTaskExecuter.java:284)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:301)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:293)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:273)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:258)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$200(ExecuteActionsTaskExecuter.java:67)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:145)
	at org.gradle.internal.execution.impl.steps.ExecuteStep.execute(ExecuteStep.java:49)
	at org.gradle.internal.execution.impl.steps.CancelExecutionStep.execute(CancelExecutionStep.java:34)
	at org.gradle.internal.execution.impl.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:69)
	at org.gradle.internal.execution.impl.steps.TimeoutStep.execute(TimeoutStep.java:49)
	at org.gradle.internal.execution.impl.steps.CatchExceptionStep.execute(CatchExceptionStep.java:33)
	at org.gradle.internal.execution.impl.steps.CreateOutputsStep.execute(CreateOutputsStep.java:50)
	at org.gradle.internal.execution.impl.steps.SnapshotOutputStep.execute(SnapshotOutputStep.java:43)
	at org.gradle.internal.execution.impl.steps.SnapshotOutputStep.execute(SnapshotOutputStep.java:29)
	at org.gradle.internal.execution.impl.steps.CacheStep.executeWithoutCache(CacheStep.java:134)
	at org.gradle.internal.execution.impl.steps.CacheStep.lambda$execute$3(CacheStep.java:83)
	at org.gradle.internal.execution.impl.steps.CacheStep.execute(CacheStep.java:82)
	at org.gradle.internal.execution.impl.steps.CacheStep.execute(CacheStep.java:36)
	at org.gradle.internal.execution.impl.steps.PrepareCachingStep.execute(PrepareCachingStep.java:33)
	at org.gradle.internal.execution.impl.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:38)
	at org.gradle.internal.execution.impl.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:23)
	at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:96)
	at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:89)
	at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:52)
	at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:36)
	at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:34)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:91)
	... 32 more

Any ideas on how I can track this down.

virtualdogbert avatar Nov 03 '20 19:11 virtualdogbert

Hey @virtualdogbert ,

would you mind posting your resource.json of the snippet which might be affected? You'll find those in your project directory under

build/generated-snippets/{snippetName}/resource.json

If you have plenty of snippets, this grep might help:

grep --include resource.json -rn "type.*:.*false" build/

ozscheyge avatar Nov 03 '20 20:11 ozscheyge

I have a lot of snippets, been working hard, but your grep, made me realize why my find wasn't working and now I can track things down. It was a JsonFieldType.array instead of JsonFieldType.ARRAY

After fighting some merge conflicts on the branch this was an easy fix. Thanks.

virtualdogbert avatar Nov 03 '20 21:11 virtualdogbert