Compare commits

..

No commits in common. "master" and "1.0.0" have entirely different histories.

85 changed files with 934 additions and 1645 deletions

View file

@ -1,62 +1,53 @@
version: 2.1 version: 2
orbs:
sdkman: joshdholtz/sdkman@0.2.0
defaults: &defaults defaults: &defaults
working_directory: ~/repo working_directory: ~/repo
environment: environment:
JVM_OPTS: -Xmx3200m JVM_OPTS: -Xmx3200m
TERM: dumb TERM: dumb
CI_NAME: "CircleCI" CI: true
commands: defaults_gradle: &defaults_gradle
build_and_test:
parameters:
reports-dir:
type: string
default: "build/reports/test_results"
steps: steps:
- checkout - checkout
- sdkman/setup-sdkman - restore_cache:
- sdkman/sdkman-install: keys:
candidate: kotlin - gradle-dependencies-{{ checksum "build.gradle.kts" }}
version: 2.1.10 # fallback to using the latest cache if no exact match is found
- gradle-dependencies-
- run: - run:
name: Download dependencies name: Gradle Dependencies
command: ./bld download command: ./gradlew dependencies
- save_cache:
paths: ~/.m2
key: gradle-dependencies-{{ checksum "build.gradle.kts" }}
- run: - run:
name: Compile source name: Run All Checks
command: ./bld compile command: ./gradlew check
- run:
name: Run tests
command: ./bld jacoco -reports-dir=<< parameters.reports-dir >>
- store_test_results:
path: << parameters.reports-dir >>
- store_artifacts: - store_artifacts:
path: build/reports/jacoco/test/html path: build/reports/
destination: reports
- store_test_results:
path: build/reports/
jobs: jobs:
bld_jdk17: build_gradle_jdk18:
<<: *defaults <<: *defaults
docker: docker:
- image: cimg/openjdk:17.0 - image: cimg/openjdk:18.0
steps: <<: *defaults_gradle
- build_and_test
bld_jdk21: build_gradle_jdk11:
<<: *defaults <<: *defaults
docker: docker:
- image: cimg/openjdk:21.0 - image: cimg/openjdk:11.0
steps: <<: *defaults_gradle
- build_and_test
workflows: workflows:
bld: version: 2
gradle:
jobs: jobs:
- bld_jdk17 - build_gradle_jdk11
- bld_jdk21 - build_gradle_jdk18

View file

@ -1,68 +0,0 @@
name: bld-ci
on: [ push, pull_request, workflow_dispatch ]
env:
COVERAGE_JDK: "21"
COVERAGE_KOTLIN: "2.1.20"
jobs:
build-bld-project:
strategy:
matrix:
java-version: [ 17, 21, 24 ]
kotlin-version: [ 1.9.25, 2.0.21, 2.1.20 ]
os: [ ubuntu-latest, windows-latest, macos-latest ]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout source repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK ${{ matrix.java-version }} with Kotlin ${{ matrix.kotlin-version }}
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
- name: Download dependencies [bld example]
working-directory: examples/bld
run: ./bld download
- name: Compile and run examples [bld example]
working-directory: examples/bld
run: |
./bld compile
./bld run
./bld run-java
- name: Run examples [gradle example]
working-directory: examples/gradle
run: |
./gradlew run
./gradlew runJava
- name: Download dependencies
run: ./bld download
- name: Compile source
run: ./bld compile
- name: Run tests
run: ./bld jacoco
- name: Remove pom.xml
if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.kotlin-version == env.COVERAGE_KOTLIN
&& matrix.os == 'ubuntu-latest'
run: rm -rf pom.xml
- name: SonarCloud Scan
uses: sonarsource/sonarcloud-github-action@master
if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.kotlin-version == env.COVERAGE_KOTLIN
&& matrix.os == 'ubuntu-latest'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

61
.github/workflows/gradle.yml vendored Normal file
View file

@ -0,0 +1,61 @@
name: gradle-ci
on: [push, pull_request, workflow_dispatch]
jobs:
build:
runs-on: ubuntu-latest
env:
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m"
SONAR_JDK: "11"
strategy:
matrix:
java-version: [ 11, 18 ]
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up JDK ${{ matrix.java-version }}
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java-version }}
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Cache SonarCloud packages
if: matrix.java-version == env.SONAR_JDK
uses: actions/cache@v1
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Gradle packages
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ matrix.java-version }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-${{ matrix.java-version }}-
- name: Test with Gradle
run: ./gradlew build check --stacktrace
- name: SonarCloud
if: success() && matrix.java-version == env.SONAR_JDK
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: ./gradlew sonarqube
- name: Cleanup Gradle Cache
run: |
rm -f ~/.gradle/caches/modules-2/modules-2.lock
rm -f ~/.gradle/caches/modules-2/gc.properties

136
.gitignore vendored
View file

@ -1,57 +1,85 @@
.gradle !.vscode/extensions.json
!.vscode/launch.json
!.vscode/settings.json
!.vscode/tasks.json
*.class
*.code-workspace
*.ctxt
*.iws
*.log
*.nar
*.rar
*.sublime-*
*.tar.gz
*.zip
.DS_Store .DS_Store
build .classpath
lib/bld/** .gradle
!lib/bld/bld-wrapper.jar .history
!lib/bld/bld-wrapper.properties .kobalt
lib/compile/ .mtj.tmp/
lib/runtime/ .mvn/timing.properties
lib/standalone/ .mvn/wrapper/maven-wrapper.jar
lib/test/ .nb-gradle
.project
# IDEA ignores .scannerwork
.settings
# User-specific .vscode/*
.idea/**/workspace.xml /**/.idea/$CACHE_FILE$
.idea/**/tasks.xml /**/.idea/$PRODUCT_WORKSPACE_FILE$
.idea/**/usage.statistics.xml /**/.idea/**/caches/build_file_checksums.ser
.idea/**/dictionaries /**/.idea/**/contentModel.xml
.idea/**/shelf /**/.idea/**/dataSources.ids
/**/.idea/**/dataSources.local.xml
# AWS User-specific /**/.idea/**/dataSources/
.idea/**/aws.xml /**/.idea/**/dbnavigator.xml
/**/.idea/**/dictionaries
# Generated files /**/.idea/**/dynamic.xml
.idea/**/contentModel.xml /**/.idea/**/gradle.xml
/**/.idea/**/httpRequests
# Sensitive or high-churn files /**/.idea/**/libraries
.idea/**/dataSources/ /**/.idea/**/mongoSettings.xml
.idea/**/dataSources.ids /**/.idea/**/replstate.xml
.idea/**/dataSources.local.xml /**/.idea/**/shelf
.idea/**/sqlDataSources.xml /**/.idea/**/shelf/
.idea/**/dynamic.xml /**/.idea/**/sqlDataSources.xml
.idea/**/uiDesigner.xml /**/.idea/**/tasks.xml
.idea/**/dbnavigator.xml /**/.idea/**/uiDesigner.xml
/**/.idea/**/usage.statistics.xml
# Gradle /**/.idea/**/workspace.xml
.idea/**/gradle.xml /**/.idea/sonarlint*
/**/.idea_modules/
# Mongo Explorer plugin Thumbs.db
.idea/**/mongoSettings.xml __pycache__
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml atlassian-ide-plugin.xml
bin/
# Cursive Clojure plugin build/
.idea/replstate.xml cmake-build-*/
com_crashlytics_export_strings.xml
# SonarLint plugin crashlytics-build.properties
.idea/sonarlint/ crashlytics.properties
dependency-reduced-pom.xml
# Editor-based Rest Client deploy/
.idea/httpRequests dist/
ehthumbs.db
fabric.properties
gen/
gradle.properties
hs_err_pid*
kobaltBuild
kobaltw*-test
lib/kotlin*
libs/
local.properties local.properties
out/
pom.xml.asc
pom.xml.next
pom.xml.releaseBackup
pom.xml.tag
pom.xml.versionsBackup
proguard-project.txt
project.properties
release.properties
target/
test-output
venv

View file

@ -1,24 +1,31 @@
image: fedora:latest image: gradle:7-jdk11
variables: variables:
CI_NAME: "GitLab CI" GRADLE_OPTS: "-Dorg.gradle.daemon=false"
stages:
- test
before_script: before_script:
- dnf -qy update && dnf -y install zip - export GRADLE_USER_HOME=`pwd`/.gradle
- curl -s "https://get.sdkman.io" | bash
- echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config stages:
- echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config - build
- source "$HOME/.sdkman/bin/sdkman-init.sh" - test
- sdk install java
- sdk install kotlin build:
- source "$HOME/.sdkman/bin/sdkman-init.sh" stage: build
script: gradle --build-cache assemble
cache:
key: "$CI_COMMIT_REF_NAME"
policy: push
paths:
- build
- .gradle
test: test:
stage: test stage: test
script: script: gradle check
- ./bld download cache:
- ./bld compile key: "$CI_COMMIT_REF_NAME"
- ./bld test policy: pull
paths:
- build
- .gradle

32
.idea/app.iml generated
View file

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/build/main" />
<output-test url="file://$MODULE_DIR$/build/test" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" relativeOutputPath="resources" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/kotlin" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/kotlin/net/thauvin/erik/crypto" type="kotlin-source" />
<sourceFolder url="file://$MODULE_DIR$/src/test/kotlin/net/thauvin/erik/crypto" type="kotlin-test" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library" scope="RUNTIME">
<library>
<CLASSES>
<root url="file://$MODULE_DIR$/src/main/resources/templates" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" name="compile" level="project" />
<orderEntry type="library" scope="RUNTIME" name="runtime" level="project" />
<orderEntry type="library" scope="TEST" name="test" level="project" />
</component>
</module>

14
.idea/bld.iml generated
View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/build/bld" />
<output-test url="file://$MODULE_DIR$/build/bld" />
<exclude-output />
<content url="file://$MODULE_DIR$/src/bld">
<sourceFolder url="file://$MODULE_DIR$/src/bld/java" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="bld" level="project" />
</component>
</module>

6
.idea/bld.xml generated
View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="BldConfiguration">
<events />
</component>
</project>

View file

@ -1,6 +1,6 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<copyright> <copyright>
<option name="notice" value="&amp;#36;file.fileName&#10;&#10;Copyright 2021-&amp;#36;today.year Erik C. Thauvin (erik@thauvin.net)&#10;&#10;Redistribution and use in source and binary forms, with or without&#10;modification, are permitted provided that the following conditions are met:&#10;&#10; Redistributions of source code must retain the above copyright notice, this&#10; list of conditions and the following disclaimer.&#10;&#10; Redistributions in binary form must reproduce the above copyright notice,&#10; this list of conditions and the following disclaimer in the documentation&#10; and/or other materials provided with the distribution.&#10;&#10; Neither the name of this project nor the names of its contributors may be&#10; used to endorse or promote products derived from this software without&#10; specific prior written permission.&#10;&#10;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot;&#10;AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE&#10;IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE&#10;DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE&#10;FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL&#10;DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR&#10;SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER&#10;CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,&#10;OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE&#10;OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." /> <option name="notice" value="&amp;#36;file.fileName&#10;&#10;Copyright (c) 2021-&amp;#36;today.year, Erik C. Thauvin (erik@thauvin.net)&#10;All rights reserved.&#10;&#10;Redistribution and use in source and binary forms, with or without&#10;modification, are permitted provided that the following conditions are met:&#10;&#10; Redistributions of source code must retain the above copyright notice, this&#10; list of conditions and the following disclaimer.&#10;&#10; Redistributions in binary form must reproduce the above copyright notice,&#10; this list of conditions and the following disclaimer in the documentation&#10; and/or other materials provided with the distribution.&#10;&#10; Neither the name of this project nor the names of its contributors may be&#10; used to endorse or promote products derived from this software without&#10; specific prior written permission.&#10;&#10;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot;&#10;AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE&#10;IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE&#10;DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE&#10;FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL&#10;DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR&#10;SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER&#10;CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,&#10;OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE&#10;OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." />
<option name="myName" value="Erik's Copyright Notice" /> <option name="myName" value="Erik's Copyright Notice" />
</copyright> </copyright>
</component> </component>

View file

@ -32,49 +32,12 @@
<option name="IGNORE_POINT_TO_ITSELF" value="false" /> <option name="IGNORE_POINT_TO_ITSELF" value="false" />
<option name="myAdditionalJavadocTags" value="created" /> <option name="myAdditionalJavadocTags" value="created" />
</inspection_tool> </inspection_tool>
<inspection_tool class="JavadocDeclaration" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ADDITIONAL_TAGS" value="created" />
</inspection_tool>
<inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true">
<option name="REPORT_VARIABLES" value="true" /> <option name="REPORT_VARIABLES" value="true" />
<option name="REPORT_PARAMETERS" value="true" /> <option name="REPORT_PARAMETERS" value="true" />
<option name="REPORT_CATCH_PARAMETERS" value="false" /> <option name="REPORT_CATCH_PARAMETERS" value="false" />
<option name="REPORT_IMPLICIT_FINALS" value="false" /> <option name="REPORT_IMPLICIT_FINALS" value="false" />
</inspection_tool> </inspection_tool>
<inspection_tool class="MissingJavadoc" enabled="true" level="WARNING" enabled_by_default="true">
<option name="PACKAGE_SETTINGS">
<Options>
<option name="ENABLED" value="false" />
</Options>
</option>
<option name="MODULE_SETTINGS">
<Options>
<option name="ENABLED" value="false" />
</Options>
</option>
<option name="TOP_LEVEL_CLASS_SETTINGS">
<Options>
<option name="ENABLED" value="false" />
</Options>
</option>
<option name="INNER_CLASS_SETTINGS">
<Options>
<option name="ENABLED" value="false" />
</Options>
</option>
<option name="METHOD_SETTINGS">
<Options>
<option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
<option name="ENABLED" value="false" />
</Options>
</option>
<option name="FIELD_SETTINGS">
<Options>
<option name="ENABLED" value="false" />
</Options>
</option>
</inspection_tool>
<inspection_tool class="RedundantExplicitVariableType" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> <inspection_tool class="RedundantExplicitVariableType" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UsePropertyAccessSyntax" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
</profile> </profile>
</component> </component>

View file

@ -1,204 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaDocConfiguration">
<GENERAL>
<MODE>UPDATE</MODE>
<OVERRIDDEN_METHODS>false</OVERRIDDEN_METHODS>
<SPLITTED_CLASS_NAME>true</SPLITTED_CLASS_NAME>
<LEVELS>
<LEVEL>TYPE</LEVEL>
<LEVEL>METHOD</LEVEL>
<LEVEL>FIELD</LEVEL>
</LEVELS>
<VISIBILITIES>
<VISIBILITY>DEFAULT</VISIBILITY>
<VISIBILITY>PUBLIC</VISIBILITY>
<VISIBILITY>PROTECTED</VISIBILITY>
</VISIBILITIES>
</GENERAL>
<TEMPLATES>
<CLASSES>
<CLASS>
<KEY>^.*(public|protected|private)*.+interface\s+\w+.*</KEY>
<VALUE>/**\n
* The interface ${name}.\n
&lt;#if element.typeParameters?has_content&gt; * \n
&lt;/#if&gt;
&lt;#list element.typeParameters as typeParameter&gt;
* @param &lt;${typeParameter.name}&gt; the type parameter\n
&lt;/#list&gt;
*/</VALUE>
</CLASS>
<CLASS>
<KEY>^.*(public|protected|private)*.+enum\s+\w+.*</KEY>
<VALUE>/**\n
* The enum ${name}.\n
*/</VALUE>
</CLASS>
<CLASS>
<KEY>^.*(public|protected|private)*.+class\s+\w+.*</KEY>
<VALUE>/**\n
* The type ${name}.\n
&lt;#if element.typeParameters?has_content&gt; * \n
&lt;/#if&gt;
&lt;#list element.typeParameters as typeParameter&gt;
* @param &lt;${typeParameter.name}&gt; the type parameter\n
&lt;/#list&gt;
*/</VALUE>
</CLASS>
<CLASS>
<KEY>.+</KEY>
<VALUE>/**\n
* The type ${name}.\n
*/</VALUE>
</CLASS>
</CLASSES>
<CONSTRUCTORS>
<CONSTRUCTOR>
<KEY>.+</KEY>
<VALUE>/**\n
* Instantiates a new ${name}.\n
&lt;#if element.parameterList.parameters?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.parameterList.parameters as parameter&gt;
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
&lt;/#list&gt;
&lt;#if element.throwsList.referenceElements?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.throwsList.referenceElements as exception&gt;
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
&lt;/#list&gt;
*/</VALUE>
</CONSTRUCTOR>
</CONSTRUCTORS>
<METHODS>
<METHOD>
<KEY>^.*(public|protected|private)*\s*.*(\w(\s*&lt;.+&gt;)*)+\s+get\w+\s*\(.*\).+</KEY>
<VALUE>/**\n
* Gets ${partName}.\n
&lt;#if element.typeParameters?has_content&gt; * \n
&lt;/#if&gt;
&lt;#list element.typeParameters as typeParameter&gt;
* @param &lt;${typeParameter.name}&gt; the type parameter\n
&lt;/#list&gt;
&lt;#if element.parameterList.parameters?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.parameterList.parameters as parameter&gt;
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
&lt;/#list&gt;
&lt;#if isNotVoid&gt;
*\n
* @return the ${partName}\n
&lt;/#if&gt;
&lt;#if element.throwsList.referenceElements?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.throwsList.referenceElements as exception&gt;
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
&lt;/#list&gt;
*/</VALUE>
</METHOD>
<METHOD>
<KEY>^.*(public|protected|private)*\s*.*(void|\w(\s*&lt;.+&gt;)*)+\s+set\w+\s*\(.*\).+</KEY>
<VALUE>/**\n
* Sets ${partName}.\n
&lt;#if element.typeParameters?has_content&gt; * \n
&lt;/#if&gt;
&lt;#list element.typeParameters as typeParameter&gt;
* @param &lt;${typeParameter.name}&gt; the type parameter\n
&lt;/#list&gt;
&lt;#if element.parameterList.parameters?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.parameterList.parameters as parameter&gt;
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
&lt;/#list&gt;
&lt;#if isNotVoid&gt;
*\n
* @return the ${partName}\n
&lt;/#if&gt;
&lt;#if element.throwsList.referenceElements?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.throwsList.referenceElements as exception&gt;
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
&lt;/#list&gt;
*/</VALUE>
</METHOD>
<METHOD>
<KEY>^.*((public\s+static)|(static\s+public))\s+void\s+main\s*\(\s*String\s*(\[\s*\]|\.\.\.)\s+\w+\s*\).+</KEY>
<VALUE>/**\n
* The entry point of application.\n
&lt;#if element.parameterList.parameters?has_content&gt;
*\n
&lt;/#if&gt;
* @param ${element.parameterList.parameters[0].name} the input arguments\n
&lt;#if element.throwsList.referenceElements?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.throwsList.referenceElements as exception&gt;
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
&lt;/#list&gt;
*/</VALUE>
</METHOD>
<METHOD>
<KEY>.+</KEY>
<VALUE>/**\n
* ${name}&lt;#if isNotVoid&gt; ${return}&lt;/#if&gt;.\n
&lt;#if element.typeParameters?has_content&gt; * \n
&lt;/#if&gt;
&lt;#list element.typeParameters as typeParameter&gt;
* @param &lt;${typeParameter.name}&gt; the type parameter\n
&lt;/#list&gt;
&lt;#if element.parameterList.parameters?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.parameterList.parameters as parameter&gt;
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
&lt;/#list&gt;
&lt;#if isNotVoid&gt;
*\n
* @return the ${return}\n
&lt;/#if&gt;
&lt;#if element.throwsList.referenceElements?has_content&gt;
*\n
&lt;/#if&gt;
&lt;#list element.throwsList.referenceElements as exception&gt;
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
&lt;/#list&gt;
*/</VALUE>
</METHOD>
</METHODS>
<FIELDS>
<FIELD>
<KEY>^.*(public|protected|private)*.+static.*(\w\s\w)+.+</KEY>
<VALUE>/**\n
* The constant ${element.getName()}.\n
*/</VALUE>
</FIELD>
<FIELD>
<KEY>^.*(public|protected|private)*.*(\w\s\w)+.+</KEY>
<VALUE>/**\n
&lt;#if element.parent.isInterface()&gt;
* The constant ${element.getName()}.\n
&lt;#else&gt;
* The ${name}.\n
&lt;/#if&gt; */</VALUE>
</FIELD>
<FIELD>
<KEY>.+</KEY>
<VALUE>/**\n
&lt;#if element.parent.isEnum()&gt;
*${name} ${typeName}.\n
&lt;#else&gt;
* The ${name}.\n
&lt;/#if&gt;*/</VALUE>
</FIELD>
</FIELDS>
</TEMPLATES>
</component>
</project>

12
.idea/kotlinc.xml generated
View file

@ -1,16 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Kotlin2JsCompilerArguments">
<option name="moduleKind" value="plain" />
</component>
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
</component>
<component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.0" />
<option name="languageVersion" value="2.0" />
</component>
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="2.0.0" /> <option name="version" value="1.7.10" />
</component> </component>
</project> </project>

View file

@ -1,18 +0,0 @@
<component name="libraryTable">
<library name="bld">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/bld" />
<root url="jar://$USER_HOME$/.bld/dist/bld-2.2.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/bld" />
<root url="jar://$USER_HOME$/.bld/dist/bld-2.2.1-sources.jar!/" />
</SOURCES>
<excluded>
<root url="jar://$PROJECT_DIR$/lib/bld/bld-wrapper.jar!/" />
</excluded>
<jarDirectory url="file://$PROJECT_DIR$/lib/bld" recursive="false" />
<jarDirectory url="file://$PROJECT_DIR$/lib/bld" recursive="false" type="SOURCES" />
</library>
</component>

View file

@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="compile">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/compile" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/compile" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="true" />
<jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="true" type="SOURCES" />
</library>
</component>

View file

@ -1,14 +0,0 @@
<component name="libraryTable">
<library name="runtime">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/runtime" />
<root url="file://$PROJECT_DIR$/src/main/resources" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/runtime" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="true" />
<jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="true" type="SOURCES" />
</library>
</component>

View file

@ -1,14 +0,0 @@
<component name="libraryTable">
<library name="test">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/test" />
<root url="file://$PROJECT_DIR$/src/test/resources" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/test" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="true" />
<jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="true" type="SOURCES" />
</library>
</component>

13
.idea/misc.xml generated
View file

@ -1,17 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="EntryPointsManager">
<pattern value="net.thauvin.erik.crypto.CryptoPriceBuild" />
<pattern value="net.thauvin.erik.crypto.CryptoPriceBuild" method="jacoco" />
<pattern value="net.thauvin.erik.crypto.CryptoPriceBuild" method="detekt" />
<pattern value="net.thauvin.erik.crypto.CryptoPriceBuild" method="detektBaseline" />
<pattern value="net.thauvin.erik.crypto.CryptoPrice.Companion" method="main" />
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration"> <component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$/examples/gradle" /> <file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="18" project-jdk-type="JavaSDK" />
</project> </project>

9
.idea/modules.xml generated
View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/app.iml" filepath="$PROJECT_DIR$/.idea/app.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/bld.iml" filepath="$PROJECT_DIR$/.idea/bld.iml" />
</modules>
</component>
</project>

View file

@ -1,4 +1,5 @@
Copyright 2021-2025 Erik C. Thauvin (erik@thauvin.net) Copyright (c) 2021-2022, Erik C. Thauvin (erik@thauvin.net)
All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:

View file

@ -1,17 +1,10 @@
[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](https://opensource.org/licenses/BSD-3-Clause) [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](https://opensource.org/licenses/BSD-3-Clause) [![Release](https://img.shields.io/github/release/ethauvin/cryptoprice.svg)](https://github.com/ethauvin/cryptoprice/releases/latest) [![Maven Central](https://img.shields.io/maven-central/v/net.thauvin.erik/cryptoprice.svg?label=maven%20central&color=blue)](https://search.maven.org/search?q=g:%22net.thauvin.erik%22%20AND%20a:%22cryptoprice%22) <!-- [![Nexus Snapshot](https://img.shields.io/nexus/s/net.thauvin.erik/cryptoprice?server=https%3A%2F%2Foss.sonatype.org%2F)](https://oss.sonatype.org/content/repositories/snapshots/net/thauvin/erik/cryptoprice/) -->
[![Kotlin](https://img.shields.io/badge/kotlin-2.1.20-7f52ff)](https://kotlinlang.org/)
[![bld](https://img.shields.io/badge/2.2.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld)
[![Release](https://img.shields.io/github/release/ethauvin/cryptoprice.svg)](https://github.com/ethauvin/cryptoprice/releases/latest)
[![Maven Central](https://img.shields.io/maven-central/v/net.thauvin.erik/cryptoprice)](https://central.sonatype.com/artifact/net.thauvin.erik/cryptoprice)
[![Nexus Snapshot](https://img.shields.io/nexus/s/net.thauvin.erik/cryptoprice?label=snapshot&server=https%3A%2F%2Foss.sonatype.org%2F)](https://oss.sonatype.org/content/repositories/snapshots/net/thauvin/erik/cryptoprice/)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_cryptoprice&metric=alert_status)](https://sonarcloud.io/dashboard?id=ethauvin_cryptoprice) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/cryptoprice/badge.svg?targetFile=pom.xml)](https://snyk.io/test/github/ethauvin/cryptoprice?targetFile=pom.xml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_cryptoprice&metric=alert_status)](https://sonarcloud.io/dashboard?id=ethauvin_cryptoprice) [![GitHub CI](https://github.com/ethauvin/cryptoprice/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/cryptoprice/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/cryptoprice/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/cryptoprice/tree/master)
[![GitHub CI](https://github.com/ethauvin/cryptoprice/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/cryptoprice/actions/workflows/bld.yml)
[![CircleCI](https://circleci.com/gh/ethauvin/cryptoprice/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/cryptoprice/tree/master)
# Retrieve cryptocurrencies current (buy, sell or spot) prices # Retrieve cryptocurrencies current (buy, sell or spot) prices
A simple implementation of the prices [Coinbase Public API](https://docs.cdp.coinbase.com/coinbase-app/docs/api-prices). A simple Kotlin/Java/Android implementation of the prices [Coinbase Public API](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices).
## Examples (TL;DR) ## Examples (TL;DR)
@ -30,36 +23,19 @@ val eth = buyPrice("LTC", "GBP") // Litecoin in Pound sterling
println(eth.amount) println(eth.amount)
``` ```
- View [bld](https://github.com/ethauvin/cryptoprice/blob/master/examples/bld) or [Gradle](https://github.com/ethauvin/cryptoprice/blob/master/examples/gradle) Examples. - View [Kotlin](https://github.com/ethauvin/cryptoprice/blob/master/examples/src/main/kotlin/com/example/CryptoPriceExample.kt) or [Java](https://github.com/ethauvin/cryptoprice/blob/master/examples/src/main/java/com/example/CryptoPriceSample.java) Examples.
### bld
To use with [bld](https://rife2.com/bld), include the following dependency in your [build](https://github.com/ethauvin/cryptoprice/blob/master/examples/bld/src/bld/java/com/example/CryptoPriceExampleBuild.java) file:
```java
repositories = List.of(MAVEN_CENTRAL);
scope(compile)
.include(dependency("net.thauvin.erik:cryptoprice:1.0.2"));
```
Be sure to use the [bld Kotlin extension](https://github.com/rife2/bld-kotlin) in your project.
### Gradle, Maven, etc. ### Gradle, Maven, etc.
To use with [Gradle](https://gradle.org/), include the following dependency in your [build](https://github.com/ethauvin/cryptoprice/blob/master/examples/gradle/build.gradle.kts) file: To use with [Gradle](https://gradle.org/), include the following dependency in your [build](https://github.com/ethauvin/cryptoprice/blob/master/examples/build.gradle.kts) file:
```gradle ```gradle
repositories {
mavenCentral()
maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } // only needed for SNAPSHOT
}
dependencies { dependencies {
implementation("net.thauvin.erik:cryptoprice:1.0.2") implementation("net.thauvin.erik:cryptoprice:1.0.0")
} }
``` ```
Instructions for using with Maven, Ivy, etc. can be found on [Maven Central](https://central.sonatype.com/artifact/net.thauvin.erik/cryptoprice). Instructions for using with Maven, Ivy, etc. can be found on [Maven Central](https://search.maven.org/search?q=g:%22net.thauvin.erik%22%20AND%20a:%22cryptoprice%22).
### Prices ### Prices
@ -94,7 +70,7 @@ A `CryptoPrice` object is returned defined as follows:
```kotlin ```kotlin
CryptoPrice(val base: String, val currency: String, val amount: BigDecimal) CryptoPrice(val base: String, val currency: String, val amount: BigDecimal)
``` ```
The parameter names match the [Coinbase API](https://docs.cdp.coinbase.com/coinbase-app/docs/api-prices). The parameter names match the [Coinbase API](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices).
#### Format #### Format
@ -105,7 +81,7 @@ val euro = CryptoPrice("BTC", "EUR", 23456.78.toBigDecimal())
println(euro.toCurrency()) // €23,456.78 println(euro.toCurrency()) // €23,456.78
val krone = CryptoPrice("BTC", "DKK", 123456.78.toBigDecimal()) val krone = CryptoPrice("BTC", "DKK", 123456.78.toBigDecimal())
println(krone.toCurrency(Locale.Builder().setLanguage("da").setRegion("DK").build())) // 123.456,78 kr. println(krone.toCurrency(Locale("da", "DK"))) // 123.456,78 kr.
``` ```
##### JSON ##### JSON
@ -123,7 +99,7 @@ println(price.toJson())
{"data":{"base":"BTC","currency":"USD","amount":"34567.89"}} {"data":{"base":"BTC","currency":"USD","amount":"34567.89"}}
``` ```
The `data` object matches the [Coinbase API](https://docs.cdp.coinbase.com/coinbase-app/docs/api-prices). To specify a different (or no) key, use: The `data` object matches the [Coinbase API](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-prices). To specify a different (or no) key, use:
```kotlin ```kotlin
println(price.toJson("bitcoin")) println(price.toJson("bitcoin"))
@ -146,7 +122,7 @@ val eth = """{"ether":{"base":"ETH","currency":"USD","amount":"2345.67"}}""".toP
### Extending ### Extending
A generic `apiCall()` function is available to access other [data API endpoints](https://docs.cdp.coinbase.com/coinbase-app/docs/api-currencies). For example to retrieve the [exchange rates](https://docs.cdp.coinbase.com/coinbase-app/docs/api-exchange-rates): A generic `apiCall()` function is available to access other [data API endpoints](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-currencies). For example to retrieve the [exchange rates](https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-exchange-rates#get-exchange-rates):
```kotlin ```kotlin
apiCall(listOf("exchange-rates"), mapOf("currency" to "usd")) apiCall(listOf("exchange-rates"), mapOf("currency" to "usd"))
@ -158,22 +134,3 @@ will return something like:
``` ```
See the [examples](https://github.com/ethauvin/cryptoprice/blob/master/examples/) for more details. See the [examples](https://github.com/ethauvin/cryptoprice/blob/master/examples/) for more details.
## Contributing
If you want to contribute to this project, all you have to do is clone the GitHub
repository:
```console
git clone git@github.com:ethauvin/cryptoprice.git
```
Then use [bld](https://rife2.com/bld) to build:
```console
cd cryptoprice
./bld compile
```
The project has an [IntelliJ IDEA](https://www.jetbrains.com/idea/) project structure. You can just open it after all
the dependencies were downloaded and peruse the code.

8
baseline.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ThrowsCount:CryptoPrice.kt$CryptoPrice.Companion$ @JvmStatic @JvmOverloads @Throws(CryptoException::class, IOException::class) fun apiCall(paths: List&lt;String>, params: Map&lt;String, String> = emptyMap()): String</ID>
<ID>ThrowsCount:CryptoPrice.kt$CryptoPrice.Companion$@JvmStatic @Throws(CryptoException::class) fun String.toPrice(): CryptoPrice</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -1,20 +1,9 @@
image: ubuntu:latest image: openjdk:18
pipelines: pipelines:
default: default:
- step: - step:
name: Test with bld caches:
- gradle
script: script:
# Install latest Java & Kotlin via SDKMAN! - bash ./gradlew check
- apt-get update -qq && apt-get install -y curl zip
- curl -s "https://get.sdkman.io" | bash
- echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config
- echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config
- source "$HOME/.sdkman/bin/sdkman-init.sh"
- sdk install java
- sdk install kotlin
- source "$HOME/.sdkman/bin/sdkman-init.sh"
# Download, compile and test with bld
- ./bld download
- ./bld compile
- ./bld test

2
bld
View file

@ -1,2 +0,0 @@
#!/usr/bin/env sh
java -jar "$(dirname "$0")/lib/bld/bld-wrapper.jar" "$0" --build net.thauvin.erik.crypto.CryptoPriceBuild "$@"

View file

@ -1,4 +0,0 @@
@echo off
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
java -jar "%DIRNAME%/lib/bld/bld-wrapper.jar" "%0" --build net.thauvin.erik.crypto.CryptoPriceBuild %*

195
build.gradle.kts Normal file
View file

@ -0,0 +1,195 @@
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
plugins {
id("application")
id("com.github.ben-manes.versions") version "0.42.0"
id("io.gitlab.arturbosch.detekt") version "1.21.0"
id("java")
id("maven-publish")
id("org.jetbrains.dokka") version "1.7.10"
id("org.jetbrains.kotlinx.kover") version "0.6.0"
id("org.sonarqube") version "3.4.0.2513"
id("signing")
kotlin("jvm") version "1.7.10"
}
defaultTasks(ApplicationPlugin.TASK_RUN_NAME)
description = "Retrieve cryptocurrencies prices."
group = "net.thauvin.erik"
version = "1.0.0"
val deployDir = "deploy"
val gitHub = "ethauvin/$name"
val mavenUrl = "https://github.com/$gitHub"
val publicationName = "mavenJava"
fun isNonStable(version: String): Boolean {
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.toUpperCase().contains(it) }
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
val isStable = stableKeyword || regex.matches(version)
return isStable.not()
}
repositories {
mavenCentral()
maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") }
}
dependencies {
implementation(platform(kotlin("bom")))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("org.json:json:20220320")
testImplementation(kotlin("test"))
}
application {
mainClass.set("net.thauvin.erik.crypto.CryptoPrice")
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
withSourcesJar()
}
detekt {
//toolVersion = "main-SNAPSHOT"
}
sonarqube {
properties {
property("sonar.projectKey", "ethauvin_$name")
property("sonar.organization", "ethauvin-github")
property("sonar.host.url", "https://sonarcloud.io")
property("sonar.sourceEncoding", "UTF-8")
property("sonar.coverage.jacoco.xmlReportPaths", "${project.buildDir}/reports/kover/xml/report.xml")
}
}
val javadocJar by tasks.creating(Jar::class) {
dependsOn(tasks.dokkaJavadoc)
from(tasks.dokkaJavadoc)
archiveClassifier.set("javadoc")
}
tasks {
named<JavaExec>("run") {
args = listOf("BTC","ETH","LTC")
}
withType<DependencyUpdatesTask> {
rejectVersionIf {
isNonStable(candidate.version)
}
}
withType<KotlinCompile>().configureEach {
kotlinOptions.jvmTarget = java.targetCompatibility.toString()
}
withType<Test> {
testLogging {
exceptionFormat = TestExceptionFormat.FULL
events = setOf(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED)
}
}
withType<GenerateMavenPom> {
destination = file("$projectDir/pom.xml")
}
clean {
doLast {
project.delete(fileTree(deployDir))
}
}
withType<DokkaTask>().configureEach {
dokkaSourceSets {
named("main") {
moduleName.set("CryptoPrice")
}
}
}
val copyToDeploy by registering(Copy::class) {
from(configurations.runtimeClasspath) {
exclude("annotations-*.jar")
}
from(jar)
into(deployDir)
}
register("deploy") {
description = "Copies all needed files to the $deployDir directory."
group = PublishingPlugin.PUBLISH_TASK_GROUP
dependsOn(clean, build, jar)
outputs.dir(deployDir)
inputs.files(copyToDeploy)
mustRunAfter(clean)
}
"sonarqube" {
dependsOn(koverReport)
}
}
publishing {
publications {
create<MavenPublication>(publicationName) {
from(components["java"])
artifact(javadocJar)
pom {
name.set(project.name)
description.set(project.description)
url.set(mavenUrl)
licenses {
license {
name.set("BSD 3-Clause")
url.set("https://opensource.org/licenses/BSD-3-Clause")
}
}
developers {
developer {
id.set("ethauvin")
name.set("Erik C. Thauvin")
email.set("erik@thauvin.net")
url.set("https://erik.thauvin.net/")
}
}
scm {
connection.set("scm:git:git://github.com/$gitHub.git")
developerConnection.set("scm:git:git@github.com:$gitHub.git")
url.set(mavenUrl)
}
issueManagement {
system.set("GitHub")
url.set("$mavenUrl/issues")
}
}
}
}
repositories {
maven {
name = "ossrh"
url = if (project.version.toString().contains("SNAPSHOT"))
uri("https://oss.sonatype.org/content/repositories/snapshots/") else
uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials(PasswordCredentials::class)
}
}
}
signing {
useGpgCmd()
sign(publishing.publications[publicationName])
}

View file

@ -2,7 +2,6 @@
<SmellBaseline> <SmellBaseline>
<ManuallySuppressedIssues/> <ManuallySuppressedIssues/>
<CurrentIssues> <CurrentIssues>
<ID>NestedBlockDepth:CryptoPrice.kt$CryptoPrice.Companion$@JvmStatic @JvmOverloads @Throws(CryptoException::class, IOException::class) fun apiCall(paths: List&lt;String>, params: Map&lt;String, String> = emptyMap()): String</ID>
<ID>ThrowsCount:CryptoPrice.kt$CryptoPrice.Companion$@JvmStatic @JvmOverloads @Throws(CryptoException::class, IOException::class) fun apiCall(paths: List&lt;String>, params: Map&lt;String, String> = emptyMap()): String</ID> <ID>ThrowsCount:CryptoPrice.kt$CryptoPrice.Companion$@JvmStatic @JvmOverloads @Throws(CryptoException::class, IOException::class) fun apiCall(paths: List&lt;String>, params: Map&lt;String, String> = emptyMap()): String</ID>
</CurrentIssues> </CurrentIssues>
</SmellBaseline> </SmellBaseline>

View file

@ -3,5 +3,3 @@
# Ignore Gradle build output directory # Ignore Gradle build output directory
build build
bin

View file

@ -1,55 +0,0 @@
.gradle
.DS_Store
build
lib/bld/**
!lib/bld/bld-wrapper.jar
!lib/bld/bld-wrapper.properties
lib/compile/
lib/runtime/
lib/standalone/
lib/test/
# IDEA ignores
# User-specific
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Editor-based Rest Client
.idea/httpRequests

3
examples/bld/.idea/.gitignore generated vendored
View file

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

View file

@ -1 +0,0 @@
cryptoprice-examples-bld

View file

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/build/main" />
<output-test url="file://$MODULE_DIR$/build/test" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" relativeOutputPath="resources" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/kotln" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library" scope="RUNTIME">
<library>
<CLASSES>
<root url="file://$MODULE_DIR$/src/main/resources/templates" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" name="compile" level="project" />
<orderEntry type="library" scope="RUNTIME" name="runtime" level="project" />
<orderEntry type="library" scope="TEST" name="test" level="project" />
</component>
</module>

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/build/bld" />
<output-test url="file://$MODULE_DIR$/build/bld" />
<exclude-output />
<content url="file://$MODULE_DIR$/src/bld">
<sourceFolder url="file://$MODULE_DIR$/src/bld/java" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="bld" level="project" />
</component>
</module>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="BldConfiguration">
<events />
</component>
</project>

View file

@ -1,8 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="JavadocDeclaration" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ADDITIONAL_TAGS" value="created" />
</inspection_tool>
</profile>
</component>

View file

@ -1,17 +0,0 @@
<component name="libraryTable">
<library name="bld">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/bld" />
<root url="jar://$USER_HOME$/.bld/dist/bld-2.2.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.bld/dist/bld-2.2.1-sources.jar!/" />
</SOURCES>
<excluded>
<root url="jar://$PROJECT_DIR$/lib/bld/bld-wrapper.jar!/" />
</excluded>
<jarDirectory url="file://$PROJECT_DIR$/lib/bld" recursive="false" />
<jarDirectory url="file://$PROJECT_DIR$/lib/bld" recursive="false" type="SOURCES" />
</library>
</component>

View file

@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="compile">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/compile" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/compile" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="true" />
<jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="true" type="SOURCES" />
</library>
</component>

View file

@ -1,14 +0,0 @@
<component name="libraryTable">
<library name="runtime">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/runtime" />
<root url="file://$PROJECT_DIR$/src/main/resources" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/runtime" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="true" />
<jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="true" type="SOURCES" />
</library>
</component>

View file

@ -1,14 +0,0 @@
<component name="libraryTable">
<library name="test">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib/test" />
<root url="file://$PROJECT_DIR$/src/test/resources" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/lib/test" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="true" />
<jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="true" type="SOURCES" />
</library>
</component>

View file

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<pattern value="com.example.CryptoPriceExampleBuild" method="runJava" />
</component>
<component name="PDMPlugin">
<option name="customRuleSets">
<list>
<option value="K:\java\semver\config\pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-pitest/config/pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-jacoco-report/config/pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-checkstyle/config/pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-exec/config/pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-testng/config/pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-generated-version/config/pmd.xml" />
</list>
</option>
<option name="skipTestSources" value="false" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build" />
</component>
</project>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/app.iml" filepath="$PROJECT_DIR$/.idea/app.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/bld.iml" filepath="$PROJECT_DIR$/.idea/bld.iml" />
</modules>
</component>
</project>

View file

@ -1,9 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Tests" type="Application" factoryName="Application" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="com.example.ExampleTest" />
<module name="app" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

View file

@ -1,11 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Run Tests",
"request": "launch",
"mainClass": "com.example.ExampleTest"
}
]
}

View file

@ -1,15 +0,0 @@
{
"java.project.sourcePaths": [
"src/main/java",
"src/main/resources",
"src/test/java",
"src/test/resources",
"src/bld/java",
"src/bld/resources"
],
"java.configuration.updateBuildConfiguration": "automatic",
"java.project.referencedLibraries": [
"${HOME}/.bld/dist/bld-2.2.1.jar",
"lib/**/*.jar"
]
}

View file

@ -1,22 +0,0 @@
## Kotlin Example
To compile & run the Kotlin example:
```console
./bld compile
./bld run
./bld run --args="btc"
./bld run --args="eth eur"
```
## Java Example
To compile & run the Java example:
```console
./bld compile
./bld run-java
./bld run-java --args="btc"
./bld run-java --args="eth eur"
```

View file

@ -1,2 +0,0 @@
#!/usr/bin/env sh
java -jar "$(dirname "$0")/lib/bld/bld-wrapper.jar" "$0" --build com.example.CryptoPriceExampleBuild "$@"

View file

@ -1,4 +0,0 @@
@echo off
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
java -jar "%DIRNAME%/lib/bld/bld-wrapper.jar" "%0" --build com.example.CryptoPriceExampleBuild %*

Binary file not shown.

View file

@ -1,7 +0,0 @@
bld.downloadExtensionJavadoc=false
bld.downloadExtensionSources=true
bld.downloadLocation=
bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0-SNAPSHOT
bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES
bld.sourceDirectories=
bld.version=2.2.1

View file

@ -1,53 +0,0 @@
package com.example;
import rife.bld.BuildCommand;
import rife.bld.extension.CompileKotlinOperation;
import rife.bld.operations.RunOperation;
import rife.bld.BaseProject;
import java.util.List;
import static rife.bld.dependencies.Repository.*;
import static rife.bld.dependencies.Scope.compile;
public class CryptoPriceExampleBuild extends BaseProject {
public CryptoPriceExampleBuild() {
pkg = "com.example";
name = "Example";
version = version(0, 1, 0);
mainClass = "com.example.CryptoPriceExampleKt";
javaRelease = 11;
downloadSources = true;
autoDownloadPurge = true;
repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, SONATYPE_SNAPSHOTS_LEGACY);
scope(compile)
.include(dependency("net.thauvin.erik", "cryptoprice", version(1, 0, 3, "SNAPSHOT")))
.include(dependency("org.json", "json", "20250107"));
}
public static void main(String[] args) {
new CryptoPriceExampleBuild().start(args);
}
@Override
public void compile() throws Exception {
new CompileKotlinOperation()
.fromProject(this)
.execute();
// Also compile the Java source code
super.compile();
}
@BuildCommand(value = "run-java", summary = "Runs the Java example")
public void runJava() throws Exception {
new RunOperation()
.fromProject(this)
.mainClass("com.example.CryptoPriceSample")
.execute();
}
}

View file

@ -1,58 +0,0 @@
package com.example;
import net.thauvin.erik.crypto.CryptoException;
import net.thauvin.erik.crypto.CryptoPrice;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Collections;
import java.util.Currency;
import java.util.List;
import java.util.Locale;
public class CryptoPriceSample {
public static void main(final String[] args) {
try {
if (args.length > 0) {
final String currency;
if (args.length == 2) {
currency = args[1];
} else {
currency = Currency.getInstance(Locale.getDefault()).getCurrencyCode();
}
final var price = CryptoPrice.spotPrice(args[0], currency);
System.out.println("The current " + price.getBase() + " price is " + price.toCurrency());
} else {
// Get current Bitcoin spot price.
final var price = CryptoPrice.spotPrice("BTC");
System.out.println("The current Bitcoin price is " + price.toCurrency());
System.out.println();
// Get current Ethereum buy price in Pound sterling.
final var gbpPrice = CryptoPrice.buyPrice("ETH", "GBP");
System.out.println("The current Ethereum buy price is " + gbpPrice.toCurrency());
// Get current Litecoin sell price in Euros.
final var euroPrice = CryptoPrice.sellPrice("LTC", "EUR");
System.out.println("The current Litecoin sell price is " + euroPrice.toCurrency());
System.out.println();
// Get exchange rate using API.
// See: https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-exchange-rates
final var response = CryptoPrice.apiCall(List.of("exchange-rates"),
Collections.singletonMap("currency", "USD"));
final var rates = new JSONObject(response).getJSONObject("data").getJSONObject("rates");
System.out.println("The USD-EUR exchange rate is: " + rates.getString("EUR"));
}
} catch (CryptoException e) {
System.err.println("HTTP Status Code: " + e.getStatusCode());
System.err.println(e.getMessage() + " (" + e.getId() + ')');
} catch (IllegalArgumentException e) {
System.err.println("Could not display the specified currency: " + args[1]);
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
}

View file

@ -2,10 +2,15 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
id("application") id("application")
id("com.github.ben-manes.versions") version "0.51.0" id("com.github.ben-manes.versions") version "0.42.0"
kotlin("jvm") version "2.1.20" kotlin("jvm") version "1.7.10"
} }
// ./gradlew run
// ./gradlew runJava
// ./gradlew run --args="btc"
// ./gradlew runJava --args="eth eur"
defaultTasks(ApplicationPlugin.TASK_RUN_NAME) defaultTasks(ApplicationPlugin.TASK_RUN_NAME)
repositories { repositories {
@ -15,8 +20,8 @@ repositories {
} }
dependencies { dependencies {
implementation("net.thauvin.erik:cryptoprice:1.0.3-SNAPSHOT") implementation("net.thauvin.erik:cryptoprice:1.0.0")
implementation("org.json:json:20240303") implementation("org.json:json:20220320")
} }
java { java {
@ -28,11 +33,11 @@ application {
mainClass.set("com.example.CryptoPriceExampleKt") mainClass.set("com.example.CryptoPriceExampleKt")
} }
kotlin { tasks {
compilerOptions.jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11) withType<KotlinCompile>().configureEach {
kotlinOptions.jvmTarget = java.targetCompatibility.toString()
} }
tasks {
register<JavaExec>("runJava") { register<JavaExec>("runJava") {
group = "application" group = "application"
mainClass.set("com.example.CryptoPriceSample") mainClass.set("com.example.CryptoPriceSample")

3
examples/gradle/.idea/.gitignore generated vendored
View file

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

View file

@ -1 +0,0 @@
cryptoprice-examples-gradle

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View file

@ -1,8 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="JavadocDeclaration" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ADDITIONAL_TAGS" value="created" />
</inspection_tool>
</profile>
</component>

View file

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenLocal" />
<option name="name" value="MavenLocal" />
<option name="url" value="file:$MAVEN_REPOSITORY$/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" />
</remote-repository>
</component>
</project>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.21" />
</component>
</project>

View file

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="PDMPlugin">
<option name="customRuleSets">
<list>
<option value="K:\java\semver\config\pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-pitest/config/pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-jacoco-report/config/pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-checkstyle/config/pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-exec/config/pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-testng/config/pmd.xml" />
<option value="$PROJECT_DIR$/../../../../java/bld-generated-version/config/pmd.xml" />
</list>
</option>
<option name="skipTestSources" value="false" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="20" project-jdk-type="JavaSDK" />
</project>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

View file

@ -1,18 +0,0 @@
## Kotlin Example
To run the Kotlin example:
```console
./gradlew run
./gradlew run --args="btc"
./gradlew run --args="eth eur"
```
## Java Example
To run the Java example:
```console
./gradlew runJava
./gradlew runJava --args="btc"
./gradlew runJava --args="eth eur"
```

Binary file not shown.

View file

@ -1,49 +0,0 @@
package com.example
import net.thauvin.erik.crypto.CryptoException
import net.thauvin.erik.crypto.CryptoPrice.Companion.apiCall
import net.thauvin.erik.crypto.CryptoPrice.Companion.buyPrice
import net.thauvin.erik.crypto.CryptoPrice.Companion.sellPrice
import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice
import org.json.JSONObject
import java.io.IOException
import java.util.*
fun main(args: Array<String>) {
try {
if (args.isNotEmpty()) {
val currency = if (args.size == 2) args[1] else Currency.getInstance(Locale.getDefault()).currencyCode
val price = spotPrice(args[0], currency)
println("The current ${price.base} price is ${price.toCurrency()}")
} else {
// Get current Bitcoin spot price.
val price = spotPrice("BTC")
println("The current Bitcoin price is ${price.toCurrency()}")
println()
// Get current Ethereum sell price in Pound sterling.
val gbpPrice = sellPrice("ETH", "GBP")
println("The current Ethereum sell price is ${gbpPrice.toCurrency()}")
// Get current Litecoin buy price in Euro.
val euroPrice = buyPrice("LTC", "EUR")
println("The current Litecoin buy price is ${euroPrice.toCurrency()}")
println()
// Get exchange rate using API.
// See: https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-exchange-rates
val response = apiCall(listOf("exchange-rates"), mapOf("currency" to "usd"))
val rates = JSONObject(response).getJSONObject("data").getJSONObject("rates")
println("The USD-EUR exchange rate is: ${rates.getString("EUR")}")
}
} catch (e: CryptoException) {
System.err.println("HTTP Status Code: ${e.statusCode}")
System.err.println("${e.message} (${e.id})")
} catch (ignore: IllegalArgumentException) {
System.err.println("Could not display the specified currency: ${args[1]}")
} catch (e: IOException) {
System.err.println(e.message)
}
}

Binary file not shown.

View file

@ -1,7 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View file

@ -15,8 +15,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@ -57,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -82,11 +80,13 @@ do
esac esac
done done
# This is normally unused APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH="\\\"\\\"" CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@ -133,29 +133,22 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
if ! command -v java >/dev/null 2>&1 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@ -200,28 +193,18 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Collect all arguments for the java command;
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# Collect all arguments for the java command: # * put everything else in single quotes, so that it's not re-expanded.
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args. # Use "xargs" to parse quoted args.
# #
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. # With -n1 it outputs one arg per line, with the quotes and backslashes removed.

View file

@ -13,8 +13,6 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@ -28,7 +26,6 @@ if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@ -43,13 +40,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if "%ERRORLEVEL%" == "0" goto execute
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail
@ -59,34 +56,32 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH= set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd if "%ERRORLEVEL%"=="0" goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL% if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
if %EXIT_CODE% equ 0 set EXIT_CODE=1 exit /b 1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

View file

@ -48,7 +48,7 @@ public class CryptoPriceSample {
} }
} catch (CryptoException e) { } catch (CryptoException e) {
System.err.println("HTTP Status Code: " + e.getStatusCode()); System.err.println("HTTP Status Code: " + e.getStatusCode());
System.err.println(e.getMessage() + " (" + e.getId() + ')'); System.err.println(e.getMessage());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
System.err.println("Could not display the specified currency: " + args[1]); System.err.println("Could not display the specified currency: " + args[1]);
} catch (IOException e) { } catch (IOException e) {

View file

@ -40,8 +40,8 @@ fun main(args: Array<String>) {
} }
} catch (e: CryptoException) { } catch (e: CryptoException) {
System.err.println("HTTP Status Code: ${e.statusCode}") System.err.println("HTTP Status Code: ${e.statusCode}")
System.err.println("${e.message} (${e.id})") System.err.println(e.message)
} catch (ignore: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
System.err.println("Could not display the specified currency: ${args[1]}") System.err.println("Could not display the specified currency: ${args[1]}")
} catch (e: IOException) { } catch (e: IOException) {
System.err.println(e.message) System.err.println(e.message)

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
gradlew vendored Executable file
View file

@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

89
gradlew.bat vendored Normal file
View file

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

Binary file not shown.

View file

@ -1,10 +0,0 @@
bld.downloadExtensionJavadoc=false
bld.downloadExtensionSources=true
bld.downloadLocation=
bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.10-SNAPSHOT
bld.extension-dokka=com.uwyn.rife2:bld-dokka:1.0.4-SNAPSHOT
bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.10
bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0-SNAPSHOT
bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES
bld.sourceDirectories=
bld.version=2.2.1

69
pom.xml
View file

@ -1,12 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.thauvin.erik</groupId> <groupId>net.thauvin.erik</groupId>
<artifactId>cryptoprice</artifactId> <artifactId>cryptoprice</artifactId>
<version>1.0.3-SNAPSHOT</version> <version>1.0.0</version>
<name>cryptoprice</name> <name>cryptoprice</name>
<description>Retrieve cryptocurrencies prices</description> <description>Retrieve cryptocurrencies prices.</description>
<url>https://github.com/ethauvin/cryptoprice</url> <url>https://github.com/ethauvin/cryptoprice</url>
<licenses> <licenses>
<license> <license>
@ -14,26 +18,6 @@
<url>https://opensource.org/licenses/BSD-3-Clause</url> <url>https://opensource.org/licenses/BSD-3-Clause</url>
</license> </license>
</licenses> </licenses>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>2.1.20</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20250107</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<developers> <developers>
<developer> <developer>
<id>ethauvin</id> <id>ethauvin</id>
@ -43,8 +27,43 @@
</developer> </developer>
</developers> </developers>
<scm> <scm>
<connection>scm:git:https://github.com/ethauvin/cryptoprice.git</connection> <connection>scm:git:git://github.com/ethauvin/cryptoprice.git</connection>
<developerConnection>scm:git:git@github.com:ethauvin/cryptoprice.git</developerConnection> <developerConnection>scm:git:git@github.com:ethauvin/cryptoprice.git</developerConnection>
<url>https://github.com/ethauvin/cryptoprice</url> <url>https://github.com/ethauvin/cryptoprice</url>
</scm> </scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/ethauvin/cryptoprice/issues</url>
</issueManagement>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-bom</artifactId>
<version>1.7.10</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.7.10</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.10.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220320</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project> </project>

18
settings.gradle.kts Normal file
View file

@ -0,0 +1,18 @@
plugins {
id("com.gradle.enterprise").version("3.6.3")
}
gradleEnterprise {
buildScan {
link("GitHub", "https://github.com/ethauvin/cryptoprice/tree/master")
if (!System.getenv("CI").isNullOrEmpty()) {
isUploadInBackground = false
publishOnFailure()
tag("CI")
}
termsOfServiceUrl = "https://gradle.com/terms-of-service"
termsOfServiceAgree = "yes"
}
}
rootProject.name = "cryptoprice"

View file

@ -1,7 +0,0 @@
sonar.organization=ethauvin-github
sonar.projectKey=ethauvin_cryptoprice
sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml
sonar.sources=src/main/kotlin/
sonar.tests=src/test/kotlin/
sonar.java.binaries=build/main,build/test
sonar.java.libraries=lib/compile/*.jar

View file

@ -1,196 +0,0 @@
/*
* CryptoPriceBuild.java
*
* Copyright 2021-2025 Erik C. Thauvin (erik@thauvin.net)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.crypto;
import rife.bld.BuildCommand;
import rife.bld.Project;
import rife.bld.extension.CompileKotlinOperation;
import rife.bld.extension.DetektOperation;
import rife.bld.extension.DokkaOperation;
import rife.bld.extension.JacocoReportOperation;
import rife.bld.extension.dokka.LoggingLevel;
import rife.bld.extension.dokka.OutputFormat;
import rife.bld.extension.kotlin.CompileOptions;
import rife.bld.operations.exceptions.ExitStatusException;
import rife.bld.publish.PomBuilder;
import rife.bld.publish.PublishDeveloper;
import rife.bld.publish.PublishLicense;
import rife.bld.publish.PublishScm;
import rife.tools.exceptions.FileUtilsErrorException;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import static rife.bld.dependencies.Repository.*;
import static rife.bld.dependencies.Scope.compile;
import static rife.bld.dependencies.Scope.test;
public class CryptoPriceBuild extends Project {
final File srcMainKotlin = new File(srcMainDirectory(), "kotlin");
public CryptoPriceBuild() {
pkg = "net.thauvin.erik.crypto";
name = "cryptoprice";
version = version(1, 0, 3, "SNAPSHOT");
mainClass = "net.thauvin.erik.crypto.CryptoPrice";
javaRelease = 11;
downloadSources = true;
autoDownloadPurge = true;
repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL);
final var kotlin = version(2, 1, 20);
scope(compile)
.include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin))
.include(dependency("org.json", "json", "20250107"))
.include(dependency("com.squareup.okhttp3", "okhttp", version(4, 12, 0)));
scope(test)
.include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1)))
.include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin))
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2)))
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2)))
.include(dependency("org.junit.platform", "junit-platform-launcher", version(1, 12, 2)));
publishOperation()
.repository(version.isSnapshot() ? repository(SONATYPE_SNAPSHOTS_LEGACY.location())
.withCredentials(property("sonatype.user"), property("sonatype.password"))
: repository(SONATYPE_RELEASES_LEGACY.location())
.withCredentials(property("sonatype.user"), property("sonatype.password")))
.repository(repository("github"))
.info()
.groupId("net.thauvin.erik")
.artifactId(name)
.description("Retrieve cryptocurrencies prices")
.url("https://github.com/ethauvin/" + name)
.developer(new PublishDeveloper()
.id("ethauvin")
.name("Erik C. Thauvin")
.email("erik@thauvin.net")
.url("https://erik.thauvin.net/")
)
.license(new PublishLicense()
.name("BSD 3-Clause")
.url("https://opensource.org/licenses/BSD-3-Clause")
)
.scm(new PublishScm()
.connection("scm:git:https://github.com/ethauvin/" + name + ".git")
.developerConnection("scm:git:git@github.com:ethauvin/" + name + ".git")
.url("https://github.com/ethauvin/" + name)
)
.signKey(property("sign.key"))
.signPassphrase(property("sign.passphrase"));
jarSourcesOperation().sourceDirectories(srcMainKotlin);
}
public static void main(final String[] args) {
// Enable detailed logging for the extensions
final var level = Level.ALL;
final var logger = Logger.getLogger("rife.bld.extension");
final var consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(level);
logger.addHandler(consoleHandler);
logger.setLevel(level);
logger.setUseParentHandlers(false);
new CryptoPriceBuild().start(args);
}
@BuildCommand(summary = "Compiles the Kotlin project")
@Override
public void compile() throws Exception {
new CompileKotlinOperation()
.fromProject(this)
.compileOptions(new CompileOptions().verbose(true))
.execute();
}
@BuildCommand(summary = "Checks source with Detekt")
public void detekt() throws ExitStatusException, IOException, InterruptedException {
new DetektOperation()
.fromProject(this)
.execute();
}
@BuildCommand(value = "detekt-baseline", summary = "Creates the Detekt baseline")
public void detektBaseline() throws ExitStatusException, IOException, InterruptedException {
new DetektOperation()
.fromProject(this)
.baseline("detekt-baseline.xml")
.createBaseline(true)
.execute();
}
@BuildCommand(summary = "Generates JaCoCo Reports")
public void jacoco() throws Exception {
new JacocoReportOperation()
.fromProject(this)
.sourceFiles(srcMainKotlin)
.execute();
}
@Override
public void javadoc() throws ExitStatusException, IOException, InterruptedException {
new DokkaOperation()
.fromProject(this)
.loggingLevel(LoggingLevel.INFO)
.moduleName("CryptoPrice")
.moduleVersion(version.toString())
.outputDir(new File(buildDirectory(), "javadoc"))
.outputFormat(OutputFormat.JAVADOC)
.execute();
}
@Override
public void publish() throws Exception {
super.publish();
pomRoot();
}
@Override
public void publishLocal() throws Exception {
super.publishLocal();
pomRoot();
}
@BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory")
public void pomRoot() throws FileUtilsErrorException {
PomBuilder.generateInto(publishOperation().fromProject(this).info(), dependencies(),
new File(workDirectory, "pom.xml"));
}
}

View file

@ -1,7 +1,8 @@
/* /*
* CryptoException.kt * CryptoException.kt
* *
* Copyright 2021-2025 Erik C. Thauvin (erik@thauvin.net) * Copyright (c) 2021-2022, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
@ -36,7 +37,6 @@ package net.thauvin.erik.crypto
*/ */
class CryptoException @JvmOverloads constructor( class CryptoException @JvmOverloads constructor(
var statusCode: Int = NO_STATUS, var statusCode: Int = NO_STATUS,
var id: String,
message: String, message: String,
cause: Throwable? = null cause: Throwable? = null
) : Exception(message, cause) { ) : Exception(message, cause) {

View file

@ -1,7 +1,8 @@
/* /*
* CryptoPrice.kt * CryptoPrice.kt
* *
* Copyright 2021-2025 Erik C. Thauvin (erik@thauvin.net) * Copyright (c) 2021-2022, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
@ -42,8 +43,6 @@ import java.math.BigDecimal
import java.text.NumberFormat import java.text.NumberFormat
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
import java.util.logging.Level
import java.util.logging.Logger
/** /**
* Retrieves and holds a cryptocurrency price. * Retrieves and holds a cryptocurrency price.
@ -59,9 +58,6 @@ open class CryptoPrice(val base: String, val currency: String, val amount: BigDe
// Coinbase API URL // Coinbase API URL
private const val COINBASE_API_URL = "https://api.coinbase.com/v2/" private const val COINBASE_API_URL = "https://api.coinbase.com/v2/"
/** The logger instance. **/
val logger: Logger by lazy { Logger.getLogger(CryptoPrice::class.java.simpleName) }
/** /**
* Converts JSON data object to [CryptoPrice]. * Converts JSON data object to [CryptoPrice].
* *
@ -77,9 +73,9 @@ open class CryptoPrice(val base: String, val currency: String, val amount: BigDe
return CryptoPrice(getString("base"), getString("currency"), getString("amount").toBigDecimal()) return CryptoPrice(getString("base"), getString("currency"), getString("amount").toBigDecimal())
} }
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {
throw CryptoException(id = "convert_error", message = "Could not convert amount to number.", cause = e) throw CryptoException(message = "Could not convert amount to number.", cause = e)
} catch (e: JSONException) { } catch (e: JSONException) {
throw CryptoException(id = "parse_error", message = "Could not parse price data.", cause = e) throw CryptoException(message = "Could not parse price data.", cause = e)
} }
} }
@ -104,38 +100,18 @@ open class CryptoPrice(val base: String, val currency: String, val amount: BigDe
}.build() }.build()
val request = Request.Builder().url(httpUrl).build() val request = Request.Builder().url(httpUrl).build()
client.newCall(request).execute().use { response -> val response = client.newCall(request).execute()
val body = response.body?.string() ?: throw CryptoException( val body = response.body?.string() ?: throw CryptoException(response.code, "Empty response.")
response.code,
id = "empty_response",
message = "Empty response."
)
if (logger.isLoggable(Level.FINE)) {
logger.fine(body)
}
try { try {
val json = JSONObject(body) val json = JSONObject(body)
if (response.isSuccessful) { if (response.isSuccessful) {
return body return body
} else { } else {
if (json.has("errors")) {
val data = json.getJSONArray("errors") val data = json.getJSONArray("errors")
throw CryptoException( throw CryptoException(response.code, data.getJSONObject(0).getString("message"))
response.code,
data.getJSONObject(0).getString("id"),
data.getJSONObject(0).getString("message")
)
} else {
throw CryptoException(
response.code,
json.getString("error"),
json.getString("message")
)
}
} }
} catch (e: JSONException) { } catch (e: JSONException) {
throw CryptoException(response.code, id = "parse_error", "Could not parse data.", e) throw CryptoException(response.code, "Could not parse data.", e)
}
} }
} }
@ -157,16 +133,12 @@ open class CryptoPrice(val base: String, val currency: String, val amount: BigDe
*/ */
@JvmStatic @JvmStatic
fun main(args: Array<String>) { fun main(args: Array<String>) {
if (args.isEmpty()) {
println("Please specify one or more ticker symbols as arguments.")
} else {
args.forEach { args.forEach {
with(spotPrice(it)) { with(spotPrice(it)) {
println("$base:\t" + "%10s".format(toCurrency())) println("$base:\t" + "%10s".format(toCurrency()))
} }
} }
} }
}
/** /**
* Retrieves the sell price. * Retrieves the sell price.

View file

@ -1,7 +1,8 @@
/* /*
* CryptoPriceTest.kt * CryptoPriceTest.kt
* *
* Copyright 2021-2025 Erik C. Thauvin (erik@thauvin.net) * Copyright (c) 2021-2022, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
@ -31,27 +32,19 @@
package net.thauvin.erik.crypto package net.thauvin.erik.crypto
import assertk.all
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isGreaterThan
import assertk.assertions.prop
import net.thauvin.erik.crypto.CryptoPrice.Companion.apiCall import net.thauvin.erik.crypto.CryptoPrice.Companion.apiCall
import net.thauvin.erik.crypto.CryptoPrice.Companion.buyPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.buyPrice
import net.thauvin.erik.crypto.CryptoPrice.Companion.sellPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.sellPrice
import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.spotPrice
import net.thauvin.erik.crypto.CryptoPrice.Companion.toPrice import net.thauvin.erik.crypto.CryptoPrice.Companion.toPrice
import org.json.JSONObject import org.json.JSONObject
import org.junit.jupiter.api.BeforeAll
import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
import java.util.logging.ConsoleHandler
import java.util.logging.Level
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
/** /**
* [CryptoPrice] Tests * [CryptoPrice] Tests
@ -65,11 +58,9 @@ class CryptoPriceTest {
fun testBitcoinPrice() { fun testBitcoinPrice() {
val prices = listOf(spotPrice("BTC"), buyPrice("BTC"), sellPrice("BTC")) val prices = listOf(spotPrice("BTC"), buyPrice("BTC"), sellPrice("BTC"))
for (price in prices) { for (price in prices) {
assertThat(price).all { assertEquals("BTC", price.base, "BTC")
prop(CryptoPrice::base).isEqualTo("BTC") assertEquals("USD", price.currency, "is USD")
prop(CryptoPrice::currency).isEqualTo("USD") assertTrue(price.amount.signum() > 0, "BTC > 0")
prop(CryptoPrice::amount).isGreaterThan(BigDecimal(0))
}
} }
} }
@ -78,11 +69,9 @@ class CryptoPriceTest {
fun testEtherPrice() { fun testEtherPrice() {
val prices = listOf(spotPrice("ETH", "EUR"), buyPrice("ETH", "EUR"), sellPrice("ETH", "EUR")) val prices = listOf(spotPrice("ETH", "EUR"), buyPrice("ETH", "EUR"), sellPrice("ETH", "EUR"))
for (price in prices) { for (price in prices) {
assertThat(price).all { assertEquals("ETH", price.base, "ETH")
prop(CryptoPrice::base).isEqualTo("ETH") assertEquals("EUR", price.currency, "is EUR")
prop(CryptoPrice::currency).isEqualTo("EUR") assertTrue(price.amount.signum() > 0, "ETH > 0")
prop(CryptoPrice::amount).isGreaterThan(BigDecimal(0))
}
} }
} }
@ -91,11 +80,9 @@ class CryptoPriceTest {
fun testLitecoinPrice() { fun testLitecoinPrice() {
val prices = listOf(spotPrice("LTC", "GBP"), buyPrice("LTC", "GBP"), sellPrice("LTC", "GBP")) val prices = listOf(spotPrice("LTC", "GBP"), buyPrice("LTC", "GBP"), sellPrice("LTC", "GBP"))
for (price in prices) { for (price in prices) {
assertThat(price).all { assertEquals("LTC", price.base, "LTC")
prop(CryptoPrice::base).isEqualTo("LTC") assertEquals("GBP", price.currency, "is GBP")
prop(CryptoPrice::currency).isEqualTo("GBP") assertTrue(price.amount.signum() > 0, "LTC > 0")
prop(CryptoPrice::amount).isGreaterThan(BigDecimal(0))
}
} }
} }
@ -103,32 +90,35 @@ class CryptoPriceTest {
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testBitcoinCashPrice() { fun testBitcoinCashPrice() {
val price = spotPrice("BCH", "GBP", LocalDate.now().minusDays(1)) val price = spotPrice("BCH", "GBP", LocalDate.now().minusDays(1))
assertThat(price, "spotPrice(BCH,GPB)").all { assertEquals("BCH", price.base, "BCH")
prop(CryptoPrice::base).isEqualTo("BCH") assertEquals("GBP", price.currency, "is GBP")
prop(CryptoPrice::currency).isEqualTo("GBP") assertTrue(price.amount.signum() > 0, "BCH > 0")
prop(CryptoPrice::amount).isGreaterThan(BigDecimal(0))
}
} }
@Test @Test
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testApiCall() { fun testApiCall() {
val price = apiCall(listOf("prices", "BTC-USD", "buy"), emptyMap()).toPrice() val price = apiCall(listOf("prices", "BTC-USD", "buy"), emptyMap()).toPrice()
assertThat(price, "apiCall(prices,BTC-USD,buy)").all { assertEquals("BTC", price.base, "buy BTC")
prop(CryptoPrice::base).isEqualTo("BTC") assertEquals("USD", price.currency, "buy BTC is USD")
prop(CryptoPrice::currency).isEqualTo("USD") assertTrue(price.amount.signum() > 0, "buy BTC > 0")
prop(CryptoPrice::amount).isGreaterThan(BigDecimal(0))
}
val response = apiCall(listOf("exchange-rates"), mapOf("currency" to "usd")) val response = apiCall(listOf("exchange-rates"), mapOf("currency" to "usd"))
val rates = JSONObject(response).getJSONObject("data").getJSONObject("rates") val rates = JSONObject(response).getJSONObject("data").getJSONObject("rates")
assertEquals("1.0", rates.getString("USD"), "apiCall(exchange-rates,USD)") assertEquals("1.0", rates.getString("USD"), "usd rate")
} }
@Test @Test
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testNotFound() { fun testPrices() {
assertFailsWith( assertFailsWith(
message = "buyPrice(BTC,BAR)", message = "FOO did not fail",
exceptionClass = CryptoException::class,
block = { spotPrice("FOO") }
)
assertFailsWith(
message = "BAR did not fail",
exceptionClass = CryptoException::class, exceptionClass = CryptoException::class,
block = { buyPrice("BTC", "BAR") } block = { buyPrice("BTC", "BAR") }
) )
@ -136,11 +126,7 @@ class CryptoPriceTest {
try { try {
sellPrice("FOOBAR") sellPrice("FOOBAR")
} catch (e: CryptoException) { } catch (e: CryptoException) {
assertThat(e, "sellPrice(FOOBAR)").all { assertNotEquals(400, e.statusCode, "FOOBAR status code is not 400")
prop(CryptoException::statusCode).isEqualTo(404)
prop(CryptoException::message).isEqualTo("not found")
prop(CryptoException::id).isEqualTo("not found")
}
} }
} }
@ -149,45 +135,33 @@ class CryptoPriceTest {
fun testToCurrency() { fun testToCurrency() {
val d = 12345.60.toBigDecimal() val d = 12345.60.toBigDecimal()
val usd = CryptoPrice("BTC", "USD", d) val usd = CryptoPrice("BTC", "USD", d)
assertEquals("$12,345.60", usd.toCurrency(), "CryptoPrice(BTC,USD)") assertEquals("$12,345.60", usd.toCurrency(), "USD format")
assertEquals(usd, usd, "CryptoPrice(BTC,USD) = CryptoPrice(BTC,USD)") assertEquals(usd, usd, "USD = USD")
assertNotEquals( assertNotEquals(usd, CryptoPrice("BTC", "USD", 12345.70.toBigDecimal()), ".60 !- .70")
usd,
CryptoPrice("BTC", "USD", 12345.70.toBigDecimal()),
"CryptoPrice(BTC,USD) = CryptoPrice(BTC,USD,BigDecimal)"
)
val eur = CryptoPrice("BTC", "EUR", d) val eur = CryptoPrice("BTC", "EUR", d)
assertEquals("€12,345.60", eur.toCurrency(), "CryptoPrice(BTC,EUR)") assertEquals("€12,345.60", eur.toCurrency(), "EUR format")
assertNotEquals( assertNotEquals(usd.hashCode(), eur.hashCode(), "hashCode()")
usd.hashCode(), assertNotEquals(usd, eur, "USD != EUR")
eur.hashCode(),
"CryptoPrice(BTC,USD).hashCode != CryptoPrice(BTC,EUR).hashCode"
)
assertNotEquals(usd, eur, "CryptoPrice(BTC,USD) != CryptoPrice(BTC,EUR)")
val gbp = CryptoPrice("ETH", "GBP", d) val gbp = CryptoPrice("ETH", "GBP", d)
assertEquals("£12,345.60", gbp.toCurrency(), "CryptoPrice(ETH,GBP)") assertEquals("£12,345.60", gbp.toCurrency(), "GBP format")
assertNotEquals(usd, gbp, "CryptoPrice(BTC,USD) != CryptoPrice(BTC,GBP)") assertNotEquals(usd, gbp, "BTC != ETH")
val aud = CryptoPrice("LTC", "AUD", d) val aud = CryptoPrice("LTC", "AUD", d)
assertEquals("A$12,345.60", aud.toCurrency(), "CryptoPrice(LTC,AUD)") assertEquals("A$12,345.60", aud.toCurrency(), "AUD format")
val dk = CryptoPrice("BCH", "DKK", d) val dk = CryptoPrice("BCH", "DKK", d)
assertEquals( assertEquals("12.345,60 kr.", dk.toCurrency(Locale("da", "DK")), "EUR-DKK format")
"12.345,60 kr.", dk.toCurrency(
Locale.Builder().setLanguage("da").setRegion("DK").build()
), "CryptoPrice(BCH,DKK)"
)
val jp = CryptoPrice("BTC", "JPY", d) val jp = CryptoPrice("BTC", "JPY", d)
assertEquals("¥12,345.60", jp.toCurrency(Locale.JAPAN), "CryptoPrice(BTC,JPY)") assertEquals("¥12,345.60", jp.toCurrency(Locale.JAPAN), "EUR-JPY format")
assertEquals("$12,345.6000", usd.toCurrency(minFractionDigits = 4), "toCurrency(minFractionDigits = 4)") assertEquals("$12,345.6000", usd.toCurrency(minFractionDigits = 4), "minFractionDigits = 4")
assertEquals("$12,345.6", usd.toCurrency(minFractionDigits = 0), "toCurrency(minFractionDigits = 0)") assertEquals("$12,345.6", usd.toCurrency(minFractionDigits = 0), "minFractionDigits = 0")
} }
@Test @Test
@ -201,35 +175,34 @@ class CryptoPriceTest {
} }
} }
@Test @Test
@Throws(CryptoException::class) @Throws(CryptoException::class)
fun testToPrice() { fun testToPrice() {
val d = "57515.60" val d = "57515.60"
val data = jsonData.format(d) val data = jsonData.format(d)
val price = data.toPrice() val price = data.toPrice()
assertThat(price, "toPrice()").all { assertEquals("BTC", price.base, "base is BTC")
prop(CryptoPrice::base).isEqualTo("BTC") assertEquals("USD", price.currency, "currency is USD")
prop(CryptoPrice::currency).isEqualTo("USD") assertEquals(d, price.amount.toString(), "amount is $d")
prop(CryptoPrice::amount).isEqualTo(BigDecimal(d))
}
assertEquals(price, price.toString().toPrice(""), "toPrice('')") assertEquals(price, price.toString().toPrice(""), "toPrice('')")
assertEquals(price, price.toJson("test").toPrice("test"), "toPrice(test)") assertEquals(price, price.toJson("test").toPrice("test"), "toPrice(test)")
assertFailsWith( assertFailsWith(
message = "a.toPrice()", message = "amount conversion did not fail",
exceptionClass = CryptoException::class, exceptionClass = CryptoException::class,
block = { data.replace("5", "a").toPrice() } block = { data.replace("5", "a").toPrice() }
) )
assertFailsWith( assertFailsWith(
message = "{}.toPrice()", message = "empty did not fail",
exceptionClass = CryptoException::class, exceptionClass = CryptoException::class,
block = { "{}".toPrice() } block = { "{}".toPrice() }
) )
assertFailsWith( assertFailsWith(
message = "foo.toPrice()", message = "no base did not fail",
exceptionClass = CryptoException::class, exceptionClass = CryptoException::class,
block = { data.replace("base", "foo").toPrice() } block = { data.replace("base", "foo").toPrice() }
) )
@ -242,16 +215,4 @@ class CryptoPriceTest {
assertEquals(json, price.toString(), "toString()") assertEquals(json, price.toString(), "toString()")
assertEquals(price.toString(), price.toJson(""), "toString() = toJson('')") assertEquals(price.toString(), price.toJson(""), "toString() = toJson('')")
} }
companion object {
@JvmStatic
@BeforeAll
fun beforeAll() {
with(CryptoPrice.logger) {
addHandler(ConsoleHandler().apply { level = Level.FINE })
level = Level.FINE
useParentHandlers = false
}
}
}
} }