forked from Qortal/qortal
Merge pull request #6 from KaaCee/master
Globalization/translation implementation and switch to JUnit5
This commit is contained in:
commit
3c8a1713d5
104
globalization/Api.de.xml
Normal file
104
globalization/Api.de.xml
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<localization>
|
||||||
|
<context locale="de">
|
||||||
|
<context path="Api">
|
||||||
|
<context path="ApiError">
|
||||||
|
<translation key="0" template="Unbekannter Fehler" />
|
||||||
|
<translation key="1" template="JSON Nachricht konnte nicht geparsed werden" />
|
||||||
|
<translation key="2" template="Guthaben ungenügend" />
|
||||||
|
<translation key="3" template="Feature wurde noch nicht veröffentlicht" />
|
||||||
|
|
||||||
|
<translation key="101" template="Ungültige Signatur" />
|
||||||
|
<translation key="102" template="Ungültige Adresse" />
|
||||||
|
<translation key="103" template="Ungültiger Seed" />
|
||||||
|
<translation key="104" template="Ungültiger Betrag" />
|
||||||
|
<translation key="105" template="Ungültige Gebühr" />
|
||||||
|
<translation key="106" template="Ungültiger Sender" />
|
||||||
|
<translation key="107" template="Ungültiger Empfänger" />
|
||||||
|
<translation key="108" template="Ungültige Namenslänge" />
|
||||||
|
<translation key="109" template="Ungültige Wertlänge" />
|
||||||
|
<translation key="110" template="Ungültiger Namensbesitzer" />
|
||||||
|
<translation key="111" template="Ungültiger Käufer" />
|
||||||
|
<translation key="112" template="Ungültiger Public Key" />
|
||||||
|
<translation key="113" template="Ungültige Optionen-Länge" />
|
||||||
|
<translation key="114" template="Ungültige Optionslänge" />
|
||||||
|
<translation key="115" template="Ungültige Daten" />
|
||||||
|
<translation key="116" template="Ungültige Datenlänge" />
|
||||||
|
<translation key="117" template="Ungültiger Update-Wert" />
|
||||||
|
<translation key="118" template="Der Schlüssel existiert bereits, Editieren ist deaktiviert" />
|
||||||
|
<translation key="119" template="Der Schlüssel existiert nicht" />
|
||||||
|
<translation key="120" template="Du kannst den Schlüssel '${key}' nicht löschen, wenn er der einzige ist" />
|
||||||
|
<translation key="121" template="fee less required" />
|
||||||
|
<translation key="122" template="Das Wallet muss synchronisiert werden" />
|
||||||
|
<translation key="123" template="Ungültige Netzwerkadresse" />
|
||||||
|
|
||||||
|
<translation key="201" template="Das Wallet existiert nicht" />
|
||||||
|
<translation key="202" template="Die Adresse existiert nicht im Wallet" />
|
||||||
|
<translation key="203" template="Das Wallet ist abgeschlossen" />
|
||||||
|
<translation key="204" template="Das Wallet existiert bereits" />
|
||||||
|
<translation key="205" template="Der Benutzer hat den API-Aufruf abgelehnt" />
|
||||||
|
|
||||||
|
<translation key="301" template="Der Block existiert nicht" />
|
||||||
|
<translation key="311" template="Die Transaktion existiert nicht" />
|
||||||
|
<translation key="304" template="Public Key wurde nicht gefunden" />
|
||||||
|
|
||||||
|
<translation key="401" template="Der Name existiert nicht" />
|
||||||
|
<translation key="402" template="Der Name existiert bereits" />
|
||||||
|
<translation key="403" template="Der Name steht bereits zum Verkauf" />
|
||||||
|
<translation key="404" template="Der Name muss aus Kleinbuchstaben bestehen" />
|
||||||
|
<translation key="410" template="Namensverkauf existiert nicht" />
|
||||||
|
<translation key="411" template="Der Käufer ist bereits Besitzer" />
|
||||||
|
|
||||||
|
<translation key="501" template="Die Abstimmung existiert nicht" />
|
||||||
|
<translation key="502" template="Die Abstimmung existiert bereits" />
|
||||||
|
<translation key="503" template="Nicht alle Optionen sind eindeutig" />
|
||||||
|
<translation key="504" template="Die option existiert nicht" />
|
||||||
|
<translation key="505" template="Bereits für diese Option abgestimmt" />
|
||||||
|
|
||||||
|
<translation key="601" template="Ungültige Asset ID" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<translation key="701" template="?NAME_NOT_REGISTERED?" />
|
||||||
|
<translation key="702" template="?NAME_FOR_SALE?" />
|
||||||
|
<translation key="703" template="?NAME_WITH_SPACE?" />
|
||||||
|
-->
|
||||||
|
|
||||||
|
<translation key="801" template="Ungültige Beschreibungslänge. Max. Länge ${MAX_LENGTH}" />
|
||||||
|
<translation key="802" template="Der Code ist leer" />
|
||||||
|
<translation key="803" template="Ungültige Datenlänge" />
|
||||||
|
<translation key="804" template="Ungültige Seiten" />
|
||||||
|
<translation key="805" template="Ungültige Typlänge" />
|
||||||
|
<translation key="806" template="Ungültige Tag-Länge" />
|
||||||
|
<translation key="809" template="Fehler in Creation Bytes" />
|
||||||
|
|
||||||
|
<translation key="901" template="invalid body it must not be empty" />
|
||||||
|
<translation key="902" template="Dieser Blog ist deaktiviert" />
|
||||||
|
<translation key="903" template="the creator address does not own the author name" />
|
||||||
|
<translation key="904" template="the data size is too large - currently only ${BATCH_TX_AMOUNT} arbitrary transactions are allowed at once!" />
|
||||||
|
<translation key="905" template="transaction with this signature contains no entries!" />
|
||||||
|
<translation key="906" template="this blog is empty" />
|
||||||
|
<translation key="907" template="the attribute postid is empty! this is the signature of the post you want to comment" />
|
||||||
|
<translation key="908" template="for the given postid no blogpost to comment was found" />
|
||||||
|
<translation key="909" template="commenting is for this blog disabled" />
|
||||||
|
<translation key="910" template="for the given signature no comment was found" />
|
||||||
|
<translation key="911" template="invalid comment owner" />
|
||||||
|
|
||||||
|
<translation key="1001" template="the Message format is not hex - correct the text or use isTextMessage = true" />
|
||||||
|
<translation key="1002" template="The message attribute is missing or content is blank" />
|
||||||
|
<translation key="1003" template="The recipient has not yet performed any action in the blockchain.\nYou can't send an encrypted message to him." />
|
||||||
|
<translation key="1004" template="Message size exceeded!" />
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<context path="ApiClient">
|
||||||
|
<translation key="invalid command" template="Ungültiger Befehl!\nGib 'help all' ein, um eine Liste aller gültigen Befehle zu erhalten." />
|
||||||
|
<translation key="error footer" template="Gib 'help all' ein, um eine Liste aller gültigen Befehle zu erhalten." />
|
||||||
|
|
||||||
|
<translation key="help: success responses" template="Antwort bei Erfolg:" />
|
||||||
|
<translation key="help: failure responses" template="Antwort bei Misserfolg:" />
|
||||||
|
</context>
|
||||||
|
|
||||||
|
</context>
|
||||||
|
|
||||||
|
</context>
|
||||||
|
|
||||||
|
</localization>
|
108
globalization/Api.en.xml
Normal file
108
globalization/Api.en.xml
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<localization>
|
||||||
|
<context locale="en">
|
||||||
|
<context path="Api">
|
||||||
|
<context path="ApiError">
|
||||||
|
<translation key="0" template="unknown error" />
|
||||||
|
<translation key="1" template="failed to parse json message" />
|
||||||
|
<translation key="2" template="not enough balance" />
|
||||||
|
<translation key="3" template="that feature is not yet released" />
|
||||||
|
|
||||||
|
<translation key="101" template="invalid signature" />
|
||||||
|
<translation key="102" template="invalid address" />
|
||||||
|
<translation key="103" template="invalid seed" />
|
||||||
|
<translation key="104" template="invalid amount" />
|
||||||
|
<translation key="105" template="invalid fee" />
|
||||||
|
<translation key="106" template="invalid sender" />
|
||||||
|
<translation key="107" template="invalid recipient" />
|
||||||
|
<translation key="108" template="invalid name length" />
|
||||||
|
<translation key="109" template="invalid value length" />
|
||||||
|
<translation key="110" template="invalid name owner" />
|
||||||
|
<translation key="111" template="invalid buyer" />
|
||||||
|
<translation key="112" template="invalid public key" />
|
||||||
|
<translation key="113" template="invalid options length" />
|
||||||
|
<translation key="114" template="invalid option length" />
|
||||||
|
<translation key="115" template="invalid data" />
|
||||||
|
<translation key="116" template="invalid data length" />
|
||||||
|
<translation key="117" template="invalid update value" />
|
||||||
|
<translation key="118" template="key already exists, edit is false" />
|
||||||
|
<translation key="119" template="the key does not exist" />
|
||||||
|
<translation key="120" template="you can't delete the key '${key}' if it is the only key" />
|
||||||
|
<translation key="121" template="fee less required" />
|
||||||
|
<translation key="122" template="wallet needs to be synchronized" />
|
||||||
|
<translation key="123" template="invalid network address" />
|
||||||
|
|
||||||
|
<translation key="201" template="wallet does not exist" />
|
||||||
|
<translation key="202" template="address does not exist in wallet" />
|
||||||
|
<translation key="203" template="wallet is locked" />
|
||||||
|
<translation key="204" template="wallet already exists" />
|
||||||
|
<translation key="205" template="user denied api call" />
|
||||||
|
|
||||||
|
<translation key="301" template="block does not exist" />
|
||||||
|
<translation key="311" template="transactions does not exist" />
|
||||||
|
<translation key="304" template="public key not found" />
|
||||||
|
|
||||||
|
<translation key="401" template="name does not exist" />
|
||||||
|
<translation key="402" template="name already exists" />
|
||||||
|
<translation key="403" template="name already for sale" />
|
||||||
|
<translation key="404" template="name must be lower case" />
|
||||||
|
<translation key="410" template="namesale does not exist" />
|
||||||
|
<translation key="411" template="buyer is already owner" />
|
||||||
|
|
||||||
|
<translation key="501" template="poll does not exist" />
|
||||||
|
<translation key="502" template="poll already exists" />
|
||||||
|
<translation key="503" template="not all options are unique" />
|
||||||
|
<translation key="504" template="option does not exist" />
|
||||||
|
<translation key="505" template="already voted for that option" />
|
||||||
|
|
||||||
|
<translation key="601" template="invalid asset id" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<translation key="701" template="?NAME_NOT_REGISTERED?" />
|
||||||
|
<translation key="702" template="?NAME_FOR_SALE?" />
|
||||||
|
<translation key="703" template="?NAME_WITH_SPACE?" />
|
||||||
|
-->
|
||||||
|
|
||||||
|
<translation key="801" template="invalid description length. max length ${MAX_LENGTH}" />
|
||||||
|
<translation key="802" template="code is empty" />
|
||||||
|
<translation key="803" template="invalid data length" />
|
||||||
|
<translation key="804" template="invalid pages" />
|
||||||
|
<translation key="805" template="invalid type length" />
|
||||||
|
<translation key="806" template="invalid tags length" />
|
||||||
|
<translation key="809" template="error in creation bytes" />
|
||||||
|
|
||||||
|
<translation key="901" template="invalid body it must not be empty" />
|
||||||
|
<translation key="902" template="this blog is disabled" />
|
||||||
|
<translation key="903" template="the creator address does not own the author name" />
|
||||||
|
<translation key="904" template="the data size is too large - currently only ${BATCH_TX_AMOUNT} arbitrary transactions are allowed at once!" />
|
||||||
|
<translation key="905" template="transaction with this signature contains no entries!" />
|
||||||
|
<translation key="906" template="this blog is empty" />
|
||||||
|
<translation key="907" template="the attribute postid is empty! this is the signature of the post you want to comment" />
|
||||||
|
<translation key="908" template="for the given postid no blogpost to comment was found" />
|
||||||
|
<translation key="909" template="commenting is for this blog disabled" />
|
||||||
|
<translation key="910" template="for the given signature no comment was found" />
|
||||||
|
<translation key="911" template="invalid comment owner" />
|
||||||
|
|
||||||
|
<translation key="1001" template="the Message format is not hex - correct the text or use isTextMessage = true" />
|
||||||
|
<translation key="1002" template="The message attribute is missing or content is blank" />
|
||||||
|
<translation key="1003" template="The recipient has not yet performed any action in the blockchain.\nYou can't send an encrypted message to him." />
|
||||||
|
<translation key="1004" template="Message size exceeded!" />
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<context path="ApiClient">
|
||||||
|
<translation key="invalid command" template="Invalid command! \nType 'help all' to get a list of commands." />
|
||||||
|
<translation key="error footer" template="Type 'help all' to get a list of commands." />
|
||||||
|
|
||||||
|
<translation key="API error response" template="(API error: ${ERROR_CODE}) ${DESCRIPTION}" />
|
||||||
|
|
||||||
|
<translation key="error: with body" template="HTTP Status ${STATUS}: ${BODY}" />
|
||||||
|
<translation key="error: without body" template="HTTP Status ${STATUS}" />
|
||||||
|
<translation key="help: success responses" template="On success returns:" />
|
||||||
|
<translation key="help: failure responses" template="On failure returns:" />
|
||||||
|
</context>
|
||||||
|
|
||||||
|
</context>
|
||||||
|
|
||||||
|
</context>
|
||||||
|
|
||||||
|
</localization>
|
56
globalization/BlocksResource.de.xml
Normal file
56
globalization/BlocksResource.de.xml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<localization>
|
||||||
|
<context locale="de">
|
||||||
|
|
||||||
|
<context path="Api">
|
||||||
|
<context path="BlocksResource">
|
||||||
|
<!--<context path="GET signature">
|
||||||
|
<translation key="operation:description" template="returns the block that matches the given signature" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
<context path="GET first">
|
||||||
|
<translation key="operation:description" template="returns the genesis block" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
<context path="GET last">
|
||||||
|
<translation key="operation:description" template="returns the last valid block" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
<context path="GET child:signature">
|
||||||
|
<translation key="operation:description" template="returns the child block of the block that matches the given signature" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
<context path="GET generatingbalance">
|
||||||
|
<translation key="operation:description" template="calculates the generating balance of the block that will follow the last block" />
|
||||||
|
<translation key="success_response:description" template="the generating balance" />
|
||||||
|
</context>
|
||||||
|
<context path="GET generatingbalance:signature">
|
||||||
|
<translation key="operation:description" template="calculates the generating balance of the block that will follow the block that matches the signature" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
<context path="GET time">
|
||||||
|
<translation key="operation:description" template="calculates the time it should take for the network to generate the next block" />
|
||||||
|
<translation key="success_response:description" template="the time" />
|
||||||
|
</context>
|
||||||
|
<context path="GET time:generatingbalance">
|
||||||
|
<translation key="operation:description" template="calculates the time it should take for the network to generate blocks when the current generating balance in the network is the specified generating balance" />
|
||||||
|
<translation key="success_response:description" template="the time" />
|
||||||
|
</context>
|
||||||
|
<context path="GET height">
|
||||||
|
<translation key="operation:description" template="returns the block height of the last block." />
|
||||||
|
<translation key="success_response:description" template="the height" />
|
||||||
|
</context>
|
||||||
|
<context path="GET height:signature">
|
||||||
|
<translation key="operation:description" template="returns the block height of the block that matches the given signature" />
|
||||||
|
<translation key="success_response:description" template="the height" />
|
||||||
|
</context>
|
||||||
|
<context path="GET byheight:height">
|
||||||
|
<translation key="operation:description" template="returns the block whith given height" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>-->
|
||||||
|
</context>
|
||||||
|
</context>
|
||||||
|
|
||||||
|
</context>
|
||||||
|
|
||||||
|
</localization>
|
56
globalization/BlocksResource.en.xml
Normal file
56
globalization/BlocksResource.en.xml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<localization>
|
||||||
|
<context locale="en">
|
||||||
|
|
||||||
|
<context path="Api">
|
||||||
|
<context path="BlocksResource">
|
||||||
|
<context path="GET signature">
|
||||||
|
<translation key="operation:description" template="returns the block that matches the given signature" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
<context path="GET first">
|
||||||
|
<translation key="operation:description" template="returns the genesis block" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
<context path="GET last">
|
||||||
|
<translation key="operation:description" template="returns the last valid block" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
<context path="GET child:signature">
|
||||||
|
<translation key="operation:description" template="returns the child block of the block that matches the given signature" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
<context path="GET generatingbalance">
|
||||||
|
<translation key="operation:description" template="calculates the generating balance of the block that will follow the last block" />
|
||||||
|
<translation key="success_response:description" template="the generating balance" />
|
||||||
|
</context>
|
||||||
|
<context path="GET generatingbalance:signature">
|
||||||
|
<translation key="operation:description" template="calculates the generating balance of the block that will follow the block that matches the signature" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
<context path="GET time">
|
||||||
|
<translation key="operation:description" template="calculates the time it should take for the network to generate the next block" />
|
||||||
|
<translation key="success_response:description" template="the time" />
|
||||||
|
</context>
|
||||||
|
<context path="GET time:generatingbalance">
|
||||||
|
<translation key="operation:description" template="calculates the time it should take for the network to generate blocks when the current generating balance in the network is the specified generating balance" />
|
||||||
|
<translation key="success_response:description" template="the time" />
|
||||||
|
</context>
|
||||||
|
<context path="GET height">
|
||||||
|
<translation key="operation:description" template="returns the block height of the last block." />
|
||||||
|
<translation key="success_response:description" template="the height" />
|
||||||
|
</context>
|
||||||
|
<context path="GET height:signature">
|
||||||
|
<translation key="operation:description" template="returns the block height of the block that matches the given signature" />
|
||||||
|
<translation key="success_response:description" template="the height" />
|
||||||
|
</context>
|
||||||
|
<context path="GET byheight:height">
|
||||||
|
<translation key="operation:description" template="returns the block whith given height" />
|
||||||
|
<translation key="success_response:description" template="the block" />
|
||||||
|
</context>
|
||||||
|
</context>
|
||||||
|
</context>
|
||||||
|
|
||||||
|
</context>
|
||||||
|
|
||||||
|
</localization>
|
101
pom.xml
101
pom.xml
@ -4,6 +4,9 @@
|
|||||||
<groupId>org.qora</groupId>
|
<groupId>org.qora</groupId>
|
||||||
<artifactId>qora-core</artifactId>
|
<artifactId>qora-core</artifactId>
|
||||||
<version>2.0.0-SNAPSHOT</version>
|
<version>2.0.0-SNAPSHOT</version>
|
||||||
|
<properties>
|
||||||
|
<swagger-ui.version>3.19.0</swagger-ui.version>
|
||||||
|
</properties>
|
||||||
<build>
|
<build>
|
||||||
<sourceDirectory>src</sourceDirectory>
|
<sourceDirectory>src</sourceDirectory>
|
||||||
<plugins>
|
<plugins>
|
||||||
@ -15,6 +18,74 @@
|
|||||||
<target>1.8</target>
|
<target>1.8</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<!-- unpack swagger-ui to target folder -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>swagger ui</id>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>unpack</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<artifactItems>
|
||||||
|
<artifactItem>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>swagger-ui</artifactId>
|
||||||
|
<version>${swagger-ui.version}</version>
|
||||||
|
</artifactItem>
|
||||||
|
</artifactItems>
|
||||||
|
<outputDirectory>${project.build.directory}/swagger-ui.unpacked</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<!-- inject correct url to swagger json file into swwagger-ui -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.google.code.maven-replacer-plugin</groupId>
|
||||||
|
<artifactId>replacer</artifactId>
|
||||||
|
<version>1.5.3</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>replace</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<file>${project.build.directory}/swagger-ui.unpacked/META-INF/resources/webjars/swagger-ui/${swagger-ui.version}/index.html</file>
|
||||||
|
<replacements>
|
||||||
|
<replacement>
|
||||||
|
<token>https://petstore.swagger.io/v2/swagger.json</token>
|
||||||
|
<value>/openapi.json</value>
|
||||||
|
</replacement>
|
||||||
|
</replacements>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- add swagger-ui as resource to output package -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-resources</id>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/classes/resources/swagger-ui</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${project.build.directory}/swagger-ui.unpacked/META-INF/resources/webjars/swagger-ui/${swagger-ui.version}</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
<repositories>
|
<repositories>
|
||||||
@ -118,5 +189,35 @@
|
|||||||
<version>2.4.1</version>
|
<version>2.4.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<version>5.3.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
<artifactId>hamcrest-library</artifactId>
|
||||||
|
<version>1.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jersey.media</groupId>
|
||||||
|
<artifactId>jersey-media-multipart</artifactId>
|
||||||
|
<version>2.27</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.mail</groupId>
|
||||||
|
<artifactId>mail</artifactId>
|
||||||
|
<version>1.5.0-b01</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>swagger-ui</artifactId>
|
||||||
|
<version>${swagger-ui.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-rewrite</artifactId>
|
||||||
|
<version>9.4.11.v20180605</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
@ -8,7 +8,7 @@ import repository.hsqldb.HSQLDBRepositoryFactory;
|
|||||||
|
|
||||||
public class Start {
|
public class Start {
|
||||||
|
|
||||||
private static final String connectionUrl = "jdbc:hsqldb:mem:db/test;create=true;close_result=true;sql.strict_exec=true;sql.enforce_names=true;sql.syntax_mys=true";
|
private static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true";
|
||||||
|
|
||||||
public static void main(String args[]) throws DataException {
|
public static void main(String args[]) throws DataException {
|
||||||
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
||||||
@ -19,7 +19,7 @@ public class Start {
|
|||||||
|
|
||||||
//// testing the API client
|
//// testing the API client
|
||||||
//ApiClient client = ApiClient.getInstance();
|
//ApiClient client = ApiClient.getInstance();
|
||||||
//String test = client.executeCommand("GET blocks/height");
|
//String test = client.executeCommand("GET blocks/first");
|
||||||
//System.out.println(test);
|
//System.out.println(test);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
382
src/api/AddressesResource.java
Normal file
382
src/api/AddressesResource.java
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
package api;
|
||||||
|
|
||||||
|
import data.account.AccountData;
|
||||||
|
import data.block.BlockData;
|
||||||
|
import globalization.Translator;
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.extensions.Extension;
|
||||||
|
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import qora.account.Account;
|
||||||
|
import qora.assets.Asset;
|
||||||
|
import qora.crypto.Crypto;
|
||||||
|
import repository.Repository;
|
||||||
|
import repository.RepositoryManager;
|
||||||
|
import utils.Base58;
|
||||||
|
|
||||||
|
@Path("addresses")
|
||||||
|
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
||||||
|
@OpenAPIDefinition(
|
||||||
|
extensions = @Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="/Api/AddressesResource")
|
||||||
|
})
|
||||||
|
)
|
||||||
|
public class AddressesResource {
|
||||||
|
|
||||||
|
@Context
|
||||||
|
HttpServletRequest request;
|
||||||
|
|
||||||
|
private ApiErrorFactory apiErrorFactory;
|
||||||
|
|
||||||
|
public AddressesResource() {
|
||||||
|
this(new ApiErrorFactory(Translator.getInstance()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddressesResource(ApiErrorFactory apiErrorFactory) {
|
||||||
|
this.apiErrorFactory = apiErrorFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/lastreference/{address}")
|
||||||
|
@Operation(
|
||||||
|
description = "Returns the 64-byte long base58-encoded signature of last transaction where the address is delivered as creator. Or the first incoming transaction. Returns \"false\" if there is no transactions.",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET lastreference:address"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_ADDRESS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the base58-encoded transaction signature or \"false\"",
|
||||||
|
content = @Content(schema = @Schema(implementation = String.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public String getLastReference(
|
||||||
|
@Parameter(description = "a base58-encoded address", required = true) @PathParam("address") String address
|
||||||
|
) {
|
||||||
|
Security.checkApiCallAllowed("GET addresses/lastreference", request);
|
||||||
|
|
||||||
|
if (!Crypto.isValidAddress(address))
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
|
||||||
|
|
||||||
|
byte[] lastReference = null;
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
Account account = new Account(repository, address);
|
||||||
|
account.getLastReference();
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lastReference == null || lastReference.length == 0) {
|
||||||
|
return "false";
|
||||||
|
} else {
|
||||||
|
return Base58.encode(lastReference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/lastreference/{address}/unconfirmed")
|
||||||
|
@Operation(
|
||||||
|
description = "Returns the 64-byte long base58-encoded signature of last transaction including unconfirmed where the address is delivered as creator. Or the first incoming transaction. Returns \\\"false\\\" if there is no transactions.",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET lastreference:address:unconfirmed"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_ADDRESS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the base58-encoded transaction signature",
|
||||||
|
content = @Content(schema = @Schema(implementation = String.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public String getLastReferenceUnconfirmed(@PathParam("address") String address) {
|
||||||
|
Security.checkApiCallAllowed("GET addresses/lastreference", request);
|
||||||
|
|
||||||
|
// XXX: is this method needed?
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/validate/{address}")
|
||||||
|
@Operation(
|
||||||
|
description = "Validates the given address. Returns true/false.",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET validate:address"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
//description = "",
|
||||||
|
content = @Content(schema = @Schema(implementation = Boolean.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public boolean validate(@PathParam("address") String address) {
|
||||||
|
Security.checkApiCallAllowed("GET addresses/validate", request);
|
||||||
|
|
||||||
|
return Crypto.isValidAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/generatingbalance/{address}")
|
||||||
|
@Operation(
|
||||||
|
description = "Return the generating balance of the given address.",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET generatingbalance:address"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_ADDRESS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the generating balance",
|
||||||
|
content = @Content(schema = @Schema(implementation = BigDecimal.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public BigDecimal getGeneratingBalanceOfAddress(@PathParam("address") String address) {
|
||||||
|
Security.checkApiCallAllowed("GET addresses/generatingbalance", request);
|
||||||
|
|
||||||
|
if (!Crypto.isValidAddress(address))
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
Account account = new Account(repository, address);
|
||||||
|
return account.getGeneratingBalance();
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("balance/{address}")
|
||||||
|
@Operation(
|
||||||
|
description = "Returns the confirmed balance of the given address.",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET balance:address"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_ADDRESS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the balance",
|
||||||
|
content = @Content(schema = @Schema(implementation = BigDecimal.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public BigDecimal getGeneratingBalance(@PathParam("address") String address) {
|
||||||
|
Security.checkApiCallAllowed("GET addresses/balance", request);
|
||||||
|
|
||||||
|
if (!Crypto.isValidAddress(address))
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
Account account = new Account(repository, address);
|
||||||
|
return account.getConfirmedBalance(Asset.QORA);
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("assetbalance/{assetid}/{address}")
|
||||||
|
@Operation(
|
||||||
|
description = "Returns the confirmed balance of the given address for the given asset key.",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET assetbalance:assetid:address"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_ADDRESS\", \"INVALID_ASSET_ID\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the balance",
|
||||||
|
content = @Content(schema = @Schema(implementation = BigDecimal.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public BigDecimal getAssetBalance(@PathParam("assetid") long assetid, @PathParam("address") String address) {
|
||||||
|
Security.checkApiCallAllowed("GET addresses/assetbalance", request);
|
||||||
|
|
||||||
|
if (!Crypto.isValidAddress(address))
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
Account account = new Account(repository, address);
|
||||||
|
return account.getConfirmedBalance(assetid);
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("assets/{address}")
|
||||||
|
@Operation(
|
||||||
|
description = "Returns the list of assets for this address with balances.",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET assets:address"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_ADDRESS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the list of assets",
|
||||||
|
content = @Content(schema = @Schema(implementation = String.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public String getAssetBalance(@PathParam("address") String address) {
|
||||||
|
Security.checkApiCallAllowed("GET addresses/assets", request);
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("balance/{address}/{confirmations}")
|
||||||
|
@Operation(
|
||||||
|
description = "Calculates the balance of the given address after the given confirmations.",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET balance:address:confirmations"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_ADDRESS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the balance",
|
||||||
|
content = @Content(schema = @Schema(implementation = String.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public String getGeneratingBalance(@PathParam("address") String address, @PathParam("confirmations") int confirmations) {
|
||||||
|
Security.checkApiCallAllowed("GET addresses/balance", request);
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/publickey/{address}")
|
||||||
|
@Operation(
|
||||||
|
description = "Returns the 32-byte long base58-encoded account publickey of the given address.",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET publickey:address"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_ADDRESS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the publickey",
|
||||||
|
content = @Content(schema = @Schema(implementation = String.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public String getPublicKey(@PathParam("address") String address) {
|
||||||
|
Security.checkApiCallAllowed("GET addresses/publickey", request);
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
221
src/api/AnnotationPostProcessor.java
Normal file
221
src/api/AnnotationPostProcessor.java
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
|
||||||
|
package api;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import globalization.ContextPaths;
|
||||||
|
import globalization.Translator;
|
||||||
|
import io.swagger.v3.core.converter.ModelConverters;
|
||||||
|
import io.swagger.v3.jaxrs2.Reader;
|
||||||
|
import io.swagger.v3.jaxrs2.ReaderListener;
|
||||||
|
import io.swagger.v3.oas.models.media.Content;
|
||||||
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
|
import io.swagger.v3.oas.models.Operation;
|
||||||
|
import io.swagger.v3.oas.models.PathItem;
|
||||||
|
import io.swagger.v3.oas.models.examples.Example;
|
||||||
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
import io.swagger.v3.oas.models.media.MediaType;
|
||||||
|
import io.swagger.v3.oas.models.media.Schema;
|
||||||
|
import io.swagger.v3.oas.models.responses.ApiResponse;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public class AnnotationPostProcessor implements ReaderListener {
|
||||||
|
|
||||||
|
private class ContextInformation {
|
||||||
|
public String path;
|
||||||
|
public Map<String, String> keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Translator translator;
|
||||||
|
private final ApiErrorFactory apiErrorFactory;
|
||||||
|
|
||||||
|
public AnnotationPostProcessor() {
|
||||||
|
this(Translator.getInstance(), ApiErrorFactory.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationPostProcessor(Translator translator, ApiErrorFactory apiErrorFactory) {
|
||||||
|
this.translator = translator;
|
||||||
|
this.apiErrorFactory = apiErrorFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeScan(Reader reader, OpenAPI openAPI) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterScan(Reader reader, OpenAPI openAPI) {
|
||||||
|
// use context path and keys from "x-translation" extension annotations
|
||||||
|
// to translate supported annotations and finally remove "x-translation" extensions
|
||||||
|
Info resourceInfo = openAPI.getInfo();
|
||||||
|
ContextInformation resourceContext = getContextInformation(openAPI.getExtensions());
|
||||||
|
removeTranslationAnnotations(openAPI.getExtensions());
|
||||||
|
TranslateProperties(Constants.TRANSLATABLE_INFO_PROPERTIES, resourceContext, resourceInfo);
|
||||||
|
|
||||||
|
for (Map.Entry<String, PathItem> pathEntry : openAPI.getPaths().entrySet())
|
||||||
|
{
|
||||||
|
PathItem pathItem = pathEntry.getValue();
|
||||||
|
ContextInformation pathContext = getContextInformation(pathItem.getExtensions(), resourceContext);
|
||||||
|
removeTranslationAnnotations(pathItem.getExtensions());
|
||||||
|
TranslateProperties(Constants.TRANSLATABLE_PATH_ITEM_PROPERTIES, pathContext, pathItem);
|
||||||
|
|
||||||
|
for (Operation operation : pathItem.readOperations()) {
|
||||||
|
ContextInformation operationContext = getContextInformation(operation.getExtensions(), pathContext);
|
||||||
|
removeTranslationAnnotations(operation.getExtensions());
|
||||||
|
TranslateProperties(Constants.TRANSLATABLE_OPERATION_PROPERTIES, operationContext, operation);
|
||||||
|
|
||||||
|
addApiErrorResponses(operation);
|
||||||
|
removeApiErrorsAnnotations(operation.getExtensions());
|
||||||
|
|
||||||
|
for (Map.Entry<String, ApiResponse> responseEntry : operation.getResponses().entrySet()) {
|
||||||
|
ApiResponse response = responseEntry.getValue();
|
||||||
|
ContextInformation responseContext = getContextInformation(response.getExtensions(), operationContext);
|
||||||
|
removeTranslationAnnotations(response.getExtensions());
|
||||||
|
TranslateProperties(Constants.TRANSLATABLE_API_RESPONSE_PROPERTIES, responseContext, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addApiErrorResponses(Operation operation) {
|
||||||
|
List<ApiError> apiErrors = getApiErrors(operation.getExtensions());
|
||||||
|
if(apiErrors != null) {
|
||||||
|
for(ApiError apiError : apiErrors) {
|
||||||
|
String statusCode = Integer.toString(apiError.getStatus());
|
||||||
|
ApiResponse apiResponse = operation.getResponses().get(statusCode);
|
||||||
|
if(apiResponse == null) {
|
||||||
|
Schema errorMessageSchema = ModelConverters.getInstance().readAllAsResolvedSchema(ApiErrorMessage.class).schema;
|
||||||
|
MediaType mediaType = new MediaType().schema(errorMessageSchema);
|
||||||
|
Content content = new Content().addMediaType(javax.ws.rs.core.MediaType.APPLICATION_JSON, mediaType);
|
||||||
|
apiResponse = new ApiResponse().content(content);
|
||||||
|
operation.getResponses().addApiResponse(statusCode, apiResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
int apiErrorCode = apiError.getCode();
|
||||||
|
ApiErrorMessage apiErrorMessage = new ApiErrorMessage(apiErrorCode, this.apiErrorFactory.getErrorMessage(apiError));
|
||||||
|
Example example = new Example().value(apiErrorMessage);
|
||||||
|
|
||||||
|
// XXX: addExamples(..) is not working in Swagger 2.0.4. This bug is referenced in https://github.com/swagger-api/swagger-ui/issues/2651
|
||||||
|
// Replace the call to .setExample(..) by .addExamples(..) when the bug is fixed.
|
||||||
|
apiResponse.getContent().get(javax.ws.rs.core.MediaType.APPLICATION_JSON).setExample(example);
|
||||||
|
//apiResponse.getContent().get(javax.ws.rs.core.MediaType.APPLICATION_JSON).addExamples(Integer.toString(apiErrorCode), example);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void TranslateProperties(List<TranslatableProperty<T>> translatableProperties, ContextInformation context, T item) {
|
||||||
|
if(context.keys != null) {
|
||||||
|
Map<String, String> keys = context.keys;
|
||||||
|
for(TranslatableProperty<T> prop : translatableProperties) {
|
||||||
|
String key = keys.get(prop.keyName());
|
||||||
|
if(key != null) {
|
||||||
|
String originalValue = prop.getValue(item);
|
||||||
|
// XXX: use browser locale instead default?
|
||||||
|
String translation = translator.translate(context.path, key, originalValue);
|
||||||
|
prop.setValue(item, translation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ApiError> getApiErrors(Map<String, Object> extensions) {
|
||||||
|
if(extensions == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
List<String> apiErrorStrings = new ArrayList();
|
||||||
|
try {
|
||||||
|
ArrayNode apiErrorsNode = (ArrayNode)extensions.get("x-" + Constants.API_ERRORS_EXTENSION_NAME);
|
||||||
|
if(apiErrorsNode == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for(int i = 0; i < apiErrorsNode.size(); i++) {
|
||||||
|
String errorString = apiErrorsNode.get(i).asText();
|
||||||
|
apiErrorStrings.add(errorString);
|
||||||
|
}
|
||||||
|
} catch(Exception e) {
|
||||||
|
// TODO: error logging
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ApiError> result = new ArrayList<>();
|
||||||
|
for(String apiErrorString : apiErrorStrings) {
|
||||||
|
ApiError apiError = null;
|
||||||
|
try {
|
||||||
|
apiError = ApiError.valueOf(apiErrorString);
|
||||||
|
} catch(IllegalArgumentException e) {
|
||||||
|
try {
|
||||||
|
int errorCodeInt = Integer.parseInt(apiErrorString);
|
||||||
|
apiError = ApiError.fromCode(errorCodeInt);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(apiError == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
result.add(apiError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContextInformation getContextInformation(Map<String, Object> extensions) {
|
||||||
|
return getContextInformation(extensions, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContextInformation getContextInformation(Map<String, Object> extensions, ContextInformation base) {
|
||||||
|
if(extensions != null) {
|
||||||
|
Map<String, Object> translationDefinitions = (Map<String, Object>)extensions.get("x-" + Constants.TRANSLATION_EXTENSION_NAME);
|
||||||
|
if(translationDefinitions != null) {
|
||||||
|
ContextInformation result = new ContextInformation();
|
||||||
|
result.path = combinePaths(base, (String)translationDefinitions.get(Constants.TRANSLATION_PATH_EXTENSION_NAME));
|
||||||
|
result.keys = getTranslationKeys(translationDefinitions);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(base != null) {
|
||||||
|
ContextInformation result = new ContextInformation();
|
||||||
|
result.path = base.path;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeApiErrorsAnnotations(Map<String, Object> extensions) {
|
||||||
|
String extensionName = Constants.API_ERRORS_EXTENSION_NAME;
|
||||||
|
removeExtension(extensions, extensionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeTranslationAnnotations(Map<String, Object> extensions) {
|
||||||
|
String extensionName = Constants.TRANSLATION_EXTENSION_NAME;
|
||||||
|
removeExtension(extensions, extensionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeExtension(Map<String, Object> extensions, String extensionName) {
|
||||||
|
if(extensions == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
extensions.remove("x-" + extensionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getTranslationKeys(Map<String, Object> translationDefinitions) {
|
||||||
|
Map<String, String> result = new HashMap<>();
|
||||||
|
|
||||||
|
for(TranslatableProperty prop : Constants.TRANSLATABLE_INFO_PROPERTIES) {
|
||||||
|
String key = (String)translationDefinitions.get(prop.keyName());
|
||||||
|
if(key != null)
|
||||||
|
result.put(prop.keyName(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String combinePaths(ContextInformation base, String path) {
|
||||||
|
String basePath = (base != null) ? base.path : null;
|
||||||
|
return ContextPaths.combinePaths(basePath, path);
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,20 @@
|
|||||||
package api;
|
package api;
|
||||||
|
|
||||||
|
import globalization.ContextPaths;
|
||||||
import globalization.Translator;
|
import globalization.Translator;
|
||||||
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
|
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.extensions.Extension;
|
||||||
|
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
|
||||||
import io.swagger.v3.oas.annotations.media.Content;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.AbstractMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
@ -49,6 +53,8 @@ public class ApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String TRANSLATION_CONTEXT_PATH = "/Api/ApiClient";
|
||||||
|
|
||||||
private static final Pattern COMMAND_PATTERN = Pattern.compile("^ *(?<method>GET|POST|PUT|PATCH|DELETE) *(?<path>.*)$");
|
private static final Pattern COMMAND_PATTERN = Pattern.compile("^ *(?<method>GET|POST|PUT|PATCH|DELETE) *(?<path>.*)$");
|
||||||
private static final Pattern HELP_COMMAND_PATTERN = Pattern.compile("^ *help *(?<command>.*)$", Pattern.CASE_INSENSITIVE);
|
private static final Pattern HELP_COMMAND_PATTERN = Pattern.compile("^ *help *(?<command>.*)$", Pattern.CASE_INSENSITIVE);
|
||||||
private static final List<Class<? extends Annotation>> HTTP_METHOD_ANNOTATIONS = Arrays.asList(
|
private static final List<Class<? extends Annotation>> HTTP_METHOD_ANNOTATIONS = Arrays.asList(
|
||||||
@ -59,8 +65,8 @@ public class ApiClient {
|
|||||||
DELETE.class
|
DELETE.class
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private final Translator translator;
|
||||||
ApiService apiService;
|
ApiService apiService;
|
||||||
private Translator translator;
|
|
||||||
List<HelpInfo> helpInfos;
|
List<HelpInfo> helpInfos;
|
||||||
|
|
||||||
public ApiClient(ApiService apiService, Translator translator) {
|
public ApiClient(ApiService apiService, Translator translator) {
|
||||||
@ -83,8 +89,6 @@ public class ApiClient {
|
|||||||
private List<HelpInfo> getHelpInfos(Iterable<Class<?>> resources) {
|
private List<HelpInfo> getHelpInfos(Iterable<Class<?>> resources) {
|
||||||
List<HelpInfo> result = new ArrayList<>();
|
List<HelpInfo> result = new ArrayList<>();
|
||||||
|
|
||||||
// TODO: need some way to realize translation from resource annotations
|
|
||||||
|
|
||||||
// scan each resource class
|
// scan each resource class
|
||||||
for (Class<?> resource : resources) {
|
for (Class<?> resource : resources) {
|
||||||
if (OpenApiResource.class.isAssignableFrom(resource)) {
|
if (OpenApiResource.class.isAssignableFrom(resource)) {
|
||||||
@ -94,18 +98,28 @@ public class ApiClient {
|
|||||||
if (resourcePath == null) {
|
if (resourcePath == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String resourcePathString = resourcePath.value();
|
String resourcePathString = resourcePath.value();
|
||||||
|
|
||||||
|
// get translation context path for resource
|
||||||
|
String resourceContextPath = "/";
|
||||||
|
OpenAPIDefinition openAPIDefinition = resource.getDeclaredAnnotation(OpenAPIDefinition.class);
|
||||||
|
if(openAPIDefinition != null)
|
||||||
|
resourceContextPath = getContextPath(openAPIDefinition.extensions());
|
||||||
|
|
||||||
// scan each method
|
// scan each method
|
||||||
for (Method method : resource.getDeclaredMethods()) {
|
for (Method method : resource.getDeclaredMethods()) {
|
||||||
Operation operationAnnotation = method.getAnnotation(Operation.class);
|
Operation operationAnnotation = method.getAnnotation(Operation.class);
|
||||||
if (operationAnnotation == null) {
|
if (operationAnnotation == null)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
String description = operationAnnotation.description();
|
String description = operationAnnotation.description();
|
||||||
|
|
||||||
|
// translate
|
||||||
|
String operationContextPath = ContextPaths.combinePaths(resourceContextPath, getContextPath(operationAnnotation.extensions()));
|
||||||
|
String operationDescriptionKey = getDescriptionTranslationKey(operationAnnotation.extensions());
|
||||||
|
if(operationDescriptionKey != null)
|
||||||
|
description = translator.translate(operationContextPath, operationDescriptionKey, description);
|
||||||
|
|
||||||
// extract responses
|
// extract responses
|
||||||
ArrayList success = new ArrayList();
|
ArrayList success = new ArrayList();
|
||||||
ArrayList errors = new ArrayList();
|
ArrayList errors = new ArrayList();
|
||||||
@ -114,6 +128,20 @@ public class ApiClient {
|
|||||||
if(StringUtils.isBlank(responseDescription))
|
if(StringUtils.isBlank(responseDescription))
|
||||||
continue; // ignore responses without description
|
continue; // ignore responses without description
|
||||||
|
|
||||||
|
// translate
|
||||||
|
String responseContextPath = ContextPaths.combinePaths(operationContextPath, getContextPath(response.extensions()));
|
||||||
|
String responseDescriptionKey = getDescriptionTranslationKey(response.extensions());
|
||||||
|
if(responseDescriptionKey != null)
|
||||||
|
responseDescription = translator.translate(responseContextPath, responseDescriptionKey, responseDescription);
|
||||||
|
|
||||||
|
String apiErrorCode = getApiErrorCode(response.extensions());
|
||||||
|
if(apiErrorCode != null) {
|
||||||
|
responseDescription = translator.translate(TRANSLATION_CONTEXT_PATH, "API error response", "(API error: ${ERROR_CODE}) ${DESCRIPTION}",
|
||||||
|
new AbstractMap.SimpleEntry<>("ERROR_CODE", apiErrorCode),
|
||||||
|
new AbstractMap.SimpleEntry<>("DESCRIPTION", responseDescription)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// try to identify response type by status code
|
// try to identify response type by status code
|
||||||
int responseCode = Integer.parseInt(response.responseCode());
|
int responseCode = Integer.parseInt(response.responseCode());
|
||||||
@ -165,6 +193,50 @@ public class ApiClient {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getApiErrorCode(Extension[] extensions) {
|
||||||
|
if(extensions == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for(Extension extension : extensions) {
|
||||||
|
if(extension.name() != null && !extension.name().isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for(ExtensionProperty prop : extension.properties()) {
|
||||||
|
if(Constants.API_ERROR_CODE_EXTENSION_NAME.equals(prop.name())) {
|
||||||
|
return prop.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getContextPath(Extension[] extensions) {
|
||||||
|
return getTranslationExtensionValue(extensions, Constants.TRANSLATION_PATH_EXTENSION_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDescriptionTranslationKey(Extension[] extensions) {
|
||||||
|
return getTranslationExtensionValue(extensions, Constants.TRANSLATION_ANNOTATION_DESCRIPTION_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTranslationExtensionValue(Extension[] extensions, String key) {
|
||||||
|
if(extensions == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for(Extension extension : extensions) {
|
||||||
|
if(!Constants.TRANSLATION_EXTENSION_NAME.equals(extension.name()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for(ExtensionProperty prop : extension.properties()) {
|
||||||
|
if(key.equals(prop.name())) {
|
||||||
|
return prop.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private String getHelpPatternForPath(String path) {
|
private String getHelpPatternForPath(String path) {
|
||||||
path = path
|
path = path
|
||||||
.replaceAll("\\.", "\\.") // escapes "." as "\."
|
.replaceAll("\\.", "\\.") // escapes "." as "\."
|
||||||
@ -205,7 +277,7 @@ public class ApiClient {
|
|||||||
|
|
||||||
match = COMMAND_PATTERN.matcher(command);
|
match = COMMAND_PATTERN.matcher(command);
|
||||||
if(!match.matches())
|
if(!match.matches())
|
||||||
return this.translator.translate(Locale.getDefault(), "ApiClient: INVALID_COMMAND", "Invalid command! \nType help to get a list of commands.");
|
return this.translator.translate(TRANSLATION_CONTEXT_PATH, "invalid command", "Invalid command! \nType 'help all' to get a list of commands.");
|
||||||
|
|
||||||
// send the command to the API service
|
// send the command to the API service
|
||||||
String method = match.group("method");
|
String method = match.group("method");
|
||||||
@ -223,13 +295,22 @@ public class ApiClient {
|
|||||||
final int status = response.getStatus();
|
final int status = response.getStatus();
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
if(status >= 400) {
|
if(status >= 400) {
|
||||||
result.append("HTTP Status ");
|
if(StringUtils.isBlank(body)) {
|
||||||
result.append(status);
|
result.append(
|
||||||
if(!StringUtils.isBlank(body)) {
|
this.translator.translate(TRANSLATION_CONTEXT_PATH, "error without body", "HTTP Status ${STATUS}",
|
||||||
result.append(": ");
|
new AbstractMap.SimpleEntry<>("STATUS", status)
|
||||||
result.append(body);
|
)
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
result.append(
|
||||||
|
this.translator.translate(TRANSLATION_CONTEXT_PATH, "error with body", "HTTP Status ${STATUS}: ${BODY}",
|
||||||
|
new AbstractMap.SimpleEntry<>("STATUS", status),
|
||||||
|
new AbstractMap.SimpleEntry<>("BODY", body)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
result.append("\nType help to get a list of commands.");
|
result.append("\n");
|
||||||
|
result.append(this.translator.translate(TRANSLATION_CONTEXT_PATH, "error footer", "Type 'help all' to get a list of commands."));
|
||||||
} else {
|
} else {
|
||||||
result.append(body);
|
result.append(body);
|
||||||
}
|
}
|
||||||
@ -240,13 +321,17 @@ public class ApiClient {
|
|||||||
builder.append(help.fullPath + "\n");
|
builder.append(help.fullPath + "\n");
|
||||||
builder.append(" " + help.description + "\n");
|
builder.append(" " + help.description + "\n");
|
||||||
if(help.success != null && help.success.size() > 0) {
|
if(help.success != null && help.success.size() > 0) {
|
||||||
builder.append(" On success returns:\n");
|
builder.append(" ");
|
||||||
|
builder.append(this.translator.translate(TRANSLATION_CONTEXT_PATH, "help: success responses", "On success returns:"));
|
||||||
|
builder.append("\n");
|
||||||
for(String content : help.success) {
|
for(String content : help.success) {
|
||||||
builder.append(" " + content + "\n");
|
builder.append(" " + content + "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(help.errors != null && help.errors.size() > 0) {
|
if(help.errors != null && help.errors.size() > 0) {
|
||||||
builder.append(" On failure returns:\n");
|
builder.append(" ");
|
||||||
|
builder.append(this.translator.translate(TRANSLATION_CONTEXT_PATH, "help: failure responses", "On failure returns:"));
|
||||||
|
builder.append("\n");
|
||||||
for(String content : help.errors) {
|
for(String content : help.errors) {
|
||||||
builder.append(" " + content + "\n");
|
builder.append(" " + content + "\n");
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,15 @@ public enum ApiError {
|
|||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ApiError fromCode(int code) {
|
||||||
|
for(ApiError apiError : ApiError.values()) {
|
||||||
|
if(apiError.code == code)
|
||||||
|
return apiError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
int getCode() {
|
int getCode() {
|
||||||
return this.code;
|
return this.code;
|
||||||
}
|
}
|
||||||
@ -117,4 +126,5 @@ public enum ApiError {
|
|||||||
int getStatus() {
|
int getStatus() {
|
||||||
return this.status;
|
return this.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -101,16 +101,16 @@ public class ApiErrorFactory {
|
|||||||
// this.errorMessages.put(ApiError.NAME_FOR_SALE, createErrorMessageEntry(ApiError.NAME_FOR_SALE, NameResult.NAME_FOR_SALE.getStatusMessage()));
|
// this.errorMessages.put(ApiError.NAME_FOR_SALE, createErrorMessageEntry(ApiError.NAME_FOR_SALE, NameResult.NAME_FOR_SALE.getStatusMessage()));
|
||||||
// this.errorMessages.put(ApiError.NAME_WITH_SPACE, createErrorMessageEntry(ApiError.NAME_WITH_SPACE, NameResult.NAME_WITH_SPACE.getStatusMessage()));
|
// this.errorMessages.put(ApiError.NAME_WITH_SPACE, createErrorMessageEntry(ApiError.NAME_WITH_SPACE, NameResult.NAME_WITH_SPACE.getStatusMessage()));
|
||||||
//AT
|
//AT
|
||||||
this.errorMessages.put(ApiError.INVALID_CREATION_BYTES, createErrorMessageEntry(ApiError.INVALID_CREATION_BYTES, "error in creation bytes"));
|
|
||||||
// TODO
|
// TODO
|
||||||
// this.errorMessages.put(ApiError.INVALID_DESC_LENGTH, createErrorMessageEntry(ApiError.INVALID_DESC_LENGTH,
|
// this.errorMessages.put(ApiError.INVALID_DESC_LENGTH, createErrorMessageEntry(ApiError.INVALID_DESC_LENGTH,
|
||||||
// "invalid description length. max length ${MAX_LENGTH}",
|
// "invalid description length. max length ${MAX_LENGTH}",
|
||||||
// new AbstractMap.SimpleEntry<String, Object>("MAX_LENGTH", AT_Constants.DESC_MAX_LENGTH));
|
// new AbstractMap.SimpleEntry<String, Object>("MAX_LENGTH", AT_Constants.DESC_MAX_LENGTH));
|
||||||
this.errorMessages.put(ApiError.EMPTY_CODE, createErrorMessageEntry(ApiError.EMPTY_CODE, "code is empty"));
|
this.errorMessages.put(ApiError.EMPTY_CODE, createErrorMessageEntry(ApiError.EMPTY_CODE, "code is empty"));
|
||||||
this.errorMessages.put(ApiError.DATA_SIZE, createErrorMessageEntry(ApiError.DATA_SIZE, "invalid data length"));
|
this.errorMessages.put(ApiError.DATA_SIZE, createErrorMessageEntry(ApiError.DATA_SIZE, "invalid data length"));
|
||||||
|
this.errorMessages.put(ApiError.NULL_PAGES, createErrorMessageEntry(ApiError.NULL_PAGES, "invalid pages"));
|
||||||
this.errorMessages.put(ApiError.INVALID_TYPE_LENGTH, createErrorMessageEntry(ApiError.INVALID_TYPE_LENGTH, "invalid type length"));
|
this.errorMessages.put(ApiError.INVALID_TYPE_LENGTH, createErrorMessageEntry(ApiError.INVALID_TYPE_LENGTH, "invalid type length"));
|
||||||
this.errorMessages.put(ApiError.INVALID_TAGS_LENGTH, createErrorMessageEntry(ApiError.INVALID_TAGS_LENGTH, "invalid tags length"));
|
this.errorMessages.put(ApiError.INVALID_TAGS_LENGTH, createErrorMessageEntry(ApiError.INVALID_TAGS_LENGTH, "invalid tags length"));
|
||||||
this.errorMessages.put(ApiError.NULL_PAGES, createErrorMessageEntry(ApiError.NULL_PAGES, "invalid pages"));
|
this.errorMessages.put(ApiError.INVALID_CREATION_BYTES, createErrorMessageEntry(ApiError.INVALID_CREATION_BYTES, "error in creation bytes"));
|
||||||
|
|
||||||
//BLOG
|
//BLOG
|
||||||
this.errorMessages.put(ApiError.BODY_EMPTY, createErrorMessageEntry(ApiError.BODY_EMPTY, "invalid body it must not be empty"));
|
this.errorMessages.put(ApiError.BODY_EMPTY, createErrorMessageEntry(ApiError.BODY_EMPTY, "invalid body it must not be empty"));
|
||||||
@ -147,13 +147,17 @@ public class ApiErrorFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ErrorMessageEntry createErrorMessageEntry(ApiError errorCode, String defaultTemplate, AbstractMap.SimpleEntry<String, Object>... templateValues) {
|
private ErrorMessageEntry createErrorMessageEntry(ApiError errorCode, String defaultTemplate, AbstractMap.SimpleEntry<String, Object>... templateValues) {
|
||||||
String templateKey = String.format("%s: ApiError.%s message", ApiErrorFactory.class.getSimpleName(), errorCode.name());
|
String templateKey = String.format(Constants.APIERROR_KEY, errorCode.name());
|
||||||
return new ErrorMessageEntry(templateKey, defaultTemplate, templateValues);
|
return new ErrorMessageEntry(templateKey, defaultTemplate, templateValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getErrorMessage(ApiError error) {
|
||||||
|
return getErrorMessage(null, error);
|
||||||
|
}
|
||||||
|
|
||||||
public String getErrorMessage(Locale locale, ApiError error) {
|
public String getErrorMessage(Locale locale, ApiError error) {
|
||||||
ErrorMessageEntry errorMessage = this.errorMessages.get(error);
|
ErrorMessageEntry errorMessage = this.errorMessages.get(error);
|
||||||
String message = this.translator.translate(locale, errorMessage.templateKey, errorMessage.defaultTemplate, errorMessage.templateValues);
|
String message = this.translator.translate(locale, Constants.APIERROR_CONTEXT_PATH, errorMessage.templateKey, errorMessage.defaultTemplate, errorMessage.templateValues);
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,8 +170,7 @@ public class ApiErrorFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ApiException createError(ApiError error, Throwable throwable) {
|
public ApiException createError(ApiError error, Throwable throwable) {
|
||||||
Locale locale = Locale.ENGLISH; // default locale
|
return createError(null, error, throwable);
|
||||||
return createError(locale, error, throwable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApiException createError(Locale locale, ApiError error, Throwable throwable) {
|
public ApiException createError(Locale locale, ApiError error, Throwable throwable) {
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
package api;
|
package api;
|
||||||
|
|
||||||
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
|
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
|
||||||
|
import java.io.File;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
|
||||||
|
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.handler.InetAccessHandler;
|
import org.eclipse.jetty.server.handler.InetAccessHandler;
|
||||||
|
import org.eclipse.jetty.servlet.DefaultServlet;
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
import org.glassfish.jersey.server.ResourceConfig;
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
@ -21,8 +25,10 @@ public class ApiService {
|
|||||||
public ApiService() {
|
public ApiService() {
|
||||||
// resources to register
|
// resources to register
|
||||||
this.resources = new HashSet<Class<?>>();
|
this.resources = new HashSet<Class<?>>();
|
||||||
|
this.resources.add(AddressesResource.class);
|
||||||
this.resources.add(BlocksResource.class);
|
this.resources.add(BlocksResource.class);
|
||||||
this.resources.add(OpenApiResource.class); // swagger
|
this.resources.add(OpenApiResource.class); // swagger
|
||||||
|
this.resources.add(AnnotationPostProcessor.class); // for API resource annotations
|
||||||
ResourceConfig config = new ResourceConfig(this.resources);
|
ResourceConfig config = new ResourceConfig(this.resources);
|
||||||
|
|
||||||
// create RPC server
|
// create RPC server
|
||||||
@ -35,16 +41,30 @@ public class ApiService {
|
|||||||
}
|
}
|
||||||
this.server.setHandler(accessHandler);
|
this.server.setHandler(accessHandler);
|
||||||
|
|
||||||
|
// url rewriting
|
||||||
|
RewriteHandler rewriteHandler = new RewriteHandler();
|
||||||
|
accessHandler.setHandler(rewriteHandler);
|
||||||
|
|
||||||
// context
|
// context
|
||||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
||||||
context.setContextPath("/");
|
context.setContextPath("/");
|
||||||
accessHandler.setHandler(context);
|
rewriteHandler.setHandler(context);
|
||||||
|
|
||||||
// API servlet
|
// API servlet
|
||||||
ServletContainer container = new ServletContainer(config);
|
ServletContainer container = new ServletContainer(config);
|
||||||
ServletHolder apiServlet = new ServletHolder(container);
|
ServletHolder apiServlet = new ServletHolder(container);
|
||||||
apiServlet.setInitOrder(1);
|
apiServlet.setInitOrder(1);
|
||||||
context.addServlet(apiServlet, "/*");
|
context.addServlet(apiServlet, "/*");
|
||||||
|
|
||||||
|
// Swagger-UI static content
|
||||||
|
ClassLoader loader = this.getClass().getClassLoader();
|
||||||
|
File swaggerUIResourceLocation = new File(loader.getResource("resources/swagger-ui/").getFile());
|
||||||
|
ServletHolder swaggerUIServlet = new ServletHolder("static-swagger-ui", DefaultServlet.class);
|
||||||
|
swaggerUIServlet.setInitParameter("resourceBase", swaggerUIResourceLocation.getAbsolutePath());
|
||||||
|
swaggerUIServlet.setInitParameter("dirAllowed","true");
|
||||||
|
swaggerUIServlet.setInitParameter("pathInfoOnly","true");
|
||||||
|
context.addServlet(swaggerUIServlet,"/api-documentation/*");
|
||||||
|
rewriteHandler.addRule(new RedirectPatternRule("/api-documentation", "/api-documentation/index.html")); // redirect to swagger ui start page
|
||||||
}
|
}
|
||||||
|
|
||||||
//XXX: replace singleton pattern by dependency injection?
|
//XXX: replace singleton pattern by dependency injection?
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
package api;
|
package api;
|
||||||
|
|
||||||
|
import data.block.BlockData;
|
||||||
import globalization.Translator;
|
import globalization.Translator;
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.extensions.Extension;
|
||||||
|
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
|
||||||
import io.swagger.v3.oas.annotations.media.Content;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
@ -13,12 +18,19 @@ import javax.ws.rs.PathParam;
|
|||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import qora.block.Block;
|
||||||
|
|
||||||
import repository.Repository;
|
import repository.Repository;
|
||||||
import repository.RepositoryManager;
|
import repository.RepositoryManager;
|
||||||
|
import utils.Base58;
|
||||||
|
|
||||||
@Path("blocks")
|
@Path("blocks")
|
||||||
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
||||||
|
@OpenAPIDefinition(
|
||||||
|
extensions = @Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="/Api/BlocksResource")
|
||||||
|
})
|
||||||
|
)
|
||||||
public class BlocksResource {
|
public class BlocksResource {
|
||||||
|
|
||||||
@Context
|
@Context
|
||||||
@ -27,240 +39,366 @@ public class BlocksResource {
|
|||||||
private ApiErrorFactory apiErrorFactory;
|
private ApiErrorFactory apiErrorFactory;
|
||||||
|
|
||||||
public BlocksResource() {
|
public BlocksResource() {
|
||||||
this(new ApiErrorFactory(new Translator()));
|
this(new ApiErrorFactory(Translator.getInstance()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlocksResource(ApiErrorFactory apiErrorFactory) {
|
public BlocksResource(ApiErrorFactory apiErrorFactory) {
|
||||||
this.apiErrorFactory = apiErrorFactory;
|
this.apiErrorFactory = apiErrorFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
|
||||||
@Operation(
|
|
||||||
description = "Returns an array of the 50 last blocks generated by your accounts",
|
|
||||||
responses = {
|
|
||||||
@ApiResponse(
|
|
||||||
description = "The blocks"
|
|
||||||
//content = @Content(schema = @Schema(implementation = ???))
|
|
||||||
),
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "422",
|
|
||||||
description = "Error: 201 - Wallet does not exist",
|
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
public String getBlocks() {
|
|
||||||
Security.checkApiCallAllowed("GET blocks", request);
|
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/address/{address}")
|
|
||||||
@Operation(
|
|
||||||
description = "Returns an array of the 50 last blocks generated by a specific address in your wallet",
|
|
||||||
responses = {
|
|
||||||
@ApiResponse(
|
|
||||||
description = "The blocks"
|
|
||||||
//content = @Content(schema = @Schema(implementation = ???))
|
|
||||||
),
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "400",
|
|
||||||
description = "102 - Invalid address",
|
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
|
||||||
),
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "422",
|
|
||||||
description = "201 - Wallet does not exist",
|
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
|
||||||
),
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "422",
|
|
||||||
description = "202 - Address does not exist in wallet",
|
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
public String getBlocks(@PathParam("address") String address) {
|
|
||||||
Security.checkApiCallAllowed("GET blocks/address/" + address, request);
|
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/{signature}")
|
@Path("/{signature}")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Returns the block that matches the given signature",
|
description = "returns the block that matches the given signature",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET signature"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The block"
|
description = "the block",
|
||||||
//content = @Content(schema = @Schema(implementation = ???))
|
content = @Content(schema = @Schema(implementation = BlockData.class)),
|
||||||
),
|
extensions = {
|
||||||
@ApiResponse(
|
@Extension(name = "translation", properties = {
|
||||||
responseCode = "400",
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
description = "101 - Invalid signature",
|
})
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
}
|
||||||
),
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "422",
|
|
||||||
description = "301 - Block does not exist",
|
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public String getBlock(@PathParam("signature") String signature) {
|
public BlockData getBlock(@PathParam("signature") String signature) {
|
||||||
Security.checkApiCallAllowed("GET blocks", request);
|
Security.checkApiCallAllowed("GET blocks", request);
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
// decode signature
|
||||||
|
byte[] signatureBytes;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
signatureBytes = Base58.decode(signature);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
||||||
|
|
||||||
|
// check if block exists
|
||||||
|
if(blockData == null)
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
|
return blockData;
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/first")
|
@Path("/first")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Returns the genesis block",
|
description = "returns the genesis block",
|
||||||
|
extensions = @Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET first"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The block"
|
description = "the block",
|
||||||
//content = @Content(schema = @Schema(implementation = ???))
|
content = @Content(schema = @Schema(implementation = BlockData.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public String getFirstBlock() {
|
public BlockData getFirstBlock() {
|
||||||
Security.checkApiCallAllowed("GET blocks/first", request);
|
Security.checkApiCallAllowed("GET blocks/first", request);
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
BlockData blockData = repository.getBlockRepository().fromHeight(1);
|
||||||
|
return blockData;
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/last")
|
@Path("/last")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Returns the last valid block",
|
description = "returns the last valid block",
|
||||||
|
extensions = @Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET last"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The block"
|
description = "the block",
|
||||||
//content = @Content(schema = @Schema(implementation = ???))
|
content = @Content(schema = @Schema(implementation = BlockData.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public String getLastBlock() {
|
public BlockData getLastBlock() {
|
||||||
Security.checkApiCallAllowed("GET blocks/last", request);
|
Security.checkApiCallAllowed("GET blocks/last", request);
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
||||||
|
return blockData;
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/child/{signature}")
|
@Path("/child/{signature}")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Returns the child block of the block that matches the given signature",
|
description = "returns the child block of the block that matches the given signature",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET child:signature"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The block"
|
description = "the block",
|
||||||
//content = @Content(schema = @Schema(implementation = ???))
|
content = @Content(schema = @Schema(implementation = BlockData.class)),
|
||||||
),
|
extensions = {
|
||||||
@ApiResponse(
|
@Extension(name = "translation", properties = {
|
||||||
responseCode = "400",
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
description = "101 - Invalid signature",
|
})
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
}
|
||||||
),
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "422",
|
|
||||||
description = "301 - Block does not exist",
|
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public String getChild(@PathParam("signature") String signature) {
|
public BlockData getChild(@PathParam("signature") String signature) {
|
||||||
Security.checkApiCallAllowed("GET blocks/child", request);
|
Security.checkApiCallAllowed("GET blocks/child", request);
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
// decode signature
|
||||||
|
byte[] signatureBytes;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
signatureBytes = Base58.decode(signature);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
||||||
|
|
||||||
|
// check if block exists
|
||||||
|
if(blockData == null)
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
|
int height = blockData.getHeight();
|
||||||
|
BlockData childBlockData = repository.getBlockRepository().fromHeight(height + 1);
|
||||||
|
|
||||||
|
// check if child exists
|
||||||
|
if(childBlockData == null)
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
|
return childBlockData;
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/generatingbalance")
|
@Path("/generatingbalance")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Calculates the generating balance of the block that will follow the last block",
|
description = "calculates the generating balance of the block that will follow the last block",
|
||||||
|
extensions = @Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET generatingbalance"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The generating balance",
|
description = "the generating balance",
|
||||||
content = @Content(schema = @Schema(implementation = long.class))
|
content = @Content(schema = @Schema(implementation = BigDecimal.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public long getGeneratingBalance() {
|
public BigDecimal getGeneratingBalance() {
|
||||||
Security.checkApiCallAllowed("GET blocks/generatingbalance", request);
|
Security.checkApiCallAllowed("GET blocks/generatingbalance", request);
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
||||||
|
Block block = new Block(repository, blockData);
|
||||||
|
return block.calcNextBlockGeneratingBalance();
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/generatingbalance/{signature}")
|
@Path("/generatingbalance/{signature}")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Calculates the generating balance of the block that will follow the block that matches the signature",
|
description = "calculates the generating balance of the block that will follow the block that matches the signature",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET generatingbalance:signature"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The block",
|
description = "the block",
|
||||||
content = @Content(schema = @Schema(implementation = long.class))
|
content = @Content(schema = @Schema(implementation = BigDecimal.class)),
|
||||||
),
|
extensions = {
|
||||||
@ApiResponse(
|
@Extension(name = "translation", properties = {
|
||||||
responseCode = "400",
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
description = "101 - Invalid signature",
|
})
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
}
|
||||||
),
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "422",
|
|
||||||
description = "301 - Block does not exist",
|
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public long getGeneratingBalance(@PathParam("signature") String signature) {
|
public BigDecimal getGeneratingBalance(@PathParam("signature") String signature) {
|
||||||
Security.checkApiCallAllowed("GET blocks/generatingbalance", request);
|
Security.checkApiCallAllowed("GET blocks/generatingbalance", request);
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
// decode signature
|
||||||
|
byte[] signatureBytes;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
signatureBytes = Base58.decode(signature);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
||||||
|
|
||||||
|
// check if block exists
|
||||||
|
if(blockData == null)
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
|
Block block = new Block(repository, blockData);
|
||||||
|
return block.calcNextBlockGeneratingBalance();
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/time")
|
@Path("/time")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Calculates the time it should take for the network to generate the next block",
|
description = "calculates the time it should take for the network to generate the next block",
|
||||||
|
extensions = @Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET time"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The time", // in seconds?
|
description = "the time in seconds", // in seconds?
|
||||||
content = @Content(schema = @Schema(implementation = long.class))
|
content = @Content(schema = @Schema(implementation = long.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public long getTimePerBlock() {
|
public long getTimePerBlock() {
|
||||||
Security.checkApiCallAllowed("GET blocks/time", request);
|
Security.checkApiCallAllowed("GET blocks/time", request);
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
||||||
|
return Block.calcForgingDelay(blockData.getGeneratingBalance());
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/time/{generatingbalance}")
|
@Path("/time/{generatingbalance}")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Calculates the time it should take for the network to generate blocks when the current generating balance in the network is the specified generating balance",
|
description = "calculates the time it should take for the network to generate blocks when the current generating balance in the network is the specified generating balance",
|
||||||
|
extensions = @Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET time:generatingbalance"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The time", // in seconds?
|
description = "the time", // in seconds?
|
||||||
content = @Content(schema = @Schema(implementation = long.class))
|
content = @Content(schema = @Schema(implementation = long.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public String getTimePerBlock(@PathParam("generating") long generatingbalance) {
|
public long getTimePerBlock(@PathParam("generating") BigDecimal generatingbalance) {
|
||||||
Security.checkApiCallAllowed("GET blocks/time", request);
|
Security.checkApiCallAllowed("GET blocks/time", request);
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
return Block.calcForgingDelay(generatingbalance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/height")
|
@Path("/height")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Returns the block height of the last block.",
|
description = "returns the block height of the last block.",
|
||||||
|
extensions = @Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET height"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The height",
|
description = "the height",
|
||||||
content = @Content(schema = @Schema(implementation = int.class))
|
content = @Content(schema = @Schema(implementation = int.class)),
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
|
})
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -269,7 +407,10 @@ public class BlocksResource {
|
|||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
return repository.getBlockRepository().getBlockchainHeight();
|
return repository.getBlockRepository().getBlockchainHeight();
|
||||||
} catch (Exception e) {
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,49 +418,99 @@ public class BlocksResource {
|
|||||||
@GET
|
@GET
|
||||||
@Path("/height/{signature}")
|
@Path("/height/{signature}")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Returns the block height of the block that matches the given signature",
|
description = "returns the block height of the block that matches the given signature",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET height:signature"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The height",
|
description = "the height",
|
||||||
content = @Content(schema = @Schema(implementation = int.class))
|
content = @Content(schema = @Schema(implementation = int.class)),
|
||||||
),
|
extensions = {
|
||||||
@ApiResponse(
|
@Extension(name = "translation", properties = {
|
||||||
responseCode = "400",
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
description = "101 - Invalid signature",
|
})
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
}
|
||||||
),
|
|
||||||
@ApiResponse(
|
|
||||||
responseCode = "422",
|
|
||||||
description = "301 - Block does not exist",
|
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public int getHeight(@PathParam("signature") String signature) {
|
public int getHeight(@PathParam("signature") String signature) {
|
||||||
Security.checkApiCallAllowed("GET blocks/height", request);
|
Security.checkApiCallAllowed("GET blocks/height", request);
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
// decode signature
|
||||||
|
byte[] signatureBytes;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
signatureBytes = Base58.decode(signature);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
||||||
|
|
||||||
|
// check if block exists
|
||||||
|
if(blockData == null)
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
|
return blockData.getHeight();
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/byheight/{height}")
|
@Path("/byheight/{height}")
|
||||||
@Operation(
|
@Operation(
|
||||||
description = "Returns the block whith given height",
|
description = "returns the block whith given height",
|
||||||
|
extensions = {
|
||||||
|
@Extension(name = "translation", properties = {
|
||||||
|
@ExtensionProperty(name="path", value="GET byheight:height"),
|
||||||
|
@ExtensionProperty(name="description.key", value="operation:description")
|
||||||
|
}),
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name="apiErrors", value="[\"BLOCK_NO_EXISTS\"]", parseValue = true),
|
||||||
|
})
|
||||||
|
},
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "The block"
|
description = "the block",
|
||||||
//content = @Content(schema = @Schema(implementation = ???))
|
content = @Content(schema = @Schema(implementation = BlockData.class)),
|
||||||
),
|
extensions = {
|
||||||
@ApiResponse(
|
@Extension(name = "translation", properties = {
|
||||||
responseCode = "422",
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
||||||
description = "301 - Block does not exist",
|
})
|
||||||
content = @Content(schema = @Schema(implementation = ApiErrorMessage.class))
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public String getbyHeight(@PathParam("height") int height) {
|
public BlockData getbyHeight(@PathParam("height") int height) {
|
||||||
Security.checkApiCallAllowed("GET blocks/byheight", request);
|
Security.checkApiCallAllowed("GET blocks/byheight", request);
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
BlockData blockData = repository.getBlockRepository().fromHeight(height);
|
||||||
|
|
||||||
|
// check if block exists
|
||||||
|
if(blockData == null)
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
|
return blockData;
|
||||||
|
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
76
src/api/Constants.java
Normal file
76
src/api/Constants.java
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.models.Operation;
|
||||||
|
import io.swagger.v3.oas.models.PathItem;
|
||||||
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
import io.swagger.v3.oas.models.responses.ApiResponse;
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class Constants {
|
||||||
|
public static final String APIERROR_CONTEXT_PATH = "/Api";
|
||||||
|
public static final String APIERROR_KEY = "ApiError/%s";
|
||||||
|
|
||||||
|
public static final String TRANSLATION_EXTENSION_NAME = "translation";
|
||||||
|
public static final String TRANSLATION_PATH_EXTENSION_NAME = "path";
|
||||||
|
|
||||||
|
public static final String TRANSLATION_ANNOTATION_DESCRIPTION_KEY = "description.key";
|
||||||
|
public static final String TRANSLATION_ANNOTATION_SUMMARY_KEY = "summary.key";
|
||||||
|
public static final String TRANSLATION_ANNOTATION_TITLE_KEY = "title.key";
|
||||||
|
public static final String TRANSLATION_ANNOTATION_TERMS_OF_SERVICE_KEY = "termsOfService.key";
|
||||||
|
|
||||||
|
public static final String API_ERRORS_EXTENSION_NAME = "apiErrors";
|
||||||
|
public static final String API_ERROR_CODE_EXTENSION_NAME = "apiErrorCode";
|
||||||
|
|
||||||
|
public static final List<TranslatableProperty<Info>> TRANSLATABLE_INFO_PROPERTIES = asList(
|
||||||
|
new TranslatableProperty<Info>() {
|
||||||
|
@Override public String keyName() { return TRANSLATION_ANNOTATION_DESCRIPTION_KEY; }
|
||||||
|
@Override public void setValue(Info item, String translation) { item.setDescription(translation); }
|
||||||
|
@Override public String getValue(Info item) { return item.getDescription(); }
|
||||||
|
},
|
||||||
|
new TranslatableProperty<Info>() {
|
||||||
|
@Override public String keyName() { return TRANSLATION_ANNOTATION_TITLE_KEY; }
|
||||||
|
@Override public void setValue(Info item, String translation) { item.setTitle(translation); }
|
||||||
|
@Override public String getValue(Info item) { return item.getTitle(); }
|
||||||
|
},
|
||||||
|
new TranslatableProperty<Info>() {
|
||||||
|
@Override public String keyName() { return TRANSLATION_ANNOTATION_TERMS_OF_SERVICE_KEY; }
|
||||||
|
@Override public void setValue(Info item, String translation) { item.setTermsOfService(translation); }
|
||||||
|
@Override public String getValue(Info item) { return item.getTermsOfService(); }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final List<TranslatableProperty<PathItem>> TRANSLATABLE_PATH_ITEM_PROPERTIES = asList(
|
||||||
|
new TranslatableProperty<PathItem>() {
|
||||||
|
@Override public String keyName() { return TRANSLATION_ANNOTATION_DESCRIPTION_KEY; }
|
||||||
|
@Override public void setValue(PathItem item, String translation) { item.setDescription(translation); }
|
||||||
|
@Override public String getValue(PathItem item) { return item.getDescription(); }
|
||||||
|
},
|
||||||
|
new TranslatableProperty<PathItem>() {
|
||||||
|
@Override public String keyName() { return TRANSLATION_ANNOTATION_SUMMARY_KEY; }
|
||||||
|
@Override public void setValue(PathItem item, String translation) { item.setSummary(translation); }
|
||||||
|
@Override public String getValue(PathItem item) { return item.getSummary(); }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final List<TranslatableProperty<Operation>> TRANSLATABLE_OPERATION_PROPERTIES = asList(
|
||||||
|
new TranslatableProperty<Operation>() {
|
||||||
|
@Override public String keyName() { return TRANSLATION_ANNOTATION_DESCRIPTION_KEY; }
|
||||||
|
@Override public void setValue(Operation item, String translation) { item.setDescription(translation); }
|
||||||
|
@Override public String getValue(Operation item) { return item.getDescription(); }
|
||||||
|
},
|
||||||
|
new TranslatableProperty<Operation>() {
|
||||||
|
@Override public String keyName() { return TRANSLATION_ANNOTATION_SUMMARY_KEY; }
|
||||||
|
@Override public void setValue(Operation item, String translation) { item.setSummary(translation); }
|
||||||
|
@Override public String getValue(Operation item) { return item.getSummary(); }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final List<TranslatableProperty<ApiResponse>> TRANSLATABLE_API_RESPONSE_PROPERTIES = asList(
|
||||||
|
new TranslatableProperty<ApiResponse>() {
|
||||||
|
@Override public String keyName() { return TRANSLATION_ANNOTATION_DESCRIPTION_KEY; }
|
||||||
|
@Override public void setValue(ApiResponse item, String translation) { item.setDescription(translation); }
|
||||||
|
@Override public String getValue(ApiResponse item) { return item.getDescription(); }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
7
src/api/TranslatableProperty.java
Normal file
7
src/api/TranslatableProperty.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package api;
|
||||||
|
|
||||||
|
interface TranslatableProperty<T> {
|
||||||
|
public String keyName();
|
||||||
|
public void setValue(T item, String translation);
|
||||||
|
public String getValue(T item);
|
||||||
|
}
|
@ -3,8 +3,9 @@ package data.block;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
public class BlockData {
|
public class BlockData implements Serializable {
|
||||||
|
|
||||||
private byte[] signature;
|
private byte[] signature;
|
||||||
private int version;
|
private int version;
|
||||||
@ -20,6 +21,8 @@ public class BlockData {
|
|||||||
private int atCount;
|
private int atCount;
|
||||||
private BigDecimal atFees;
|
private BigDecimal atFees;
|
||||||
|
|
||||||
|
private BlockData() {} // necessary for JAX-RS serialization
|
||||||
|
|
||||||
public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp,
|
public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp,
|
||||||
BigDecimal generatingBalance, byte[] generatorPublicKey, byte[] generatorSignature, int atCount, BigDecimal atFees) {
|
BigDecimal generatingBalance, byte[] generatorPublicKey, byte[] generatorSignature, int atCount, BigDecimal atFees) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
33
src/globalization/ContextPaths.java
Normal file
33
src/globalization/ContextPaths.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package globalization;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class ContextPaths {
|
||||||
|
|
||||||
|
public static boolean isValidKey(String value) {
|
||||||
|
return !value.contains("/") && !ContextPaths.containsParentReference(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean containsParentReference(String value) {
|
||||||
|
for(String part : value.split("/")) {
|
||||||
|
if(part.equalsIgnoreCase(".."))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String combinePaths(String left, String right) {
|
||||||
|
left = (left != null) ? left : "";
|
||||||
|
right = (right != null) ? right : "";
|
||||||
|
return Paths.get("/", left, right).normalize().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getParent(String path) {
|
||||||
|
return combinePaths(path, "..");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isRoot(String path) {
|
||||||
|
return path.equals("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
32
src/globalization/TranslationEntry.java
Normal file
32
src/globalization/TranslationEntry.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package globalization;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class TranslationEntry {
|
||||||
|
private Locale locale;
|
||||||
|
private String path;
|
||||||
|
private String template;
|
||||||
|
|
||||||
|
public TranslationEntry(Locale locale, String path, String template) {
|
||||||
|
this.locale = locale;
|
||||||
|
this.path = path;
|
||||||
|
this.template = template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locale locale() {
|
||||||
|
return this.locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String path() {
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String template() {
|
||||||
|
return this.template;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("{locale: '%s', path: '%s', template: '%s'}", this.locale, this.path, this.template);
|
||||||
|
}
|
||||||
|
}
|
252
src/globalization/TranslationXmlStreamReader.java
Normal file
252
src/globalization/TranslationXmlStreamReader.java
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package globalization;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
|
|
||||||
|
import javax.xml.stream.XMLEventReader;
|
||||||
|
import javax.xml.stream.XMLInputFactory;
|
||||||
|
import javax.xml.stream.XMLStreamException;
|
||||||
|
import javax.xml.stream.events.*;
|
||||||
|
import org.apache.commons.text.StringEscapeUtils;
|
||||||
|
|
||||||
|
public class TranslationXmlStreamReader {
|
||||||
|
|
||||||
|
private class State {
|
||||||
|
|
||||||
|
public final Locale locale;
|
||||||
|
public final String path;
|
||||||
|
|
||||||
|
public State(Locale locale, String path) {
|
||||||
|
this.locale = locale;
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String LOCALIZATION_TAG_NAME = "localization";
|
||||||
|
|
||||||
|
private static final String CONTEXT_TAG_NAME = "context";
|
||||||
|
private static final String CONTEXT_LOCALE_ATTRIBUTE_NAME = "locale";
|
||||||
|
private static final String CONTEXT_PATH_ATTRIBUTE_NAME = "path";
|
||||||
|
|
||||||
|
private static final String TRANSLATION_TAG_NAME = "translation";
|
||||||
|
private static final String TRANSLATION_KEY_ATTRIBUTE_NAME = "key";
|
||||||
|
private static final String TRANSLATION_TEMPLATE_ATTRIBUTE_NAME = "template";
|
||||||
|
|
||||||
|
public Iterable<TranslationEntry> ReadFrom(InputStream stream) throws XMLStreamException {
|
||||||
|
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
|
||||||
|
XMLEventReader eventReader = inputFactory.createXMLEventReader(stream);
|
||||||
|
|
||||||
|
XMLEvent element = eventReader.nextEvent();
|
||||||
|
if(!element.isStartDocument())
|
||||||
|
throw new javax.xml.stream.XMLStreamException("XML declaration <?xml ... ?> must be first in the document");
|
||||||
|
|
||||||
|
State state = new State(Locale.forLanguageTag("default"), "/");
|
||||||
|
|
||||||
|
List<TranslationEntry> result = new ArrayList<>();
|
||||||
|
if (eventReader.hasNext())
|
||||||
|
{
|
||||||
|
XMLEvent event = eventReader.nextTag();
|
||||||
|
if (isStartElement(event, LOCALIZATION_TAG_NAME))
|
||||||
|
{
|
||||||
|
processLocalization(eventReader, (StartElement)event, state, result);
|
||||||
|
} else {
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected element: " + event.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (eventReader.hasNext())
|
||||||
|
{
|
||||||
|
XMLEvent event = eventReader.nextEvent();
|
||||||
|
switch(event.getEventType()) {
|
||||||
|
case XMLEvent.COMMENT:
|
||||||
|
break;
|
||||||
|
case XMLEvent.CHARACTERS:
|
||||||
|
if(!event.asCharacters().isIgnorableWhiteSpace())
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected content after end of root element: " + event.toString());
|
||||||
|
break;
|
||||||
|
case XMLEvent.END_DOCUMENT:
|
||||||
|
return result;
|
||||||
|
default:
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected content after end of root element: " + event.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new javax.xml.stream.XMLStreamException("End of document not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processLocalization(XMLEventReader eventReader, StartElement element, State state, List<TranslationEntry> result) throws XMLStreamException {
|
||||||
|
assureStartElement(element, LOCALIZATION_TAG_NAME);
|
||||||
|
|
||||||
|
Iterator<Attribute> attributes = element.getAttributes();
|
||||||
|
while (attributes.hasNext())
|
||||||
|
{
|
||||||
|
Attribute attribute = attributes.next();
|
||||||
|
QName name = attribute.getName();
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected attribute: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLEvent event;
|
||||||
|
while(!(event = eventReader.nextTag()).isEndElement()) {
|
||||||
|
if(event.isStartElement()) {
|
||||||
|
StartElement childElement = (StartElement)event;
|
||||||
|
switch(childElement.getName().toString()) {
|
||||||
|
case CONTEXT_TAG_NAME:
|
||||||
|
processContext(eventReader, childElement, state, result);
|
||||||
|
break;
|
||||||
|
case TRANSLATION_TAG_NAME:
|
||||||
|
processTranslation(eventReader, childElement, state, result);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected element: " + event.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected content: " + event.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assureEndElement(event, LOCALIZATION_TAG_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processContext(XMLEventReader eventReader, StartElement element, State state, List<TranslationEntry> result) throws XMLStreamException {
|
||||||
|
assureStartElement(element, CONTEXT_TAG_NAME);
|
||||||
|
|
||||||
|
Locale locale = state.locale;
|
||||||
|
String contextPath = state.path;
|
||||||
|
|
||||||
|
Iterator<Attribute> attributes = element.getAttributes();
|
||||||
|
while (attributes.hasNext())
|
||||||
|
{
|
||||||
|
Attribute attribute = attributes.next();
|
||||||
|
QName name = attribute.getName();
|
||||||
|
String value = attribute.getValue();
|
||||||
|
switch(name.toString()) {
|
||||||
|
case CONTEXT_LOCALE_ATTRIBUTE_NAME:
|
||||||
|
locale = Locale.forLanguageTag(value);
|
||||||
|
break;
|
||||||
|
case CONTEXT_PATH_ATTRIBUTE_NAME:
|
||||||
|
assureIsValidPathExtension(value);
|
||||||
|
contextPath = ContextPaths.combinePaths(contextPath, value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected attribute: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = new State(locale, contextPath);
|
||||||
|
|
||||||
|
XMLEvent event;
|
||||||
|
while(!(event = eventReader.nextTag()).isEndElement()) {
|
||||||
|
if(event.isStartElement()) {
|
||||||
|
StartElement childElement = (StartElement)event;
|
||||||
|
switch(childElement.getName().toString()) {
|
||||||
|
case CONTEXT_TAG_NAME:
|
||||||
|
processContext(eventReader, childElement, state, result);
|
||||||
|
break;
|
||||||
|
case TRANSLATION_TAG_NAME:
|
||||||
|
processTranslation(eventReader, childElement, state, result);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected element: " + event.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected content: " + event.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assureEndElement(event, CONTEXT_TAG_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void processTranslation(XMLEventReader eventReader, StartElement element, State state, List<TranslationEntry> result) throws XMLStreamException {
|
||||||
|
assureStartElement(element, TRANSLATION_TAG_NAME);
|
||||||
|
|
||||||
|
String path = null;
|
||||||
|
String template = null;
|
||||||
|
|
||||||
|
Iterator<Attribute> attributes = element.getAttributes();
|
||||||
|
while (attributes.hasNext())
|
||||||
|
{
|
||||||
|
Attribute attribute = attributes.next();
|
||||||
|
QName name = attribute.getName();
|
||||||
|
String value = attribute.getValue();
|
||||||
|
switch(name.toString()) {
|
||||||
|
case TRANSLATION_KEY_ATTRIBUTE_NAME:
|
||||||
|
assureIsValidKey(value);
|
||||||
|
path = ContextPaths.combinePaths(state.path, value);
|
||||||
|
break;
|
||||||
|
case TRANSLATION_TEMPLATE_ATTRIBUTE_NAME:
|
||||||
|
template = unescape(value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected attribute: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLEvent event;
|
||||||
|
while(!(event = eventReader.nextTag()).isEndElement()) {
|
||||||
|
if(event.isStartElement()) {
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected element: " + event.toString());
|
||||||
|
} else if(event.isCharacters()) {
|
||||||
|
if(template != null)
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Content must be empty if 'template' attribute is used");
|
||||||
|
template = event.asCharacters().getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assureEndElement(event, TRANSLATION_TAG_NAME);
|
||||||
|
|
||||||
|
if(path == null)
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Missing attribute: " + TRANSLATION_KEY_ATTRIBUTE_NAME);
|
||||||
|
|
||||||
|
if(template == null)
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Missing attribute: " + TRANSLATION_TEMPLATE_ATTRIBUTE_NAME);
|
||||||
|
|
||||||
|
result.add(new TranslationEntry(state.locale, path, template));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String unescape(String value) {
|
||||||
|
return StringEscapeUtils.unescapeJava(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assureIsValidPathExtension(String value) throws XMLStreamException {
|
||||||
|
if(ContextPaths.containsParentReference(value))
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Parent reference .. is not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assureIsValidKey(String value) throws XMLStreamException {
|
||||||
|
if(!ContextPaths.isValidKey(value))
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Key is not valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assureStartElement(XMLEvent event, String name) throws XMLStreamException {
|
||||||
|
if(!isStartElement(event, name))
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected start element: " + event.toString() + ", <" + name + "> expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assureEndElement(XMLEvent event, String name) throws XMLStreamException {
|
||||||
|
if(!isEndElement(event, name))
|
||||||
|
throw new javax.xml.stream.XMLStreamException("Unexpected end element: " + event.toString() + ", </" + name + "> expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStartElement(XMLEvent event, String name) {
|
||||||
|
if(!event.isStartElement())
|
||||||
|
return false;
|
||||||
|
StartElement element = ((StartElement)event);
|
||||||
|
return element.getName().toString().equals(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEndElement(XMLEvent event, String name) {
|
||||||
|
if(!event.isEndElement())
|
||||||
|
return false;
|
||||||
|
EndElement element = ((EndElement)event);
|
||||||
|
return element.getName().toString().equals(name);
|
||||||
|
}
|
||||||
|
}
|
33
src/globalization/Translations.xsd
Normal file
33
src/globalization/Translations.xsd
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
To change this license header, choose License Headers in Project Properties.
|
||||||
|
To change this template file, choose Tools | Templates
|
||||||
|
and open the template in the editor.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<xs:schema version="1.0"
|
||||||
|
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||||
|
elementFormDefault="qualified">
|
||||||
|
|
||||||
|
<xs:complexType name="localizationType">
|
||||||
|
<xs:all>
|
||||||
|
<xs:element name="context" minOccurs="1" maxOccurs="unbounded" />
|
||||||
|
</xs:all>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="contextType">
|
||||||
|
<xs:attribute name="path" type="xs:string" minOccurs="0" maxOccurs="1" />
|
||||||
|
<xs:attribute name="locale" type="xs:string" minOccurs="0" maxOccurs="1" />
|
||||||
|
<xs:all>
|
||||||
|
<xs:element type="translation" minOccurs="0" maxOccurs="unbounded" />
|
||||||
|
<xs:element type="context" minOccurs="0" maxOccurs="unbounded" />
|
||||||
|
</xs:all>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="translationType">
|
||||||
|
<xs:attribute name="keyPath" type="xs:string" minOccurs="1" maxOccurs="1" />
|
||||||
|
<xs:attribute name="template" type="xs:string" minOccurs="1" maxOccurs="1" />
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:element name="localization" type="localizationType" />
|
||||||
|
</xs:schema>
|
@ -1,24 +1,32 @@
|
|||||||
package globalization;
|
package globalization;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.AbstractMap;
|
import java.util.AbstractMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import javax.xml.stream.XMLStreamException;
|
||||||
import org.apache.commons.text.StringSubstitutor;
|
import org.apache.commons.text.StringSubstitutor;
|
||||||
|
|
||||||
|
import settings.Settings;
|
||||||
|
|
||||||
public class Translator {
|
public class Translator {
|
||||||
|
|
||||||
private Map<String, Object> createMap(Map.Entry<String, Object>[] entries) {
|
Map<Locale, Map<String, String>> translations = new HashMap<Locale, Map<String, String>>();
|
||||||
HashMap<String, Object> map = new HashMap<>();
|
|
||||||
for (AbstractMap.Entry<String, Object> entry : entries) {
|
|
||||||
map.put(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
//XXX: replace singleton pattern by dependency injection?
|
//XXX: replace singleton pattern by dependency injection?
|
||||||
private static Translator instance;
|
private static Translator instance;
|
||||||
|
|
||||||
|
private Translator() {
|
||||||
|
InitializeTranslations();
|
||||||
|
}
|
||||||
|
|
||||||
public static Translator getInstance() {
|
public static Translator getInstance() {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new Translator();
|
instance = new Translator();
|
||||||
@ -27,26 +35,142 @@ public class Translator {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String translate(Locale locale, String templateKey, AbstractMap.Entry<String, Object>... templateValues) {
|
private Settings settings() {
|
||||||
|
return Settings.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeTranslations() {
|
||||||
|
String path = this.settings().translationsPath();
|
||||||
|
File dir = new File(path);
|
||||||
|
File [] files = dir.listFiles(new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return name.endsWith(".xml");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<Locale, Map<String, String>> translations = new HashMap<>();
|
||||||
|
TranslationXmlStreamReader translationReader = new TranslationXmlStreamReader();
|
||||||
|
for (File file : files) {
|
||||||
|
Iterable<TranslationEntry> entries = null;
|
||||||
|
try {
|
||||||
|
InputStream stream = new FileInputStream(file);
|
||||||
|
entries = translationReader.ReadFrom(stream);
|
||||||
|
} catch (FileNotFoundException ex) {
|
||||||
|
Logger.getLogger(Translator.class.getName()).log(Level.SEVERE, String.format("Translation file not found: %s", file), ex);
|
||||||
|
} catch (XMLStreamException ex) {
|
||||||
|
Logger.getLogger(Translator.class.getName()).log(Level.SEVERE, String.format("Error in translation file: %s", file), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(TranslationEntry entry : entries) {
|
||||||
|
Map<String, String> localTranslations = translations.get(entry.locale());
|
||||||
|
if(localTranslations == null) {
|
||||||
|
localTranslations = new HashMap<>();
|
||||||
|
translations.put(entry.locale(), localTranslations);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(localTranslations.containsKey(entry.path())) {
|
||||||
|
Logger.getLogger(Translator.class.getName()).log(Level.SEVERE, String.format("Duplicate entry for locale '%s' and path '%s' in translation file '%s'. Falling back to default translations.", entry.locale(), entry.path(), file));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localTranslations.put(entry.path(), entry.template());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// everything is fine, so we store all read translations
|
||||||
|
this.translations = translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> createMap(Map.Entry<String, Object>[] entries) {
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
for (AbstractMap.Entry<String, Object> entry : entries) {
|
||||||
|
map.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String translate(Locale locale, String contextPath, String keyPath, AbstractMap.Entry<String, Object>... templateValues) {
|
||||||
Map<String, Object> map = createMap(templateValues);
|
Map<String, Object> map = createMap(templateValues);
|
||||||
return translate(locale, templateKey, map);
|
return translate(locale, contextPath, keyPath, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String translate(Locale locale, String templateKey, Map<String, Object> templateValues) {
|
public String translate(String contextPath, String keyPath, AbstractMap.Entry<String, Object>... templateValues) {
|
||||||
return translate(locale, templateKey, null, templateValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String translate(Locale locale, String templateKey, String defaultTemplate, AbstractMap.Entry<String, Object>... templateValues) {
|
|
||||||
Map<String, Object> map = createMap(templateValues);
|
Map<String, Object> map = createMap(templateValues);
|
||||||
return translate(locale, templateKey, defaultTemplate, map);
|
return translate(contextPath, keyPath, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String translate(Locale locale, String templateKey, String defaultTemplate, Map<String, Object> templateValues) {
|
public String translate(Locale locale, String contextPath, String keyPath, Map<String, Object> templateValues) {
|
||||||
String template = defaultTemplate; // TODO: get template for the given locale if available
|
return translate(locale, contextPath, keyPath, null, templateValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String translate(String contextPath, String keyPath, Map<String, Object> templateValues) {
|
||||||
|
return translate(contextPath, keyPath, null, templateValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String translate(Locale locale, String contextPath, String keyPath, String defaultTemplate, AbstractMap.Entry<String, Object>... templateValues) {
|
||||||
|
Map<String, Object> map = createMap(templateValues);
|
||||||
|
return translate(locale, contextPath, keyPath, defaultTemplate, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String translate(String contextPath, String keyPath, String defaultTemplate, AbstractMap.Entry<String, Object>... templateValues) {
|
||||||
|
Map<String, Object> map = createMap(templateValues);
|
||||||
|
return translate(contextPath, keyPath, defaultTemplate, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String translate(Locale locale, String contextPath, String keyPath, String defaultTemplate, Map<String, Object> templateValues) {
|
||||||
|
// look for requested language
|
||||||
|
String template = null;
|
||||||
|
if(locale != null)
|
||||||
|
template = getTemplateFromNearestPath(locale, contextPath, keyPath);
|
||||||
|
|
||||||
|
if(template != null)
|
||||||
|
return substitute(template, templateValues);
|
||||||
|
|
||||||
|
return translate(contextPath, keyPath, defaultTemplate, templateValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String translate(String contextPath, String keyPath, String defaultTemplate, Map<String, Object> templateValues) {
|
||||||
|
// scan default languages
|
||||||
|
String template = null;
|
||||||
|
for(String language : this.settings().translationsDefaultLocales()) {
|
||||||
|
Locale defaultLocale = Locale.forLanguageTag(language);
|
||||||
|
template = getTemplateFromNearestPath(defaultLocale, contextPath, keyPath);
|
||||||
|
if(template != null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(template == null)
|
||||||
|
template = defaultTemplate; // fallback template
|
||||||
|
|
||||||
|
return substitute(template, templateValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String substitute(String template, Map<String, Object> templateValues) {
|
||||||
|
if(templateValues == null)
|
||||||
|
return template;
|
||||||
|
|
||||||
StringSubstitutor sub = new StringSubstitutor(templateValues);
|
StringSubstitutor sub = new StringSubstitutor(templateValues);
|
||||||
String result = sub.replace(template);
|
String result = sub.replace(template);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getTemplateFromNearestPath(Locale locale, String contextPath, String keyPath) {
|
||||||
|
Map<String, String> localTranslations = this.translations.get(locale);
|
||||||
|
if(localTranslations == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String template = null;
|
||||||
|
while(true) {
|
||||||
|
String path = ContextPaths.combinePaths(contextPath, keyPath);
|
||||||
|
template = localTranslations.get(path);
|
||||||
|
if(template != null)
|
||||||
|
break; // found template
|
||||||
|
if(ContextPaths.isRoot(contextPath))
|
||||||
|
break; // nothing found
|
||||||
|
contextPath = ContextPaths.getParent(contextPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,15 @@ public class Settings {
|
|||||||
private int maxBytePerFee = 1024;
|
private int maxBytePerFee = 1024;
|
||||||
private String userpath = "";
|
private String userpath = "";
|
||||||
|
|
||||||
//RPC
|
// RPC
|
||||||
private int rpcPort = 9085;
|
private int rpcPort = 9085;
|
||||||
private List<String> rpcAllowed = new ArrayList<String>(Arrays.asList("127.0.0.1", "::1")); // ipv4, ipv6
|
private List<String> rpcAllowed = new ArrayList<String>(Arrays.asList("127.0.0.1", "::1")); // ipv4, ipv6
|
||||||
private boolean rpcEnabled = true;
|
private boolean rpcEnabled = true;
|
||||||
|
|
||||||
|
// Globalization
|
||||||
|
private String translationsPath = "globalization/";
|
||||||
|
private String[] translationsDefaultLocales = {"en"};
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
private static final String SETTINGS_FILENAME = "settings.json";
|
private static final String SETTINGS_FILENAME = "settings.json";
|
||||||
|
|
||||||
@ -129,6 +133,17 @@ public class Settings {
|
|||||||
{
|
{
|
||||||
this.rpcEnabled = ((Boolean) json.get("rpcenabled")).booleanValue();
|
this.rpcEnabled = ((Boolean) json.get("rpcenabled")).booleanValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Globalization
|
||||||
|
if(json.containsKey("translationspath"))
|
||||||
|
{
|
||||||
|
this.translationsPath = ((String) json.get("translationspath"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(json.containsKey("translationsdefaultlocales"))
|
||||||
|
{
|
||||||
|
this.translationsDefaultLocales = ((String[]) json.get("translationsdefaultlocales"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTestNet() {
|
public boolean isTestNet() {
|
||||||
@ -163,4 +178,14 @@ public class Settings {
|
|||||||
{
|
{
|
||||||
return this.rpcEnabled;
|
return this.rpcEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String translationsPath()
|
||||||
|
{
|
||||||
|
return this.translationsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] translationsDefaultLocales()
|
||||||
|
{
|
||||||
|
return this.translationsDefaultLocales;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.google.common.hash.HashCode;
|
import com.google.common.hash.HashCode;
|
||||||
|
|
||||||
import data.at.ATStateData;
|
import data.at.ATStateData;
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import data.block.BlockData;
|
import data.block.BlockData;
|
||||||
import data.transaction.TransactionData;
|
import data.transaction.TransactionData;
|
||||||
@ -67,7 +66,7 @@ public class BlockTests extends Common {
|
|||||||
// Block 949 has lots of varied transactions
|
// Block 949 has lots of varied transactions
|
||||||
// Blocks 390 & 754 have only payment transactions
|
// Blocks 390 & 754 have only payment transactions
|
||||||
BlockData blockData = repository.getBlockRepository().fromHeight(754);
|
BlockData blockData = repository.getBlockRepository().fromHeight(754);
|
||||||
assertNotNull("Block 754 is required for this test", blockData);
|
assertNotNull(blockData, "Block 754 is required for this test");
|
||||||
|
|
||||||
Block block = new Block(repository, blockData);
|
Block block = new Block(repository, blockData);
|
||||||
assertTrue(block.isSignatureValid());
|
assertTrue(block.isSignatureValid());
|
||||||
@ -108,7 +107,7 @@ public class BlockTests extends Common {
|
|||||||
// Block 949 has lots of varied transactions
|
// Block 949 has lots of varied transactions
|
||||||
// Blocks 390 & 754 have only payment transactions
|
// Blocks 390 & 754 have only payment transactions
|
||||||
BlockData blockData = repository.getBlockRepository().fromHeight(754);
|
BlockData blockData = repository.getBlockRepository().fromHeight(754);
|
||||||
assertNotNull("Block 754 is required for this test", blockData);
|
assertNotNull(blockData, "Block 754 is required for this test");
|
||||||
|
|
||||||
Block block = new Block(repository, blockData);
|
Block block = new Block(repository, blockData);
|
||||||
assertTrue(block.isSignatureValid());
|
assertTrue(block.isSignatureValid());
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import qora.block.BlockChain;
|
import qora.block.BlockChain;
|
||||||
import repository.DataException;
|
import repository.DataException;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import org.junit.AfterClass;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.BeforeClass;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
|
||||||
import repository.DataException;
|
import repository.DataException;
|
||||||
import repository.RepositoryFactory;
|
import repository.RepositoryFactory;
|
||||||
@ -13,13 +15,13 @@ public class Common {
|
|||||||
// public static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true;close_result=true;sql.strict_exec=true;sql.enforce_names=true;sql.syntax_mys=true;sql.pad_space=false";
|
// public static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true;close_result=true;sql.strict_exec=true;sql.enforce_names=true;sql.syntax_mys=true;sql.pad_space=false";
|
||||||
public static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true";
|
public static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true";
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeAll
|
||||||
public static void setRepository() throws DataException {
|
public static void setRepository() throws DataException {
|
||||||
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
||||||
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterAll
|
||||||
public static void closeRepository() throws DataException {
|
public static void closeRepository() throws DataException {
|
||||||
RepositoryManager.closeRepositoryFactory();
|
RepositoryManager.closeRepositoryFactory();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.google.common.hash.HashCode;
|
import com.google.common.hash.HashCode;
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.google.common.hash.HashCode;
|
import com.google.common.hash.HashCode;
|
||||||
|
|
||||||
@ -16,7 +15,7 @@ public class CryptoTests {
|
|||||||
byte[] digest = Crypto.digest(input);
|
byte[] digest = Crypto.digest(input);
|
||||||
byte[] expected = HashCode.fromString("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d").asBytes();
|
byte[] expected = HashCode.fromString("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d").asBytes();
|
||||||
|
|
||||||
assertArrayEquals(digest, expected);
|
assertArrayEquals(expected, digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -25,7 +24,7 @@ public class CryptoTests {
|
|||||||
byte[] digest = Crypto.doubleDigest(input);
|
byte[] digest = Crypto.doubleDigest(input);
|
||||||
byte[] expected = HashCode.fromString("1406e05881e299367766d313e26c05564ec91bf721d31726bd6e46e60689539a").asBytes();
|
byte[] expected = HashCode.fromString("1406e05881e299367766d313e26c05564ec91bf721d31726bd6e46e60689539a").asBytes();
|
||||||
|
|
||||||
assertArrayEquals(digest, expected);
|
assertArrayEquals(expected, digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import qora.block.Block;
|
import qora.block.Block;
|
||||||
|
|
||||||
public class ExceptionTests {
|
public class ExceptionTests {
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.AfterClass;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.BeforeClass;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
|
||||||
import data.transaction.TransactionData;
|
import data.transaction.TransactionData;
|
||||||
import qora.account.Account;
|
import qora.account.Account;
|
||||||
@ -26,13 +25,13 @@ public class GenesisTests {
|
|||||||
|
|
||||||
public static final String connectionUrl = "jdbc:hsqldb:mem:db/test;create=true;close_result=true;sql.strict_exec=true;sql.enforce_names=true;sql.syntax_mys=true";
|
public static final String connectionUrl = "jdbc:hsqldb:mem:db/test;create=true;close_result=true;sql.strict_exec=true;sql.enforce_names=true;sql.syntax_mys=true";
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeAll
|
||||||
public static void setRepository() throws DataException {
|
public static void setRepository() throws DataException {
|
||||||
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
||||||
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterAll
|
||||||
public static void closeRepository() throws DataException {
|
public static void closeRepository() throws DataException {
|
||||||
RepositoryManager.closeRepositoryFactory();
|
RepositoryManager.closeRepositoryFactory();
|
||||||
}
|
}
|
||||||
@ -40,7 +39,7 @@ public class GenesisTests {
|
|||||||
@Test
|
@Test
|
||||||
public void testGenesisBlockTransactions() throws DataException {
|
public void testGenesisBlockTransactions() throws DataException {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
assertEquals("Blockchain should be empty for this test", 0, repository.getBlockRepository().getBlockchainHeight());
|
assertEquals(0, repository.getBlockRepository().getBlockchainHeight(), "Blockchain should be empty for this test");
|
||||||
|
|
||||||
GenesisBlock block = new GenesisBlock(repository);
|
GenesisBlock block = new GenesisBlock(repository);
|
||||||
|
|
||||||
|
171
src/test/GlobalizationTests.java
Normal file
171
src/test/GlobalizationTests.java
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
package test;
|
||||||
|
|
||||||
|
import globalization.TranslationEntry;
|
||||||
|
import globalization.TranslationXmlStreamReader;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import javax.xml.stream.XMLStreamException;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import static test.utils.AssertExtensions.*;
|
||||||
|
import test.utils.EqualityComparer;
|
||||||
|
|
||||||
|
public class GlobalizationTests {
|
||||||
|
|
||||||
|
private class TranslationEntryEqualityComparer implements EqualityComparer<TranslationEntry> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(TranslationEntry first, TranslationEntry second) {
|
||||||
|
if(first == null && second == null)
|
||||||
|
return true;
|
||||||
|
if(first == null && second != null || first != null && second == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!first.locale().equals(second.locale()))
|
||||||
|
return false;
|
||||||
|
if(!first.path().equals(second.path()))
|
||||||
|
return false;
|
||||||
|
if(!first.template().equals(second.template()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode(TranslationEntry item) {
|
||||||
|
int hash = 17;
|
||||||
|
final int multiplier = 59;
|
||||||
|
|
||||||
|
hash = hash * multiplier + item.locale().hashCode();
|
||||||
|
hash = hash * multiplier + item.path().hashCode();
|
||||||
|
hash = hash * multiplier + item.template().hashCode();
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestTranslationXmlReaderContextPaths() throws XMLStreamException {
|
||||||
|
String xml =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
|
"<localization>\n" +
|
||||||
|
" <context locale=\"en-GB\">\n" +
|
||||||
|
" <context path=\"path1/\">\n" +
|
||||||
|
" <translation key=\"key1\" template=\"1\" />\n" +
|
||||||
|
" <context path=\"./path2//path3\">\n" +
|
||||||
|
" <translation key=\"key2\" template=\"2\" />\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
" <context path=\"/path4\">\n" +
|
||||||
|
" <translation key=\"key3\" template=\"3\" />\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
"</localization>\n";
|
||||||
|
|
||||||
|
List<TranslationEntry> expected = new ArrayList<>();
|
||||||
|
expected.add(new TranslationEntry(Locale.forLanguageTag("en-GB"), "/path1/key1", "1"));
|
||||||
|
expected.add(new TranslationEntry(Locale.forLanguageTag("en-GB"), "/path1/path2/path3/key2", "2"));
|
||||||
|
expected.add(new TranslationEntry(Locale.forLanguageTag("en-GB"), "/path1/path4/key3", "3"));
|
||||||
|
|
||||||
|
InputStream is = new ByteArrayInputStream(xml.getBytes(Charset.forName("UTF-8")));
|
||||||
|
TranslationXmlStreamReader reader = new TranslationXmlStreamReader();
|
||||||
|
Iterable<TranslationEntry> actual = reader.ReadFrom(is);
|
||||||
|
|
||||||
|
for(TranslationEntry i:expected)System.out.println(i);for(TranslationEntry i:actual)System.out.println(i);
|
||||||
|
assertItemsEqual(expected, actual, new TranslationEntryEqualityComparer());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestTranslationXmlReaderLocales() throws XMLStreamException {
|
||||||
|
String xml =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
|
"<localization>\n" +
|
||||||
|
" <translation key=\"key1\" template=\"1\" />\n" +
|
||||||
|
" <context locale=\"en-GB\" path=\"path1\">\n" +
|
||||||
|
" <translation key=\"key2\" template=\"2\" />\n" +
|
||||||
|
" <context locale=\"de-DE\" path=\"path2/\">\n" +
|
||||||
|
" <translation key=\"key3\" template=\"3\" />\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
"</localization>\n";
|
||||||
|
|
||||||
|
List<TranslationEntry> expected = new ArrayList<TranslationEntry>();
|
||||||
|
expected.add(new TranslationEntry(Locale.forLanguageTag("default"), "/key1", "1"));
|
||||||
|
expected.add(new TranslationEntry(Locale.forLanguageTag("en-GB"), "/path1/key2", "2"));
|
||||||
|
expected.add(new TranslationEntry(Locale.forLanguageTag("de-DE"), "/path1/path2/key3", "3"));
|
||||||
|
|
||||||
|
InputStream is = new ByteArrayInputStream(xml.getBytes(Charset.forName("UTF-8")));
|
||||||
|
TranslationXmlStreamReader reader = new TranslationXmlStreamReader();
|
||||||
|
Iterable<TranslationEntry> actual = reader.ReadFrom(is);
|
||||||
|
|
||||||
|
assertItemsEqual(expected, actual, new TranslationEntryEqualityComparer());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestTranslationXmlReader_BadPath() {
|
||||||
|
String xml =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
|
"<localization>\n" +
|
||||||
|
" <context locale=\"en-GB\">\n" +
|
||||||
|
" <context path=\"path1\">\n" +
|
||||||
|
" <context path=\"../path2\">\n" +
|
||||||
|
" <translation key=\"key1\" template=\"1\" />\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
"</localization>\n";
|
||||||
|
|
||||||
|
InputStream is = new ByteArrayInputStream(xml.getBytes(Charset.forName("UTF-8")));
|
||||||
|
TranslationXmlStreamReader reader = new TranslationXmlStreamReader();
|
||||||
|
|
||||||
|
assertThrows(XMLStreamException.class, () -> reader.ReadFrom(is));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestTranslationXmlReader_BadKey1() {
|
||||||
|
String xml =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
|
"<localization>\n" +
|
||||||
|
" <context locale=\"en-GB\">\n" +
|
||||||
|
" <context path=\"path1\">\n" +
|
||||||
|
" <context path=\"path2\">\n" +
|
||||||
|
" <translation key=\"path3/key1\" template=\"1\" />\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
"</localization>\n";
|
||||||
|
|
||||||
|
InputStream is = new ByteArrayInputStream(xml.getBytes(Charset.forName("UTF-8")));
|
||||||
|
TranslationXmlStreamReader reader = new TranslationXmlStreamReader();
|
||||||
|
|
||||||
|
assertThrows(XMLStreamException.class, () -> reader.ReadFrom(is));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TestTranslationXmlReader_BadKey2() {
|
||||||
|
String xml =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
|
"<localization>\n" +
|
||||||
|
" <context locale=\"en-GB\">\n" +
|
||||||
|
" <context path=\"path1\">\n" +
|
||||||
|
" <context path=\"path2\">\n" +
|
||||||
|
" <translation key=\"..\" template=\"1\" />\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
" </context>\n" +
|
||||||
|
"</localization>\n";
|
||||||
|
|
||||||
|
InputStream is = new ByteArrayInputStream(xml.getBytes(Charset.forName("UTF-8")));
|
||||||
|
TranslationXmlStreamReader reader = new TranslationXmlStreamReader();
|
||||||
|
|
||||||
|
assertThrows(XMLStreamException.class, () -> reader.ReadFrom(is));
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,7 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import data.transaction.PaymentTransactionData;
|
import data.transaction.PaymentTransactionData;
|
||||||
import data.transaction.TransactionData;
|
import data.transaction.TransactionData;
|
||||||
@ -21,25 +20,25 @@ public class LoadTests extends Common {
|
|||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
TransactionRepository transactionRepository = repository.getTransactionRepository();
|
TransactionRepository transactionRepository = repository.getTransactionRepository();
|
||||||
|
|
||||||
assertTrue("Migrate from old database to at least block 49778 before running this test",
|
assertTrue(repository.getBlockRepository().getBlockchainHeight() >= 49778,
|
||||||
repository.getBlockRepository().getBlockchainHeight() >= 49778);
|
"Migrate from old database to at least block 49778 before running this test");
|
||||||
|
|
||||||
String signature58 = "1211ZPwG3hk5evWzXCZi9hMDRpwumWmkENjwWkeTCik9xA5uoYnxzF7rwR5hmHH3kG2RXo7ToCAaRc7dvnynByJt";
|
String signature58 = "1211ZPwG3hk5evWzXCZi9hMDRpwumWmkENjwWkeTCik9xA5uoYnxzF7rwR5hmHH3kG2RXo7ToCAaRc7dvnynByJt";
|
||||||
byte[] signature = Base58.decode(signature58);
|
byte[] signature = Base58.decode(signature58);
|
||||||
|
|
||||||
TransactionData transactionData = transactionRepository.fromSignature(signature);
|
TransactionData transactionData = transactionRepository.fromSignature(signature);
|
||||||
assertNotNull("Transaction data not loaded from repository", transactionData);
|
assertNotNull(transactionData, "Transaction data not loaded from repository");
|
||||||
assertEquals("Transaction data not PAYMENT type", TransactionType.PAYMENT, transactionData.getType());
|
assertEquals(TransactionType.PAYMENT, transactionData.getType(), "Transaction data not PAYMENT type");
|
||||||
assertEquals(PublicKeyAccount.getAddress(transactionData.getCreatorPublicKey()), "QXwu8924WdgPoRmtiWQBUMF6eedmp1Hu2E");
|
assertEquals("QXwu8924WdgPoRmtiWQBUMF6eedmp1Hu2E", PublicKeyAccount.getAddress(transactionData.getCreatorPublicKey()));
|
||||||
|
|
||||||
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) transactionData;
|
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) transactionData;
|
||||||
|
|
||||||
assertNotNull(paymentTransactionData);
|
assertNotNull(paymentTransactionData);
|
||||||
assertEquals(PublicKeyAccount.getAddress(paymentTransactionData.getSenderPublicKey()), "QXwu8924WdgPoRmtiWQBUMF6eedmp1Hu2E");
|
assertEquals("QXwu8924WdgPoRmtiWQBUMF6eedmp1Hu2E", PublicKeyAccount.getAddress(paymentTransactionData.getSenderPublicKey()));
|
||||||
assertEquals(paymentTransactionData.getRecipient(), "QZsv8vbJ6QfrBNba4LMp5UtHhAzhrxvVUU");
|
assertEquals("QZsv8vbJ6QfrBNba4LMp5UtHhAzhrxvVUU", paymentTransactionData.getRecipient());
|
||||||
assertEquals(paymentTransactionData.getTimestamp(), 1416209264000L);
|
assertEquals(1416209264000L, paymentTransactionData.getTimestamp());
|
||||||
assertEquals(Base58.encode(paymentTransactionData.getReference()),
|
assertEquals("31dC6kHHBeG5vYb8LMaZDjLEmhc9kQB2VUApVd8xWncSRiXu7yMejdprjYFMP2rUnzZxWd4KJhkq6LsV7rQvU1kY",
|
||||||
"31dC6kHHBeG5vYb8LMaZDjLEmhc9kQB2VUApVd8xWncSRiXu7yMejdprjYFMP2rUnzZxWd4KJhkq6LsV7rQvU1kY");
|
Base58.encode(paymentTransactionData.getReference()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +47,8 @@ public class LoadTests extends Common {
|
|||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
TransactionRepository transactionRepository = repository.getTransactionRepository();
|
TransactionRepository transactionRepository = repository.getTransactionRepository();
|
||||||
|
|
||||||
assertTrue("Migrate from old database to at least block 49778 before running this test",
|
assertTrue(repository.getBlockRepository().getBlockchainHeight() >= 49778,
|
||||||
repository.getBlockRepository().getBlockchainHeight() >= 49778);
|
"Migrate from old database to at least block 49778 before running this test");
|
||||||
|
|
||||||
String signature58 = "1211ZPwG3hk5evWzXCZi9hMDRpwumWmkENjwWkeTCik9xA5uoYnxzF7rwR5hmHH3kG2RXo7ToCAaRc7dvnynByJt";
|
String signature58 = "1211ZPwG3hk5evWzXCZi9hMDRpwumWmkENjwWkeTCik9xA5uoYnxzF7rwR5hmHH3kG2RXo7ToCAaRc7dvnynByJt";
|
||||||
byte[] signature = Base58.decode(signature58);
|
byte[] signature = Base58.decode(signature58);
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import data.block.BlockData;
|
import data.block.BlockData;
|
||||||
import data.transaction.TransactionData;
|
import data.transaction.TransactionData;
|
||||||
@ -20,8 +19,8 @@ public class NavigationTests extends Common {
|
|||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
TransactionRepository transactionRepository = repository.getTransactionRepository();
|
TransactionRepository transactionRepository = repository.getTransactionRepository();
|
||||||
|
|
||||||
assertTrue("Migrate from old database to at least block 49778 before running this test",
|
assertTrue(repository.getBlockRepository().getBlockchainHeight() >= 49778,
|
||||||
repository.getBlockRepository().getBlockchainHeight() >= 49778);
|
"Migrate from old database to at least block 49778 before running this test");
|
||||||
|
|
||||||
String signature58 = "1211ZPwG3hk5evWzXCZi9hMDRpwumWmkENjwWkeTCik9xA5uoYnxzF7rwR5hmHH3kG2RXo7ToCAaRc7dvnynByJt";
|
String signature58 = "1211ZPwG3hk5evWzXCZi9hMDRpwumWmkENjwWkeTCik9xA5uoYnxzF7rwR5hmHH3kG2RXo7ToCAaRc7dvnynByJt";
|
||||||
byte[] signature = Base58.decode(signature58);
|
byte[] signature = Base58.decode(signature58);
|
||||||
@ -29,11 +28,11 @@ public class NavigationTests extends Common {
|
|||||||
System.out.println("Navigating to Block from transaction " + signature58);
|
System.out.println("Navigating to Block from transaction " + signature58);
|
||||||
|
|
||||||
TransactionData transactionData = transactionRepository.fromSignature(signature);
|
TransactionData transactionData = transactionRepository.fromSignature(signature);
|
||||||
assertNotNull("Transaction data not loaded from repository", transactionData);
|
assertNotNull(transactionData, "Transaction data not loaded from repository");
|
||||||
assertEquals("Transaction data not PAYMENT type", TransactionType.PAYMENT, transactionData.getType());
|
assertEquals(TransactionType.PAYMENT, transactionData.getType(), "Transaction data not PAYMENT type");
|
||||||
|
|
||||||
BlockData blockData = transactionRepository.getBlockDataFromSignature(signature);
|
BlockData blockData = transactionRepository.getBlockDataFromSignature(signature);
|
||||||
assertNotNull("Block 49778 not loaded from database", blockData);
|
assertNotNull(blockData, "Block 49778 not loaded from database");
|
||||||
|
|
||||||
System.out.println("Block " + blockData.getHeight() + ", signature: " + Base58.encode(blockData.getSignature()));
|
System.out.println("Block " + blockData.getHeight() + ", signature: " + Base58.encode(blockData.getSignature()));
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import repository.DataException;
|
import repository.DataException;
|
||||||
import repository.Repository;
|
import repository.Repository;
|
||||||
|
@ -3,7 +3,8 @@ package test;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import data.transaction.PaymentTransactionData;
|
import data.transaction.PaymentTransactionData;
|
||||||
import qora.account.PublicKeyAccount;
|
import qora.account.PublicKeyAccount;
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import data.block.BlockData;
|
import data.block.BlockData;
|
||||||
import data.transaction.GenesisTransactionData;
|
import data.transaction.GenesisTransactionData;
|
||||||
import data.transaction.TransactionData;
|
import data.transaction.TransactionData;
|
||||||
@ -61,15 +60,15 @@ public class SerializationTests extends Common {
|
|||||||
|
|
||||||
TransactionData parsedTransactionData = TransactionTransformer.fromBytes(bytes);
|
TransactionData parsedTransactionData = TransactionTransformer.fromBytes(bytes);
|
||||||
|
|
||||||
assertTrue("Transaction signature mismatch", Arrays.equals(transactionData.getSignature(), parsedTransactionData.getSignature()));
|
assertTrue(Arrays.equals(transactionData.getSignature(), parsedTransactionData.getSignature()), "Transaction signature mismatch");
|
||||||
|
|
||||||
assertEquals("Data length mismatch", TransactionTransformer.getDataLength(transactionData), bytes.length);
|
assertEquals(bytes.length, TransactionTransformer.getDataLength(transactionData), "Data length mismatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testSpecificBlockTransactions(int height, TransactionType type) throws DataException, TransformationException {
|
private void testSpecificBlockTransactions(int height, TransactionType type) throws DataException, TransformationException {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
BlockData blockData = repository.getBlockRepository().fromHeight(height);
|
BlockData blockData = repository.getBlockRepository().fromHeight(height);
|
||||||
assertNotNull("Block " + height + " is required for this test", blockData);
|
assertNotNull(blockData, "Block " + height + " is required for this test");
|
||||||
|
|
||||||
Block block = new Block(repository, blockData);
|
Block block = new Block(repository, blockData);
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import data.block.BlockData;
|
import data.block.BlockData;
|
||||||
import qora.account.PrivateKeyAccount;
|
import qora.account.PrivateKeyAccount;
|
||||||
import qora.block.Block;
|
import qora.block.Block;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@ -10,8 +12,6 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
import org.json.simple.JSONObject;
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.google.common.hash.HashCode;
|
import com.google.common.hash.HashCode;
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ public class TransactionTests {
|
|||||||
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
assertEquals("Blockchain should be empty for this test", 0, repository.getBlockRepository().getBlockchainHeight());
|
assertEquals(0, repository.getBlockRepository().getBlockchainHeight(), "Blockchain should be empty for this test");
|
||||||
}
|
}
|
||||||
|
|
||||||
// [Un]set genesis timestamp as required by test
|
// [Un]set genesis timestamp as required by test
|
||||||
@ -136,7 +136,7 @@ public class TransactionTests {
|
|||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@AfterEach
|
||||||
public void closeRepository() throws DataException {
|
public void closeRepository() throws DataException {
|
||||||
RepositoryManager.closeRepositoryFactory();
|
RepositoryManager.closeRepositoryFactory();
|
||||||
}
|
}
|
||||||
@ -176,8 +176,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(paymentTransactionData);
|
block.addTransaction(paymentTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -185,21 +185,21 @@ public class TransactionTests {
|
|||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
BigDecimal expectedBalance = initialSenderBalance.subtract(amount).subtract(fee);
|
BigDecimal expectedBalance = initialSenderBalance.subtract(amount).subtract(fee);
|
||||||
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
||||||
|
|
||||||
// Fee should be in generator's balance
|
// Fee should be in generator's balance
|
||||||
expectedBalance = initialGeneratorBalance.add(fee);
|
expectedBalance = initialGeneratorBalance.add(fee);
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
||||||
|
|
||||||
// Amount should be in recipient's balance
|
// Amount should be in recipient's balance
|
||||||
expectedBalance = amount;
|
expectedBalance = amount;
|
||||||
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Recipient's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Recipient's new balance incorrect");
|
||||||
|
|
||||||
// Check recipient's reference
|
// Check recipient's reference
|
||||||
byte[] recipientsReference = recipient.getLastReference();
|
byte[] recipientsReference = recipient.getLastReference();
|
||||||
assertTrue("Recipient's new reference incorrect", Arrays.equals(paymentTransaction.getTransactionData().getSignature(), recipientsReference));
|
assertTrue(Arrays.equals(paymentTransaction.getTransactionData().getSignature(), recipientsReference), "Recipient's new reference incorrect");
|
||||||
|
|
||||||
// Orphan block
|
// Orphan block
|
||||||
block.orphan();
|
block.orphan();
|
||||||
@ -207,11 +207,11 @@ public class TransactionTests {
|
|||||||
|
|
||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's reverted balance incorrect", initialSenderBalance.compareTo(actualBalance) == 0);
|
assertTrue(initialSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
||||||
|
|
||||||
// Check generator's balance
|
// Check generator's balance
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's new balance incorrect", initialGeneratorBalance.compareTo(actualBalance) == 0);
|
assertTrue(initialGeneratorBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -237,8 +237,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(registerNameTransactionData);
|
block.addTransaction(registerNameTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -246,19 +246,19 @@ public class TransactionTests {
|
|||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
||||||
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
||||||
|
|
||||||
// Fee should be in generator's balance
|
// Fee should be in generator's balance
|
||||||
expectedBalance = initialGeneratorBalance.add(fee);
|
expectedBalance = initialGeneratorBalance.add(fee);
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
||||||
|
|
||||||
// Check name was registered
|
// Check name was registered
|
||||||
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
||||||
assertNotNull(actualNameData);
|
assertNotNull(actualNameData);
|
||||||
|
|
||||||
// Check sender's reference
|
// Check sender's reference
|
||||||
assertTrue("Sender's new reference incorrect", Arrays.equals(registerNameTransactionData.getSignature(), sender.getLastReference()));
|
assertTrue(Arrays.equals(registerNameTransactionData.getSignature(), sender.getLastReference()), "Sender's new reference incorrect");
|
||||||
|
|
||||||
// Update variables for use by other tests
|
// Update variables for use by other tests
|
||||||
reference = sender.getLastReference();
|
reference = sender.getLastReference();
|
||||||
@ -293,8 +293,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(updateNameTransactionData);
|
block.addTransaction(updateNameTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -338,8 +338,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(sellNameTransactionData);
|
block.addTransaction(sellNameTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -389,8 +389,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(cancelSellNameTransactionData);
|
block.addTransaction(cancelSellNameTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -455,8 +455,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(buyNameTransactionData);
|
block.addTransaction(buyNameTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -508,8 +508,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(createPollTransactionData);
|
block.addTransaction(createPollTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -517,19 +517,19 @@ public class TransactionTests {
|
|||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
||||||
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
||||||
|
|
||||||
// Fee should be in generator's balance
|
// Fee should be in generator's balance
|
||||||
expectedBalance = initialGeneratorBalance.add(fee);
|
expectedBalance = initialGeneratorBalance.add(fee);
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
||||||
|
|
||||||
// Check poll was created
|
// Check poll was created
|
||||||
PollData actualPollData = this.repository.getVotingRepository().fromPollName(pollName);
|
PollData actualPollData = this.repository.getVotingRepository().fromPollName(pollName);
|
||||||
assertNotNull(actualPollData);
|
assertNotNull(actualPollData);
|
||||||
|
|
||||||
// Check sender's reference
|
// Check sender's reference
|
||||||
assertTrue("Sender's new reference incorrect", Arrays.equals(createPollTransactionData.getSignature(), sender.getLastReference()));
|
assertTrue(Arrays.equals(createPollTransactionData.getSignature(), sender.getLastReference()), "Sender's new reference incorrect");
|
||||||
|
|
||||||
// Update variables for use by other tests
|
// Update variables for use by other tests
|
||||||
reference = sender.getLastReference();
|
reference = sender.getLastReference();
|
||||||
@ -567,8 +567,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(voteOnPollTransactionData);
|
block.addTransaction(voteOnPollTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -588,10 +588,10 @@ public class TransactionTests {
|
|||||||
List<VoteOnPollData> votes = repository.getVotingRepository().getVotes(pollName);
|
List<VoteOnPollData> votes = repository.getVotingRepository().getVotes(pollName);
|
||||||
assertNotNull(votes);
|
assertNotNull(votes);
|
||||||
|
|
||||||
assertEquals("Only one vote expected", 1, votes.size());
|
assertEquals(1, votes.size(), "Only one vote expected");
|
||||||
|
|
||||||
assertEquals("Wrong vote option index", pollOptionsSize - 1, votes.get(0).getOptionIndex());
|
assertEquals(pollOptionsSize - 1, votes.get(0).getOptionIndex(), "Wrong vote option index");
|
||||||
assertTrue("Wrong voter public key", Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey()));
|
assertTrue(Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey()), "Wrong voter public key");
|
||||||
|
|
||||||
// Orphan last block
|
// Orphan last block
|
||||||
BlockData lastBlockData = repository.getBlockRepository().getLastBlock();
|
BlockData lastBlockData = repository.getBlockRepository().getLastBlock();
|
||||||
@ -603,10 +603,10 @@ public class TransactionTests {
|
|||||||
votes = repository.getVotingRepository().getVotes(pollName);
|
votes = repository.getVotingRepository().getVotes(pollName);
|
||||||
assertNotNull(votes);
|
assertNotNull(votes);
|
||||||
|
|
||||||
assertEquals("Only one vote expected", 1, votes.size());
|
assertEquals(1, votes.size(), "Only one vote expected");
|
||||||
|
|
||||||
assertEquals("Wrong vote option index", pollOptionsSize - 1 - 1, votes.get(0).getOptionIndex());
|
assertEquals(pollOptionsSize - 1 - 1, votes.get(0).getOptionIndex(), "Wrong vote option index");
|
||||||
assertTrue("Wrong voter public key", Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey()));
|
assertTrue(Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey()), "Wrong voter public key");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -634,8 +634,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(issueAssetTransactionData);
|
block.addTransaction(issueAssetTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -643,12 +643,12 @@ public class TransactionTests {
|
|||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
||||||
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
||||||
|
|
||||||
// Fee should be in generator's balance
|
// Fee should be in generator's balance
|
||||||
expectedBalance = initialGeneratorBalance.add(fee);
|
expectedBalance = initialGeneratorBalance.add(fee);
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
||||||
|
|
||||||
// Check we now have an assetId
|
// Check we now have an assetId
|
||||||
Long assetId = issueAssetTransactionData.getAssetId();
|
Long assetId = issueAssetTransactionData.getAssetId();
|
||||||
@ -672,11 +672,11 @@ public class TransactionTests {
|
|||||||
|
|
||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's reverted balance incorrect", initialSenderBalance.compareTo(actualBalance) == 0);
|
assertTrue(initialSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
||||||
|
|
||||||
// Check generator's balance
|
// Check generator's balance
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's reverted balance incorrect", initialGeneratorBalance.compareTo(actualBalance) == 0);
|
assertTrue(initialGeneratorBalance.compareTo(actualBalance) == 0, "Generator's reverted balance incorrect");
|
||||||
|
|
||||||
// Check asset no longer exists
|
// Check asset no longer exists
|
||||||
assertFalse(assetRepo.assetExists(assetId));
|
assertFalse(assetRepo.assetExists(assetId));
|
||||||
@ -724,8 +724,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(transferAssetTransactionData);
|
block.addTransaction(transferAssetTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -733,12 +733,12 @@ public class TransactionTests {
|
|||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
BigDecimal expectedBalance = originalSenderBalance.subtract(fee);
|
BigDecimal expectedBalance = originalSenderBalance.subtract(fee);
|
||||||
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
||||||
|
|
||||||
// Fee should be in generator's balance
|
// Fee should be in generator's balance
|
||||||
expectedBalance = originalGeneratorBalance.add(fee);
|
expectedBalance = originalGeneratorBalance.add(fee);
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
||||||
|
|
||||||
// Check asset balances
|
// Check asset balances
|
||||||
BigDecimal actualSenderAssetBalance = sender.getConfirmedBalance(assetId);
|
BigDecimal actualSenderAssetBalance = sender.getConfirmedBalance(assetId);
|
||||||
@ -756,11 +756,11 @@ public class TransactionTests {
|
|||||||
|
|
||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's reverted balance incorrect", originalSenderBalance.compareTo(actualBalance) == 0);
|
assertTrue(originalSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
||||||
|
|
||||||
// Check generator's balance
|
// Check generator's balance
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's reverted balance incorrect", originalGeneratorBalance.compareTo(actualBalance) == 0);
|
assertTrue(originalGeneratorBalance.compareTo(actualBalance) == 0, "Generator's reverted balance incorrect");
|
||||||
|
|
||||||
// Check asset balances
|
// Check asset balances
|
||||||
actualSenderAssetBalance = sender.getConfirmedBalance(assetId);
|
actualSenderAssetBalance = sender.getConfirmedBalance(assetId);
|
||||||
@ -828,8 +828,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(createOrderTransactionData);
|
block.addTransaction(createOrderTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -909,8 +909,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(cancelOrderTransactionData);
|
block.addTransaction(cancelOrderTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -984,8 +984,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(createOrderTransactionData);
|
block.addTransaction(createOrderTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -998,7 +998,7 @@ public class TransactionTests {
|
|||||||
// Check order has trades
|
// Check order has trades
|
||||||
List<TradeData> trades = assetRepo.getOrdersTrades(orderId);
|
List<TradeData> trades = assetRepo.getOrdersTrades(orderId);
|
||||||
assertNotNull(trades);
|
assertNotNull(trades);
|
||||||
assertEquals("Trade didn't happen", 1, trades.size());
|
assertEquals(1, trades.size(), "Trade didn't happen");
|
||||||
TradeData tradeData = trades.get(0);
|
TradeData tradeData = trades.get(0);
|
||||||
|
|
||||||
// Check trade has correct values
|
// Check trade has correct values
|
||||||
@ -1093,20 +1093,20 @@ public class TransactionTests {
|
|||||||
block.addTransaction(multiPaymentTransactionData);
|
block.addTransaction(multiPaymentTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
|
|
||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's new balance incorrect", expectedSenderBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedSenderBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
||||||
|
|
||||||
// Fee should be in generator's balance
|
// Fee should be in generator's balance
|
||||||
BigDecimal expectedBalance = initialGeneratorBalance.add(fee);
|
BigDecimal expectedBalance = initialGeneratorBalance.add(fee);
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
||||||
|
|
||||||
// Check recipients
|
// Check recipients
|
||||||
for (int i = 0; i < payments.size(); ++i) {
|
for (int i = 0; i < payments.size(); ++i) {
|
||||||
@ -1114,12 +1114,12 @@ public class TransactionTests {
|
|||||||
Account recipient = new Account(this.repository, paymentData.getRecipient());
|
Account recipient = new Account(this.repository, paymentData.getRecipient());
|
||||||
|
|
||||||
byte[] recipientsReference = recipient.getLastReference();
|
byte[] recipientsReference = recipient.getLastReference();
|
||||||
assertTrue("Recipient's new reference incorrect", Arrays.equals(multiPaymentTransaction.getTransactionData().getSignature(), recipientsReference));
|
assertTrue(Arrays.equals(multiPaymentTransaction.getTransactionData().getSignature(), recipientsReference), "Recipient's new reference incorrect");
|
||||||
|
|
||||||
// Amount should be in recipient's balance
|
// Amount should be in recipient's balance
|
||||||
expectedBalance = paymentData.getAmount();
|
expectedBalance = paymentData.getAmount();
|
||||||
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Recipient's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Recipient's new balance incorrect");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1129,11 +1129,11 @@ public class TransactionTests {
|
|||||||
|
|
||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's reverted balance incorrect", initialSenderBalance.compareTo(actualBalance) == 0);
|
assertTrue(initialSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
||||||
|
|
||||||
// Check generator's balance
|
// Check generator's balance
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's new balance incorrect", initialGeneratorBalance.compareTo(actualBalance) == 0);
|
assertTrue(initialGeneratorBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -1163,8 +1163,8 @@ public class TransactionTests {
|
|||||||
block.addTransaction(messageTransactionData);
|
block.addTransaction(messageTransactionData);
|
||||||
block.sign();
|
block.sign();
|
||||||
|
|
||||||
assertTrue("Block signatures invalid", block.isSignatureValid());
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
||||||
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
@ -1172,17 +1172,17 @@ public class TransactionTests {
|
|||||||
// Check sender's balance
|
// Check sender's balance
|
||||||
BigDecimal expectedBalance = initialSenderBalance.subtract(amount).subtract(fee);
|
BigDecimal expectedBalance = initialSenderBalance.subtract(amount).subtract(fee);
|
||||||
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
||||||
|
|
||||||
// Fee should be in generator's balance
|
// Fee should be in generator's balance
|
||||||
expectedBalance = initialGeneratorBalance.add(fee);
|
expectedBalance = initialGeneratorBalance.add(fee);
|
||||||
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
||||||
|
|
||||||
// Amount should be in recipient's balance
|
// Amount should be in recipient's balance
|
||||||
expectedBalance = amount;
|
expectedBalance = amount;
|
||||||
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
||||||
assertTrue("Recipient's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Recipient's new balance incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
42
src/test/utils/AssertExtensions.java
Normal file
42
src/test/utils/AssertExtensions.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package test.utils;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.Class;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
|
public class AssertExtensions {
|
||||||
|
|
||||||
|
public static <T> void assertItemsEqual(Collection<T> expected, Iterable<T> actual, EqualityComparer<T> comparer) {
|
||||||
|
assertItemsEqual(expected, actual, comparer, (String)null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void assertItemsEqual(Collection<T> expected, Iterable<T> actual, EqualityComparer<T> comparer, String message) {
|
||||||
|
List<EquatableWrapper<T>> expectedSet = new ArrayList<EquatableWrapper<T>>();
|
||||||
|
for(T item: expected)
|
||||||
|
expectedSet.add(new EquatableWrapper<T>(item, comparer));
|
||||||
|
|
||||||
|
List<EquatableWrapper<T>> actualSet = new ArrayList<EquatableWrapper<T>>();
|
||||||
|
for(T item: actual)
|
||||||
|
actualSet.add(new EquatableWrapper<T>(item, comparer));
|
||||||
|
|
||||||
|
assertItemsEqual(expectedSet, actualSet, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void assertItemsEqual(Collection<T> expected, Iterable<T> actual) {
|
||||||
|
assertItemsEqual(expected, actual, (String)null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void assertItemsEqual(Collection<T> expected, Iterable<T> actual, String message) {
|
||||||
|
List<T> list = new ArrayList<T>();
|
||||||
|
T[] expectedArray = (T[])expected.toArray();
|
||||||
|
assertThat(message, actual, containsInAnyOrder(expectedArray));
|
||||||
|
}
|
||||||
|
}
|
6
src/test/utils/EqualityComparer.java
Normal file
6
src/test/utils/EqualityComparer.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package test.utils;
|
||||||
|
|
||||||
|
public interface EqualityComparer<T> {
|
||||||
|
boolean equals(T first, T second);
|
||||||
|
int hashCode(T item);
|
||||||
|
}
|
34
src/test/utils/EquatableWrapper.java
Normal file
34
src/test/utils/EquatableWrapper.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package test.utils;
|
||||||
|
|
||||||
|
class EquatableWrapper<T> {
|
||||||
|
|
||||||
|
private final T item;
|
||||||
|
private final EqualityComparer<T> comparer;
|
||||||
|
|
||||||
|
public EquatableWrapper(T item, EqualityComparer<T> comparer) {
|
||||||
|
this.item = item;
|
||||||
|
this.comparer = comparer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if(obj == null)
|
||||||
|
return false;
|
||||||
|
if (!(this.getClass().isInstance(obj)))
|
||||||
|
return false;
|
||||||
|
EquatableWrapper<T> otherWrapper = (EquatableWrapper<T>)obj;
|
||||||
|
if (otherWrapper.item == this.item)
|
||||||
|
return true;
|
||||||
|
return this.comparer.equals(this.item, otherWrapper.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.comparer.hashCode(this.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.item.toString();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user