Hotfix vor dem Krankenhaus oder „Hey Swagger! Wo sind meine Fehler? "

Haben Sie sich während des Hotfixes jemals an den Master angepasst? Nein?! Aber es ist mir gelungen!

In dieser Geschichte geht es darum, wie ich vergessen habe, die Dokumentation zu aktualisieren. Als Ergebnis habe ich ein Plugin für Swagger geschrieben (das zweite Mal). Und wie ich mitgerissen wurde, so dass ich meinen Krankenstand vergaß und mich besserte!


Und ein bisschen mehr über Optional nicht von Java 8.

Wir verwenden Swagger , um interaktive Dokumentation zu erstellen.

Wenn Sie eine Methode in der API erstellen, gilt Folgendes:

0. Fügen Sie die erforderlichen Anmerkungen hinzu, z. B. @RequestMapping usw.

1. Fügen Sie @ErrorCodes (unsere eigene Anmerkung) hinzu und listen Sie die Zeichenfolgenfehlercodes auf, die diese Methode in der Antwort zurückgeben kann.

2. Fügen Sie @ApiOperation hinzu und duplizieren Sie Informationen zu diesen Fehlern im Feld Notizen.

3. Fügen Sie den Rest der Anmerkungen hinzu ...

Es sah ungefähr so ​​aus (unnötig entfernt und vereinfacht entfernt):

@ApiOperation( value = "Some description.", notes = "List of possible error codes:" + "<ul>" + " <li>sms.verification.code.fail</li>" + "</ul>") @PostMapping("/security/confirmation/check") @ErrorCodes(values = {"sms.verification.code.fail"}) public ResponseDto check(@ApiParam @RequestBody @Valid RequestDto request) {... } 

Punkt 2 war die Ursache für meinen Fehler, als ich @ErrorCodes hinzufügte, aber vergaß, die Zeichenfolgenfehlercodes in @ApiOperation aufzulisten. Zufrieden mit mir selbst, aber mit einem leichten Gefühl der Angst, gab ich meine Pull-Anfrage an Code Review. Und hier haben sie mir mitgeteilt, dass ich Notizen vergessen habe! Sie erklärten auch, dass Swagger keine Informationen von @ErrorCodes abruft und Sie sie daher manuell registrieren müssen. An diesem Abend endete alles glücklich. Korrigierte seinen Defekt und wurde krankgeschrieben.

Vielleicht wäre es normal, weiterzumachen. Setzen Sie ein Häkchen für andere auf das Regal, genauso wie sie sagen: Max, seien Sie vorsichtig, achten Sie darauf ...

Aber es ist mir nicht gelungen. Den ganzen Abend und den nächsten Morgen haben wir Swagger beigebracht, unsere Anmerkungen zu lesen und dieselben Codes unabhängig voneinander in Notizen einzufügen.

Schritt 1. Jemand war hier ...


Bei einer flüchtigen Suche konnte ich herausfinden, dass bereits jemand versucht hatte , Swagger mit seiner Anmerkung zu befreunden. Es gab auch einen Link zur SpringFox-Dokumentation, der besagte, dass Sie ein Plugin schreiben können!

Glückliches Glück hat mich so sehr bedeckt, dass ich sogar die Erkältung und den Krankenstand vergessen habe! In meinem zukünftigen Artikel „Wie man das Unternehmen nicht dreimal verlässt“ erzähle ich drei Geschichten über die Rettung von Ertrinkenden. Eine davon handelt davon, wie ich es geschafft habe, ein Plugin für Chrome + Firefox zu schreiben, das die Arbeit mit Jenkins mehrmals beschleunigte. Es hat so viel Spaß gemacht, es zu schreiben! Immerhin ist dies ein Mikroprojekt! Mein sehr, sehr einfaches Startup, aber mit echten Leuten, die es benutzen. Dann stieg ich wieder aus der Routine aus und fand Inspiration. Burnout ist weg. Aber ich werde in einem zukünftigen Artikel besser darüber sprechen. In der Zwischenzeit zurück zum Plugin für Swagger.

Aktion 2. Es funktioniert!


Es erwies sich als einfach, etwas zu schreiben, das funktionierte. Ich habe ein Beispiel für ein Plugin aus der offiziellen SpringFox-Dokumentation genommen, alles Unnötige entfernt und das richtige hinzugefügt.

Plugin. Version 1
 @Component @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1) public class SwaggerErrorCodesConfiguration implements OperationBuilderPlugin { /** * Appends note of operation. Adds error codes to note of operation. * <code> * <h3>List of possible errors:</h3> * <ul> * <li>error.code.1</li> * <li>error.code.2</li> * <li>error.code.3</li> * </ul> * </code> * @param context operation context */ @Override public void apply(OperationContext context) { Method operationMethod = context.getHandlerMethod().getMethod(); // Check method is annotated by @ErrorCodes ErrorCodes errorCodes = findAnnotation(operationMethod, ErrorCodes.class); if (errorCodes == null) { return; } StringBuilder errorCodesNote = new StringBuilder(); errorCodesNote.append("<h3>List of possible errors:</h3>"); errorCodesNote.append("<ul>"); for (String errorCode: errorCodes.values()) { errorCodesNote.append("<li>").append(errorCode).append("</li>"); } errorCodesNote.append("</ul>"); // Write new version of notes. context.operationBuilder().notes(errorCodesNote.toString()).build(); } @Override public boolean supports(DocumentationType delimiter) { return SwaggerPluginSupport.pluginDoesApply(delimiter); } } 


Ich habe mit einer Methode begonnen, die keinen Wert für Notizen in @ApiOperation angibt.

 @ApiOperation(value = "Some description.") @PostMapping("/security/confirmation/check") @ErrorCodes(values = {"sms.verification.code.fail"}) public ResponseDto check(@ApiParam @RequestBody @Valid RequestDto request) { ... } 

Starten und ... Das Ergebnis! Hurra, es funktioniert! Der Zeichenfolgencode sms.verification.code.fail wurde in Notizen angezeigt !



Schritt 3. Es funktioniert, aber es funktioniert nicht.


Dann habe ich ein paar Wörter zu Notizen hinzugefügt und diesen Code erhalten:

 @ApiOperation(value = "Some description.", notes = "Some initial note.") @PostMapping("/security/confirmation/check") @ErrorCodes(values = {"sms.verification.code.fail"}) public ResponseDto check(@ApiParam @RequestBody @Valid RequestDto request) { ... } 

Wieder gestartet. Das Ergebnis war ... unerwartet. Das SpringFox-Plugin überschreibt den Notenwert beim Generieren der Dokumentation (O_o)!

Ich schaue mir an, wie context.operationBuilder (). Notes (String) funktioniert, und sehe dort Folgendes:

 public OperationBuilder notes(String notes) { this.notes = (String)BuilderDefaults.defaultIfAbsent(notes, this.notes); return this; } 

Ähm ... Ok, dann nehmen wir den aktuellen Wert der Notizen und fügen Fehlercodes hinzu. Es bleibt, die Annotation @ApiOperation zu erhalten, den gewünschten Wert zu verwenden und zu dem hinzuzufügen, was ich selbst bilde.

Also die endgültige Version ( verfügbar unter gist.github.com )

Plugin. Version 2
 @Component @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1) public class SwaggerErrorCodesConfiguration implements OperationBuilderPlugin { /** * Appends note of operation. Adds error codes to note of operation. * <code> * <h3>List of possible errors:</h3> * <ul> * <li>error.code.1</li> * <li>error.code.2</li> * <li>error.code.3</li> * </ul> * </code> * @param context operation context */ @Override public void apply(OperationContext context) { Method operationMethod = context.getHandlerMethod().getMethod(); // Check method is annotated by @ApiOperation ApiOperation apiOperation = findApiOperationAnnotation(operationMethod).orNull(); if (apiOperation == null) { return; } // Check method is annotated by @ErrorCodes ErrorCodes errorCodes = findAnnotation(operationMethod, ErrorCodes.class); if (errorCodes == null) { return; } // Prepend notes by using current value of notes in @ApiOperation StringBuilder errorCodesNote = new StringBuilder(apiOperation.notes()); errorCodesNote.append("<h3>List of possible errors:</h3>"); errorCodesNote.append("<ul>"); for (String errorCode: errorCodes.values()) { errorCodesNote.append("<li>").append(errorCode).append("</li>"); } errorCodesNote.append("</ul>"); // Write new version of notes. context.operationBuilder().notes(errorCodesNote.toString()).build(); } @Override public boolean supports(DocumentationType delimiter) { return SwaggerPluginSupport.pluginDoesApply(delimiter); } } 


Jetzt stellte sich heraus, wie es sollte!



Schritt 4. Was ist mit Optional nicht von Java 8?


Zu Beginn der Arbeit am Plugin konnte ich nicht verstehen, was mit dem Optional falsch war, das bei der Suche nach Anmerkungen zurückgegeben wird. Diese Klasse verfügte nicht über die Standardmethoden, die für die Arbeit mit java.util.Optional verwendet werden . Beispielsweise gibt es keine ifPresent- Methode, aber eine orNull- Methode.
Es stellte sich heraus, dass SpringFox Optional von Guava verwendet .

TL; DR


Ich habe ein Plugin für SpringFox geschrieben, eine Spring-Komponente, die in der Phase der Dokumentationsgenerierung aufgerufen wird, um Werte aus unserer @ ErrorCodes-Annotation zu lesen.

Plugin-Code
 @Component @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1) public class SwaggerErrorCodesConfiguration implements OperationBuilderPlugin { /** * Appends note of operation. Adds error codes to note of operation. * <code> * <h3>List of possible errors:</h3> * <ul> * <li>error.code.1</li> * <li>error.code.2</li> * <li>error.code.3</li> * </ul> * </code> * @param context operation context */ @Override public void apply(OperationContext context) { Method operationMethod = context.getHandlerMethod().getMethod(); // Check method is annotated by @ApiOperation ApiOperation apiOperation = findApiOperationAnnotation(operationMethod).orNull(); if (apiOperation == null) { return; } // Check method is annotated by @ErrorCodes ErrorCodes errorCodes = findAnnotation(operationMethod, ErrorCodes.class); if (errorCodes == null) { return; } // Prepend notes by using current value of notes in @ApiOperation StringBuilder errorCodesNote = new StringBuilder(apiOperation.notes()); errorCodesNote.append("<h3>List of possible errors:</h3>"); errorCodesNote.append("<ul>"); for (String errorCode: errorCodes.values()) { errorCodesNote.append("<li>").append(errorCode).append("</li>"); } errorCodesNote.append("</ul>"); // Write new version of notes. context.operationBuilder().notes(errorCodesNote.toString()).build(); } @Override public boolean supports(DocumentationType delimiter) { return SwaggerPluginSupport.pluginDoesApply(delimiter); } } 

Source: https://habr.com/ru/post/de420841/


All Articles