-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/Automatisation de la génération de nouveaux schémas #206
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
Merged
romainfd
merged 128 commits into
auto/model_tracker
from
feature/mdd/automatic-schema-generation
Dec 16, 2024
Merged
Changes from 119 commits
Commits
Show all changes
128 commits
Select commit
Hold shift + click to select a range
bf807bb
feat/java: modify the deserialization method from deduction to a cust…
599ae75
feat/java: update utils to properly add model type to the envelope wh…
4af1213
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 153110a
feat/java: replace temporarily added other.model property with descri…
c4e7b8c
feat/java: update error message when model name isn't present in the …
353333d
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 16388dc
feat/deserializer: fix deserialization with keyword property
41e2464
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 004bbbd
feat/deserializer: fix deserialization with keyword property
eec16af
⚙️ Auto-génération des classes et des specs
saveliy-sviridov b66a67e
feat/model: fix messagesList.json syntax
2cedf06
feat/model: fix test cases, validation error generation and edxl-de xsd
0cb7cb4
feat/auto: modify workflow to read schema data directly from the xlsx…
28975be
feat/auto: add templates for schemas and update the script
b759fb7
feat/auto: update workflow to extract information pertaining to vario…
bb907d7
feat/parser: update github action
25dcab4
Merge remote-tracking branch 'origin/auto/model_tracker' into feature…
845e435
feat/parser: fix workfow
d53b201
feat/auto: fix gha
918e428
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 016438f
feat/auto: temporarily trigger schema auto generation on pr
dc828c9
Merge remote-tracking branch 'origin/feature/mdd/automatic-schema-gen…
d28efc4
feat/auto: fix gha
73f2170
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 870e871
feat/auto: fix gha
a8712f1
Merge remote-tracking branch 'origin/feature/mdd/automatic-schema-gen…
22a8556
feat/auto: fix gha
1a145c1
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 68a8465
feat/auto: fix gha
57b54db
Merge remote-tracking branch 'origin/feature/mdd/automatic-schema-gen…
370214f
Merge remote-tracking branch 'origin/auto/model_tracker' into feature…
5fdffbd
⚙️ Auto-génération des classes et des specs
saveliy-sviridov be3ef8e
feat/parser: fix workfow
3e2200b
⚙️ Auto-génération des classes et des specs
saveliy-sviridov de4a4a4
feat/parser: fix workfow
d71c074
Merge remote-tracking branch 'origin/feature/mdd/automatic-schema-gen…
19c500a
feat/auto: fix model
b411e57
feat/auto: fix model
2761cb0
feat/auto: fix gha
9292e66
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov f09c00f
feat/auto: fix tmpl
67a1ed6
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov bfb74cc
feat/auto: fix tmpl
306d197
⚙️ Auto-génération des classes et des specs
saveliy-sviridov cc6459e
feat/auto: fix tmpl
b42c857
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov 4ef7264
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 4e97161
feat/auto: fix tmpl
4220bfd
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov 8667f24
feat/auto: fix tmpl
d0c2eb4
Merge remote-tracking branch 'origin/auto/model_tracker' into feature…
b01988e
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov 987c2a3
feat/auto: fix tmpl
d6d519d
Merge remote-tracking branch 'origin/feature/mdd/automatic-schema-gen…
a9c5355
⚙️ Auto-génération des classes et des specs
saveliy-sviridov a0827ad
feat/auto: update mdd and rc-de
4cf6bed
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov 8dca8f8
Merge branch 'auto/model_tracker' into feature/mdd/jackson-deduction-…
romainfd 6aad9b0
feat/auto: update xmlns management in schema, config and class genera…
b29a727
Merge remote-tracking branch 'origin/auto/model_tracker' into feature…
5dee9bb
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov a5c6e4e
⚙️ Auto-génération des classes et des specs
saveliy-sviridov d9c1245
Merge branch 'auto/model_tracker' into feature/mdd/automatic-schema-g…
romainfd 4e5b800
⚙️ Auto-génération des schemas et fichiers de configuration
romainfd 4c3151a
feat/auto: update template
a8aac0a
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov 7474bb5
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 0d23ee1
feat/auto: update xsds
f016558
Merge remote-tracking branch 'origin/feature/mdd/automatic-schema-gen…
c2fbdd1
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov 20c415a
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 124638c
feat/auto: update tmpl
6a9dfa1
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov 65b1581
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 5ca61ad
feat/auto: skip xsd generation for schemas with automaticGeneration s…
7961f5d
Merge remote-tracking branch 'origin/auto/model_tracker' into feature…
77f5357
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov 76dbb61
feat/auto: update non auto generated schemas
a8b2118
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov 4ef6b00
feat/auto: update xsd template to properly NOT add non-automatically …
41bf66e
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov 248043a
feat/auto: update templates and example files
4d13d2c
⚙️ Auto-génération des schemas et fichiers de configuration
saveliy-sviridov d875cac
feat/gha: remove rs-error.xsd deletion since its exclusion is handled…
saveliy-sviridov d4b6eba
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 81d37f4
feat/auto: update templates and example files
d0c5ba1
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 1ccadc6
feat/auto: update templates and example files
536f912
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 69b57ef
feat/tests: address pr comments
c930162
feat/tests: add further keyword property testing and adjust validator…
d28579a
Merge remote-tracking branch 'origin/auto/model_tracker' into feature…
cb0d88a
Merge remote-tracking branch 'origin/feature/mdd/jackson-deduction-re…
700bbab
feat/auto: add ContentMessageDeserializer template and merge generate…
598698f
feat/auto: make content message template cuter
b4779e7
⚙️ Auto-génération des classes et des specs
saveliy-sviridov e44d077
feat/auto: unmerge keyword-related changes
b8c7eff
Merge remote-tracking branch 'origin/auto/model_tracker' into feature…
37a0a6b
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 01fb544
Revert "feat/deserializer: fix deserialization with keyword property"
e9bb8bc
feat/auto: remove ContentMessageDeserializer.java and its generation
92fbfb2
feat/auto: remove ContentMessageDeserializer.java and its generation
afd9a30
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 4d3f383
feat/auto: add symlink
077d4f7
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 0d43a29
feat/auto: rename schema map, add custom message to ContentMessage.ja…
f797186
feat/auto: fix missing custom message declarations
8d2cf19
⚙️ Auto-génération des classes et des specs
saveliy-sviridov a0afe3a
feat/auto: split generator files between /auto and /manual folders an…
e88eca2
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 93b8a83
feat/auto: sort found config files before running generation
3f2064c
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 76cc770
feat/auto: rename rc-de template to avoid ordering issues
8e46ae5
⚙️ Auto-génération des classes et des specs
saveliy-sviridov b89f217
feat/auto: add a notice of information to the class generation step
fab0ef1
Merge remote-tracking branch 'origin/feature/mdd/automatic-schema-gen…
c121654
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 8df8215
feat/auto: fix comment position in content type xsd
514fb6c
⚙️ Auto-génération des classes et des specs
saveliy-sviridov cbcc918
feat/auto: rename /auto folder to /generated
c5c193d
Merge remote-tracking branch 'origin/feature/mdd/automatic-schema-gen…
eb24d19
feat/auto: remove unused env from gha
c341609
chore: specify order in README
romainfd 325a116
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 6110d05
feat/auto: remove old generated config files
326b753
⚙️ Auto-génération des classes et des specs
saveliy-sviridov 428f326
feat/auto: remove old generated classes and schemas (hopefully not to…
9f7bb89
⚙️ Auto-génération des classes et des specs
saveliy-sviridov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,6 +56,37 @@ jobs: | |
| if: steps.filter.outputs.parsing_required == 'true' | ||
| run: python workflow.py --stage parser_and_mv | ||
|
|
||
| - name: Run csv_parser to generate schemas.yaml | ||
| working-directory: ./csv_parser | ||
| if: steps.filter.outputs.parsing_required == 'true' | ||
| run: python workflow.py --stage output_schemas_yaml | ||
|
|
||
| - name: Collect schemas.yaml and copy it to json_schema2xsd | ||
| working-directory: ./csv_parser | ||
| if: steps.filter.outputs.parsing_required == 'true' | ||
| run: | | ||
| cp ./out/schemas.yaml ./json_schema2xsd/src/main/resources/schemas.yaml | ||
|
|
||
| - name: Setup gomplate | ||
| if: steps.filter.outputs.parsing_required == 'true' | ||
| uses: jason-dour/[email protected] | ||
| with: | ||
| gomplate-version: v4.2.0 | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Run automatic-schema-generator and move generated files to corresponding locations | ||
| if: steps.filter.outputs.parsing_required == 'true' | ||
| working-directory: ./automatic-schema-generator | ||
| run: | | ||
| rm -r output || true | ||
| chmod +x ./automatic-generator.sh | ||
| ./automatic-generator.sh | ||
| rsync -a --remove-source-files output/generator .. | ||
| rsync -a --remove-source-files output/edxl ../src/main/java/com/hubsante/model | ||
| rsync -a --remove-source-files output/json-schema ../src/main/resources | ||
| rsync -a --remove-source-files output/xsd ../src/main/resources | ||
|
|
||
| # TODO: Reactivate test case autogeneration | ||
| # - name: Run test_case_generator and move the folder test-cases to src/main/resources/sample/test-cases | ||
| # working-directory: ./csv_parser | ||
|
|
@@ -82,9 +113,8 @@ jobs: | |
| working-directory: ./csv_parser/json_schema2xsd | ||
| if: steps.filter.outputs.parsing_required == 'true' | ||
| run: | | ||
| rm out/RS-ERROR.xsd | ||
| # Clean XSD repo but keep manual XSDs | ||
| find ../../src/main/resources/xsd -type f -name '*.xsd' ! -name 'EDXL-DE-*.xsd' ! -name 'CustomContent.xsd' ! -name 'RC-XML-ContentType.xsd' ! -name 'RS-ERROR.xsd' ! -path '**/other-supporting-schema/*' -exec rm {} + | ||
| find ../../src/main/resources/xsd -type f -name '*.xsd' ! -name 'EDXL-DE-*.xsd' ! -name 'customContent.xsd' ! -name 'RC-DE.xsd' ! -name 'RC-XML-ContentType.xsd' ! -name 'RS-ERROR.xsd' ! -path '**/other-supporting-schema/*' -exec rm {} + | ||
| mv out/*.xsd ../../src/main/resources/xsd/ | ||
|
|
||
| - name: Remove input JSON Schemas | ||
|
|
@@ -107,18 +137,14 @@ jobs: | |
| - name: Generate Java classes | ||
| working-directory: ./generator | ||
| run: | | ||
| npx @openapitools/openapi-generator-cli generate -c ./config/RC-DE/RC-DE.generator-config.json --skip-validate-spec | ||
| npx @openapitools/openapi-generator-cli generate -c ./config/RC-DE/RC-DE.distribution-element.generator-config.json --skip-validate-spec | ||
| # Iterate over each file in the ./config directory, including the entire subfolder structure | ||
| # and then run @openapitools/openapi-generator-cli generate for each file found | ||
| # Important notice: | ||
| # Results of the find command are sorted in an alphabetic order before being passed to xargs | ||
| # This means that since the order of class generation is important, it's necessary to maintain an adequately | ||
| # named file structure in the ./config directory | ||
romainfd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| find ./config/ -type f | sort -n | xargs -i npx @openapitools/openapi-generator-cli generate -c {} --skip-validate-spec | ||
|
|
||
| IFS=' ' read -ra SCHEMAS_ARRAY <<< "$SCHEMAS" | ||
| for SCHEMA in "${SCHEMAS_ARRAY[@]}"; do | ||
| if [ "$SCHEMA" != "RS-EDA-MAJ" ] && [ "$SCHEMA" != "RS-ER" ]; then | ||
| npx @openapitools/openapi-generator-cli generate -c ./config/$SCHEMA/$SCHEMA.generator-config.json --skip-validate-spec | ||
| fi | ||
| npx @openapitools/openapi-generator-cli generate -c ./config/$SCHEMA/$SCHEMA.usecase.generator-config.json --skip-validate-spec | ||
| npx @openapitools/openapi-generator-cli generate -c ./config/$SCHEMA/$SCHEMA.wrapper.generator-config.json --skip-validate-spec | ||
| done | ||
|
|
||
| - name: Replace src/ with generated classes | ||
| run: | | ||
| rm -r ./src/main/java/com/hubsante/model/rcde || true | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # We will be using the file schemas.yaml as data source to use with gomplate in order to generate all the files | ||
| # required for each schema | ||
|
|
||
| # generate base generator config files | ||
| gomplate -f ./templates/schema.generator-config.json.tmpl -d config=./schemas.yaml -o ./output/generator/config | ||
|
|
||
| # generate usecase config files | ||
| gomplate -f ./templates/schema.usecase.generator-config.json.tmpl -d config=./schemas.yaml -o ./output/generator/config | ||
|
|
||
| # generate wrapper config files | ||
| gomplate -f ./templates/schema.wrapper.generator-config.json.tmpl -d config=./schemas.yaml -o ./output/generator/config | ||
|
|
||
| # generate ContentMessage class | ||
| gomplate -f ./templates/ContentMessage.java.tmpl -d config=./schemas.yaml -o ./output/edxl/ContentMessage.java | ||
|
|
||
| # generate EDXL-DE json schema | ||
| gomplate -f ./templates/EDXL-DE-full.schema.json.tmpl -d config=./schemas.yaml -o ./output/json-schema/EDXL-DE-full.schema.json | ||
|
|
||
| # generate RC-XML-ContentType xsd schema | ||
| gomplate -f ./templates/RC-XML-ContentType.xsd.tmpl -d config=./schemas.yaml -o ./output/xsd/RC-XML-ContentType.xsd |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../csv_parser/json_schema2xsd/src/main/resources/schemas.yaml |
53 changes: 53 additions & 0 deletions
53
automatic-schema-generator/templates/ContentMessage.java.tmpl
romainfd marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| /************************************************************************** | ||
| * This file has been generated by the automatic schema generator script. | ||
| **************************************************************************/ | ||
|
|
||
| package com.hubsante.model.edxl; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonSubTypes; | ||
| import com.fasterxml.jackson.annotation.JsonTypeInfo; | ||
|
|
||
| {{ range (datasource "config").schemas }} | ||
| {{- if eq .automaticGeneration "Y" -}} | ||
| import com.hubsante.model.{{ .package }}.{{ .rootElement | title }}; | ||
| import com.hubsante.model.{{ .package }}.{{ .rootElement | title }}Wrapper; | ||
| {{ end }}{{ end -}} | ||
| import com.hubsante.model.report.ErrorWrapper; | ||
| import com.hubsante.model.custom.CustomMessage; | ||
| import java.util.Map; | ||
| import java.util.stream.Collectors; | ||
| import java.util.stream.Stream; | ||
|
|
||
| @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) | ||
romainfd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| @JsonSubTypes({ {{ range $i, $e := (datasource "config").schemas }}{{if eq .automaticGeneration "Y"}} | ||
| @JsonSubTypes.Type({{ .rootElement | title }}Wrapper.class),{{end}}{{end}} | ||
| @JsonSubTypes.Type(ErrorWrapper.class), | ||
| @JsonSubTypes.Type(CustomMessage.class) | ||
| }) | ||
| public class ContentMessage { | ||
|
|
||
| /** This equals override is used to avoid breaking the equals override in the messages without RC-DE headers | ||
| * (in particular ErrorWrapper), as without the override the equality check would only pass when comparing | ||
| * an object to itself, and we care about the actual values. | ||
| **/ | ||
romainfd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) return true; | ||
| return o != null && getClass() == o.getClass(); | ||
| } | ||
|
|
||
| // As this class has no fields, the hashcode is always 0 | ||
| @Override | ||
| public int hashCode() { | ||
| return 0; | ||
| } | ||
|
|
||
| public static class UseCaseHelper { | ||
| public static final Map<String,String> useCases = Stream.of(new String[][] { | ||
| {{ range $i, $e := (datasource "config").schemas }}{{if eq .automaticGeneration "Y"}} | ||
| {"{{ .rootElement }}", {{ .rootElement | title }}.class.getCanonicalName()},{{end}}{{end}} | ||
| {"error", ErrorWrapper.class.getCanonicalName()}, | ||
| {"customContent", CustomMessage.class.getCanonicalName()} | ||
| }).collect(Collectors.toMap(useCaseData -> useCaseData[0], useCaseData -> useCaseData[1])); | ||
| } | ||
| } | ||
54 changes: 54 additions & 0 deletions
54
automatic-schema-generator/templates/ContentMessageDeserializer.java.tmpl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| /************************************************************************** | ||
| * This file has been generated by the automatic schema generator script. | ||
| **************************************************************************/ | ||
|
|
||
| package com.hubsante.model; | ||
|
|
||
| import com.fasterxml.jackson.core.JsonParseException; | ||
| import com.fasterxml.jackson.core.JsonParser; | ||
| import com.fasterxml.jackson.core.ObjectCodec; | ||
| import com.fasterxml.jackson.databind.DeserializationContext; | ||
| import com.fasterxml.jackson.databind.JsonDeserializer; | ||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import com.hubsante.model.edxl.ContentMessage; | ||
| import com.hubsante.model.edxl.Descriptor; | ||
| import com.hubsante.model.edxl.EdxlMessage; | ||
| import com.hubsante.model.edxl.Keyword; | ||
| {{ range (datasource "config").schemas }}{{if eq .automaticGeneration "Y" -}} | ||
| import com.hubsante.model.{{ .package }}.{{ .rootElement | title }}Wrapper; | ||
| {{ end }}{{ end -}} | ||
| import com.hubsante.model.report.ErrorWrapper; | ||
| import com.hubsante.model.custom.CustomMessage; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| public class ContentMessageDeserializer extends JsonDeserializer<ContentMessage> { | ||
|
|
||
| private static final Map<String, Class> useCases = new HashMap<String, Class>() { { | ||
| {{ range (datasource "config").schemas }}{{if eq .automaticGeneration "Y"}}put("{{ .rootElement }}", {{ .rootElement | title }}Wrapper.class); | ||
| {{ end }}{{ end -}} | ||
| put("error", ErrorWrapper.class); | ||
| put("customContent", CustomMessage.class); | ||
| } }; | ||
|
|
||
| @Override | ||
| public ContentMessage deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { | ||
| ObjectCodec codec = jp.getCodec(); | ||
| JsonNode node = codec.readTree(jp); | ||
| String model = null; | ||
| try { | ||
| model = ((EdxlMessage) jp.getParsingContext().getParent().getParent().getParent().getParent().getCurrentValue()).getDescriptor().getKeyword().stream().filter(keyword -> keyword.getValueListURI().equals("urn:hubsante:model")).findFirst().get().getValue(); | ||
| } catch (NullPointerException e) { | ||
| throw new JsonParseException(jp, "Model name not found in $.descriptor.keyword[0].value"); | ||
| } | ||
| // Find model in useCases map, throw JsonParseException if not found | ||
| Class clazz = useCases.get(model); | ||
| if (clazz == null) { | ||
| throw new JsonParseException(jp, "Unknown model: " + model); | ||
| } | ||
|
|
||
| return (ContentMessage) codec.treeToValue(node, clazz); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.