GithubHelp home page GithubHelp logo

kryszak / gwatlin Goto Github PK

View Code? Open in Web Editor NEW
6.0 4.0 1.0 1.39 MB

Guild Wars 2 API client written in Kotlin.

Home Page: https://kryszak.github.io/gwatlin-docs/

License: MIT License

Kotlin 99.85% Shell 0.15%
guildwars2 kotlin

gwatlin's Introduction

Build Status Codacy Badge Codacy Badge License: MIT Maven Central

Gwatlin

Guild Wars 2 API client written in Kotlin

Client operates on API version v2.

Important

It seems that not all api features are documented on wiki page, so feel free to create issues for missing features. Issue should contain example json response for given endpoint.

Example usage

Code

// Client without API KEY
val wvwClient = GWWvwClient()
val wvwRank = wvwClient.getRanks(listOf(1))
println(wvwRank)
// WvwRank(id=1, minRank=1, title=Invader)

// Client with API KEY
val tokenInfoClient = GWTokenClient("API KEY here")
val tokenInfo = tokenInfoClient.getTokenInfo()
println(tokenInfo)
// Token(id=ABCDE02B-8888-FEBA-1234-DE98765C7DEF, name=My API Key, permissions=[account, characters, tradingpost, unlocks, build], type=null, expiresAt=null, issuedAt=null, urls=null)

Documentation

For full code documentation visit documentation page

JDK versions compatibility

JDK Gwatlin
21 1.9.2 and above
17 1.9.1 and below

gwatlin's People

Contributors

kryszak avatar dependabot[bot] avatar github-actions[bot] avatar fanonwue avatar northburns avatar sheldan avatar

Stargazers

Diego avatar Taketoday avatar  avatar  avatar  avatar Alexis THOMAS avatar

Watchers

James Cloos avatar  avatar  avatar  avatar

Forkers

northburns

gwatlin's Issues

"/characters" endpoint missing

There is an client for the properties of characters (such as inventory), but the overall "/characters" which returns the list of character names is missing.

Implement ItemDetails correctly

While working on #28, I've noticed that the /items endpoint isn't implemented correctly. Every item can have an optional details object, and none of these optional objects have been created yet. It's currently mapped to Any, which clearly doesn't do anything useful.

I'll need to disable this non-functional property on the Item model to do the serialization transition in #28, and then this needs to be implemented correctly.

Improve API language selection

Language parameter should be an enum with values provided in documentation:
docs

Also, the default behaviour for absence of language parameter is to return results in english, so we can drop default values and send language header only when user specify it explicitly.

Migrate tests to Kotest framework

As there are plans to migrate Gradle DSL to Kotlin version, we could also get rid of tests written in Groovy and migrate them to Kotlin.

Preferred framework: kotest

Traited Fact vs Fact

I have some thoughts on Fact vs Traited Fact. I may have some trouble here because I'm so new at the game, but here goes. For reference the GW2 Wiki's API page on Skills.

My understanding: There are different kinds of facts, and current implementation of a sealed interface + concrete data classes makes a whole lot of sense. Now, some skills may also have a traited_facts array of "traited facts" which are just like facts but contain two additional fields (the required trait, and whether its a new fact or overrides existing fact).

All objects returned within a traited_facts array contain the same properties as a facts object, but they additionally have the following properties:

So the current implementation of TraitedFact seems to need work. I'm wondering what options there are in implementing this. I'd say these would be reasonable goals:

Option 1: Trying to achieve all 5 goals is quite tricky ๐Ÿค” Artificial "inbetween interfaces" are messy and would require some explaining ๐Ÿ˜… I tried to create some small poc before work today, but I think a custom serializer would need to be created. JsonContentPolymorphicSerializer is quite nice to work with, but the Fact type checking would have to be implemented there, instead of using the @SerialName - and I also noticed that the type attribute serialization would have to be implemented as well. I've done some work with Kotlin Serialization, but it's quite a long time ago, so I'd need to brush up on the custom serializer side of things ๐Ÿ˜Š

Option 2: would be to move the TraitedFact fields (as nullable) to Fact. This would do away with the TraitedFact type, and whether Fact "is traited or not" would be checking if the requires_trait is null. This would hide the TraitedFact from types, but would be super simple to implement, at the cost of a bit of added complexity to application.

Option 3: How about a custom serializer? There's a bunch of ways this could be done. Maybe a JsonTransformingSerializer would be simple enough, if we can make the assumption we'll always handle data as JSON. I created a PR with a proposal #113 .

TL;DR Just read the quoted part and Option 3, sorry for rambling ๐Ÿ˜…

How to get ItemDetails?

Hello.

Using this to build a tool for my guild, but having issues getting details about an item.

F.ex if the item I've fetched is an armor piece, how do I get the details for it, like which kind of armor piece it is (head, shoulders, coat etc), weight class, etc etc,

If those details aren't available for all the item types I'd not call the Items api to be done.
Details are found here: https://wiki.guildwars2.com/wiki/API:2/items

Add client for schema version values from API endpoint

Currently we use hardcoded schema version value 2022-03-23T19:00:00.000Z. API provides endpoint for fetching available schema version values link

We should allow to fetch available schema versions and use selected one with api clients.

Split CI for master branch push and PR

With dependabot PRs for version updates there was a problem with how dependabot updates work.

Dependabot forks repository, makes changes and creates pull request to original repo. Because of that, PR build was failing on "Upload to Codacy" step due to CODACY_TOKEN secret not existing on forked repo. As quick fix #34 was merged into master branch.

For permanent solution, build.yml file should be split into two: build.yml and pull_request.yml, where build.yml file will contain steps for building push to master branch as it was before the fix. pull_request.yml should define the same steps for pull requests but with Codacy step allowed to fail.

Add cache mechanism for requests

Library would benefit from optional response caching.
Cache should be available per client.
Cache availability should be turned on by passing parameter to client constructor.

Thoughts on the serializable classes, polymorphism and serialization

Hi! In my personal project where I use gwatlin I have a need to serialize some types to file (mainly for caching reasons, but there are others). I noticed that polymorphic classes Item, Fact (used in Skill), and GuildLog don't work as I expected. When I was serializing them like this: Json.encodeToString(item) I get the following error:

java.lang.IllegalStateException: Sealed class 'Armor' cannot be serialized as base class 'io.github.kryszak.gwatlin.api.items.model.item.Item' because it has property name that conflicts with JSON class discriminator 'type'. You can either change class discriminator in JsonConfiguration, rename property with @SerialName annotation or fall back to array polymorphism

In short, the type property (with @SerialName annotation) is used by Kotlin Serialization's polymorphism feature. This aligns nicely with GW2 API's type property. Having it as a property is not a problem when deserializing/decoding, it'll just get set to the correct value. But when serializing/encoding, it raises the above Exception, understandably.

It is of course worth the discussion whether the type property is needed, as it's simple to check for type in code e.g. item is ArmorItem especially with sealed classes/interfaces. But to maintain "binary compatibility", I'm proposing following:

  • the aforementioned classes define the type property without a backing field and a "constant value" that corresponds to serial name
  • without a backing field, Kotlin Serialization ignores it by default, thus serialization/encoding will work as expected
  • to ease maintenance I tried out a property delegate that uses Java's reflection to read the @SerialName value (introducing Kotlin Reflection as a dependency would be overkill). Of course if Java reflection wants to be kept out, it's not a huge thing to maintain the hard-coded values by hand in code.

What are your thoughts on this? I pushed a working proof of concept in branch polymorphic-json-serialization, see PR #111 (which I named "WIP" and backlinked to this issue for discussion)

Use proper domain for packaging

We should pick and change package domain to not use com, since it's meant to be used for commercial projects.

My other projects use net domain, other valid could be org or io.

I think most common domain for OSS projects is org, like all Apache libraries.

Character's guild can be null

Character's guild property is optional, API docs @ gw2 wiki. The Character class above doesn't allow null values to guild property, resulting in the following error output.

[main] ERROR io.github.kryszak.gwatlin.http.BaseHttpClient - Request failed! Unexpected JSON token at offset 146: Unexpected 'null' value instead of string literal at path: $.guild
JSON input: .....  "level": 80,
  "guild": null,
  "age": 476620,
  "last_mod.....
Exception in thread "main" The input has already been written to an output stream and can not be consumed again.
	com.github.kittinunf.fuel.core.FuelError$Companion.wrap(FuelError.kt:86)
	com.github.kittinunf.fuel.core.FuelError$Companion.wrap$default(FuelError.kt:83)
Caused by: java.lang.IllegalStateException: The input has already been written to an output stream and can not be consumed again.
	com.github.kittinunf.fuel.core.requests.DefaultBody$Companion$CONSUMED_STREAM$1.invoke(DefaultBody.kt:125)
	com.github.kittinunf.fuel.core.requests.DefaultBody$Companion$CONSUMED_STREAM$1.invoke(DefaultBody.kt:119)
	com.github.kittinunf.fuel.core.requests.DefaultBody.writeTo(DefaultBody.kt:84)
	com.github.kittinunf.fuel.core.requests.DefaultBody.toByteArray(DefaultBody.kt:50)
	com.github.kittinunf.fuel.core.Response.getData(Response.kt:32)
	io.github.kryszak.gwatlin.http.BaseHttpClient.processResult(BaseHttpClient.kt:67)
	io.github.kryszak.gwatlin.http.BaseHttpClient.access$processResult(BaseHttpClient.kt:16)
	io.github.kryszak.gwatlin.clients.characters.CharactersClient.getCharacter(CharactersClient.kt:147)
	io.github.kryszak.gwatlin.api.characters.GWCharactersClient.getCharacter(GWCharactersClient.kt:24)
	northburns.gw2.Application1$main$1.invokeSuspend(Application1.kt:25)
	kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280)
	kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
	kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	northburns.gw2.Application1.main(Application1.kt:12)

	at com.github.kittinunf.fuel.core.FuelError$Companion.wrap(FuelError.kt:86)
	at com.github.kittinunf.fuel.core.FuelError$Companion.wrap$default(FuelError.kt:83)
Caused by: java.lang.IllegalStateException: The input has already been written to an output stream and can not be consumed again.
	at com.github.kittinunf.fuel.core.requests.DefaultBody$Companion$CONSUMED_STREAM$1.invoke(DefaultBody.kt:125)
	at com.github.kittinunf.fuel.core.requests.DefaultBody$Companion$CONSUMED_STREAM$1.invoke(DefaultBody.kt:119)
	at com.github.kittinunf.fuel.core.requests.DefaultBody.writeTo(DefaultBody.kt:84)
	at com.github.kittinunf.fuel.core.requests.DefaultBody.toByteArray(DefaultBody.kt:50)
	at com.github.kittinunf.fuel.core.Response.getData(Response.kt:32)
	at io.github.kryszak.gwatlin.http.BaseHttpClient.processResult(BaseHttpClient.kt:67)
	at io.github.kryszak.gwatlin.http.BaseHttpClient.access$processResult(BaseHttpClient.kt:16)
	at io.github.kryszak.gwatlin.clients.characters.CharactersClient.getCharacter(CharactersClient.kt:147)
	at io.github.kryszak.gwatlin.api.characters.GWCharactersClient.getCharacter(GWCharactersClient.kt:24)
	at northburns.gw2.Application1$main$1.invokeSuspend(Application1.kt:25)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at northburns.gw2.Application1.main(Application1.kt:12)

Sidenote: I know, I know, I should join a guild. I haven't played for very long yet ๐Ÿ™ˆ

Add e2e tests

A set of e2e tests calling real endpoints would be beneficial for checking for eventual API changes.
Ideally it should be separate set of tests to be run on schedule and not with every build.
Schedule run for given cron expression.
Probably best to be done on separate repository that fetches gwatlin artifact from github packages.

Update Groovy and Spock version to latest

Update:

  • Spock to version 2.3-groovy-4.0
  • Groovy to version 4.0.11 (note: artifact name changed from org.codehaus.groovy:groovy-all to org.apache.groovy:groovy-all)

Both of those require updating project to Java version 17:

kotlin {
    jvmToolchain {
        languageVersion.set(JavaLanguageVersion.of(17))
    }
}

Outdated Kotlin version

The project is using Kotlin 1.3.50 right now, which is several years old by now. I propose updating to a newer version (1.8.10 at this time) to benefit from the several improvmenets made to both the language and kotlinc.

This shouldn't impact any user of this library as we can still target Java 8 bytecode if needed. I could open a PR for this if there isn't any objection to this.

Might as well switch to the Kotlin DSL that Gradle offers, which is something I personally prefer. It's a Kotlin project after all, right?

Switch from Gson to kotlinx.serialization

We are currently using Gson for our deserialization needs. According to the Github project it's now in maintenance mode and can be considered deprecated. Quote: 'Gson is currently in maintenance mode'.

I propse switching to the official serialization library kotlinx.serialization as a replacement.

Another alternative would be Moshi, but I'd prefer going with the more "native" solution.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.