initial commit of functioning opening tracking

This commit is contained in:
Sheldan
2024-01-06 23:29:25 +01:00
parent b72c68dfe5
commit 45e7982330
176 changed files with 37635 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>
<artifactId>database</artifactId>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,2 @@
FROM liquibase/liquibase:4.25.1-alpine
ADD resources/changeLog/ /liquibase/

View File

@@ -0,0 +1,27 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
@Entity(name = "currency")
class Currency(@Column(name="name", nullable = false)
val name: String,
@Column(name="description")
val description: String,
@Column(name="icon_url", nullable = false)
val iconUrl: String,
@OneToMany(
fetch = FetchType.LAZY,
cascade = [CascadeType.PERSIST, CascadeType.MERGE],
mappedBy = "currency"
)
private var openings: List<OpeningCurrency>? = null,
@Id
@Column(name="id", nullable = false)
val id: Int) {
}

View File

@@ -0,0 +1,36 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
@Entity(name = "item")
class Item(@Column(name="name", nullable = false)
val name: String,
@Column(name="description")
val description: String,
@Column(name="icon_url", nullable = false)
val iconUrl: String,
@Column(name="type", nullable = false)
val type: String,
@Column(name="rarity", nullable = false)
val rarity: String,
@OneToMany(cascade = [CascadeType.MERGE, CascadeType.PERSIST], orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "item")
var submissionTemplates: List<SubmissionTemplate>? = null,
@OneToMany(
fetch = FetchType.LAZY,
cascade = [CascadeType.PERSIST, CascadeType.MERGE],
mappedBy = "item"
)
private var openings: List<OpeningItem>? = null,
@Id
@Column(name="id", nullable = false)
val id: Int) {
}

View File

@@ -0,0 +1,25 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
import java.time.Instant
@Entity(name = "opening")
class Opening(
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", referencedColumnName = "id")
val user: User,
@OneToMany(cascade = [CascadeType.MERGE, CascadeType.PERSIST], orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "opening")
var currencies: List<OpeningCurrency>? = null,
@OneToMany(cascade = [CascadeType.MERGE, CascadeType.PERSIST], orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "opening")
var items: List<OpeningItem>? = null,
@Column(name = "description")
var description: String?=null,
@Column(name = "created", insertable = false, updatable = false)
var creationDate: Instant?=null,
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?=null
) {
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
@Entity(name = "opening_currency")
class OpeningCurrency(
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinColumn(name = "currency_id", nullable = false)
val currency: Currency,
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinColumn(name = "opening_id", nullable = false)
val opening: Opening,
@Column(name = "amount")
val amount: Int,
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?=null
) {
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
@Entity(name = "opening_item")
class OpeningItem(
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinColumn(name = "item_id", nullable = false)
val item: Item,
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinColumn(name = "opening_id", nullable = false)
val opening: Opening,
@Column(name = "count")
val count: Int,
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?=null
) {
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
@Entity(name = "submission_template")
class SubmissionTemplate(
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinColumn(name = "item_id", nullable = false)
val item: Item,
@Column(name = "template_text")
val templateText: String,
@Column(name = "name")
val name: String,
@Column(name = "description")
val description: String,
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?=null
) {
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.Id
@Entity(name = "gw2_user")
class User( @Id
@Column(name="id", nullable = false)
val id: String) {
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.gw2.tools.repo
import dev.sheldan.gw2.tools.entity.Currency
import org.springframework.data.repository.CrudRepository
interface CurrencyRepository : CrudRepository<Currency, Int> {
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.gw2.tools.repo
import dev.sheldan.gw2.tools.entity.Item
import org.springframework.data.repository.CrudRepository
interface ItemRepository : CrudRepository<Item, Int> {
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.gw2.tools.repo
import dev.sheldan.gw2.tools.entity.Opening
import dev.sheldan.gw2.tools.entity.User
import org.springframework.data.repository.CrudRepository
interface OpeningRepository : CrudRepository<Opening, Int> {
fun getAllByUser(user: User): List<Opening>
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.gw2.tools.repo
import dev.sheldan.gw2.tools.entity.Item
import dev.sheldan.gw2.tools.entity.SubmissionTemplate
import org.springframework.data.repository.CrudRepository
interface SubmissionTemplateRepository : CrudRepository<SubmissionTemplate, Int> {
fun getSubmissionTemplateByItem(item: Item): List<SubmissionTemplate>
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.gw2.tools.repo
import dev.sheldan.gw2.tools.entity.User
import org.springframework.data.repository.CrudRepository
interface UserRepository : CrudRepository<User, String> {
}

View File

@@ -0,0 +1,37 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.Currency
import dev.sheldan.gw2.tools.repo.CurrencyRepository
import org.springframework.stereotype.Component
@Component
class CurrencyManagement(val currencyRepository: CurrencyRepository) {
fun getCurrencies(currencyIds: List<Int>): List<Currency> {
return currencyRepository.findAllById(currencyIds).toList()
}
fun getCurrenciesAsMap(currencyIds: List<Int>): Map<Int, Currency> {
return getCurrencies(currencyIds).associateBy { it.id }
}
fun getCurrencies() : List<Currency> {
return currencyRepository.findAll().toList()
}
fun createAndSaveCurrency(id: Int, name: String, description: String, iconUrl: String): Currency {
val currency = createCurrency(id, name, description, iconUrl)
return currencyRepository.save(currency)
}
fun createCurrency(id: Int, name: String, description: String, iconUrl: String): Currency {
return Currency(name, description, iconUrl, null, id)
}
fun saveCurrency(currency: Currency): Currency {
return currencyRepository.save(currency)
}
fun saveCurrencies(currencies: List<Currency>) {
currencyRepository.saveAll(currencies)
}
}

View File

@@ -0,0 +1,42 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.Item
import dev.sheldan.gw2.tools.repo.ItemRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component
@Component
class ItemManagement(val itemRepository: ItemRepository) {
fun getItems(itemIds: List<Int>) : List<Item> {
return itemRepository.findAllById(itemIds).toList()
}
fun getItem(itemId: Int) : Item? {
return itemRepository.findByIdOrNull(itemId)
}
fun getItemsAsMap(itemIds: List<Int>) : Map<Int, Item> {
return getItems(itemIds).associateBy { it.id }
}
fun getItems() : List<Item> {
return itemRepository.findAll().toList()
}
fun createItem(id: Int, name: String, description: String, iconUrl: String, type: String, rarity: String): Item {
return Item(name, description, iconUrl, type, rarity, null, null, id)
}
fun createAndSaveItem(id: Int, name: String, description: String, iconUrl: String, type: String, rarity: String): Item {
val item = createItem(id, name, description, iconUrl, type, rarity)
return saveItem(item)
}
fun saveItems(items: List<Item>) {
itemRepository.saveAll(items)
}
fun saveItem(item: Item): Item {
return itemRepository.save(item)
}
}

View File

@@ -0,0 +1,37 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.*
import dev.sheldan.gw2.tools.entity.Currency
import dev.sheldan.gw2.tools.repo.OpeningRepository
import org.springframework.stereotype.Component
import java.util.*
@Component
class OpeningManagement(
val openingRepository: OpeningRepository
) {
fun createOpening(user: User, items: Map<Item, Int>, currencies: Map<Currency, Int>, description: String?){
val opening = Opening(user, description = description)
val openingItems: List<OpeningItem> = items.map { OpeningItem(it.key, opening, it.value) }
val openingCurrencies: List<OpeningCurrency> = currencies.map { OpeningCurrency(it.key, opening, it.value) }
opening.currencies = openingCurrencies
opening.items = openingItems
openingRepository.save(opening)
}
fun getOpeningsByUser(user: User): List<Opening> {
return openingRepository.getAllByUser(user)
}
fun getAllOpenings(): List<Opening> {
return openingRepository.findAll().toList()
}
fun getOpening(id: Int): Optional<Opening> {
return openingRepository.findById(id)
}
fun deleteOpening(opening: Opening) {
openingRepository.delete(opening)
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.Item
import dev.sheldan.gw2.tools.entity.SubmissionTemplate
import dev.sheldan.gw2.tools.repo.SubmissionTemplateRepository
import org.springframework.stereotype.Component
@Component
class SubmissionTemplateManagement(
val submissionTemplateRepository: SubmissionTemplateRepository
) {
fun getSubmissionTemplatesForItem(item: Item): List<SubmissionTemplate> {
return submissionTemplateRepository.getSubmissionTemplateByItem(item)
}
fun getAllSubmissionTemplates(): List<SubmissionTemplate> {
return submissionTemplateRepository.findAll().toList()
}
}

View File

@@ -0,0 +1,39 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.User
import dev.sheldan.gw2.tools.repo.UserRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component
import java.security.MessageDigest
@Component
class UserManagement(val userRepository: UserRepository) {
fun getUser(id: String): User? {
return userRepository.findByIdOrNull(id)
}
fun getOrCreateUser(token: String): User {
val userId = createUserId(token)
val possibleUser = getUser(userId)
return possibleUser ?: createUser(userId)
}
fun createUserWithId(id: String): User {
val userObj = User(id)
return userRepository.save(userObj)
}
fun createUser(token: String): User {
val hashed = createUserId(token)
val userObj = User(hashed)
return userRepository.save(userObj)
}
private fun createUserId(token: String): String {
val md = MessageDigest.getInstance("SHA-256")
val digest = md.digest(token.toByteArray())
val hashed = digest.fold("") { str, it -> str + "%02x".format(it) }
return hashed
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,34 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="currency-table">
<createTable tableName="currency">
<column name="id" type="INTEGER">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="VARCHAR(64)">
<constraints nullable="false"/>
</column>
<column name="description" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="icon_url" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<sql>
DROP TRIGGER IF EXISTS currency_update_trigger ON currency;
CREATE TRIGGER currency_update_trigger BEFORE UPDATE ON currency FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS currency_insert_trigger ON currency;
CREATE TRIGGER currency_insert_trigger BEFORE INSERT ON currency FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,44 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="item-table">
<createTable tableName="item">
<column name="id" type="INTEGER">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="VARCHAR(128)">
<constraints nullable="false"/>
</column>
<column name="description" type="VARCHAR(1024)">
<constraints nullable="true"/>
</column>
<column name="icon_url" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="type" type="VARCHAR(64)">
<constraints nullable="true"/>
</column>
<column name="rarity" type="VARCHAR(32)">
<constraints nullable="true"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<sql>
ALTER TABLE item ADD CONSTRAINT check_item_rarity CHECK (rarity IN ('JUNK', 'BASIC', 'FINE', 'MASTERWORK', 'RARE', 'EXOTIC', 'ASCENDED', 'LEGENDARY'));
ALTER TABLE item ADD CONSTRAINT check_item_type CHECK (type IN ('CONTAINER', 'ARMOR', 'BACK', 'BAG', 'CONSUMABLE', 'CRAFTING_MATERIAL', 'GATHERING', 'GIZMO', 'JADE_TECH_MODULE', 'KEY', 'MINI_PET', 'POWER_CORE', 'TOOL', 'TRAIT', 'TRINKET', 'TROPHY', 'UPGRADE_COMPONENT', 'WEAPON', 'RELIC'));
</sql>
<sql>
DROP TRIGGER IF EXISTS item_update_trigger ON item;
CREATE TRIGGER item_update_trigger BEFORE UPDATE ON item FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS item_insert_trigger ON item;
CREATE TRIGGER item_insert_trigger BEFORE INSERT ON item FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,82 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="opening-table">
<createTable tableName="opening">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_opening"/>
</column>
<column name="user_id" type="VARCHAR(255)">
<constraints nullable="false" />
</column>
<column name="description" type="VARCHAR(1024)"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="user_id" baseTableName="opening" constraintName="fk_opening_user" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="gw2_user" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS opening_update_trigger ON opening;
CREATE TRIGGER opening_update_trigger BEFORE UPDATE ON opening FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS opening_insert_trigger ON opening;
CREATE TRIGGER opening_insert_trigger BEFORE INSERT ON opening FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
<changeSet author="Sheldan" id="opening_currency-table">
<createTable tableName="opening_currency">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_opening_currency"/>
</column>
<column name="opening_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="currency_id" type="INT">
<constraints nullable="false"/>
</column>
<column name="amount" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseColumnNames="opening_id" baseTableName="opening_currency"
constraintName="fk_opening_currency_opening" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="opening"
validate="true"/>
<addForeignKeyConstraint baseColumnNames="currency_id" baseTableName="opening_currency"
constraintName="fk_opening_currency_currency" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="currency" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="opening_item-table">
<createTable tableName="opening_item">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_opening_item"/>
</column>
<column name="opening_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="item_id" type="INT">
<constraints nullable="false"/>
</column>
<column name="count" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseColumnNames="opening_id" baseTableName="opening_item"
constraintName="fk_opening_item_opening" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="opening"
validate="true"/>
<addForeignKeyConstraint baseColumnNames="item_id" baseTableName="opening_item"
constraintName="fk_opening_item_item" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="item" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE FUNCTION insert_trigger_procedure() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.created := CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE FUNCTION update_trigger_procedure() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.updated := CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;

View File

@@ -0,0 +1,34 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="submission_template-table">
<createTable tableName="submission_template">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_submission_template"/>
</column>
<column name="item_id" type="INTEGER">
<constraints nullable="false" />
</column>
<column name="description" type="VARCHAR(1024)"/>
<column name="template_text" type="VARCHAR(2048)"/>
<column name="name" type="VARCHAR(128)"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="item_id" baseTableName="submission_template" constraintName="fk_submission_template_item" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="item" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS submission_template_update_trigger ON submission_template;
CREATE TRIGGER submission_template_update_trigger BEFORE UPDATE ON submission_template FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS submission_template_insert_trigger ON submission_template;
CREATE TRIGGER submission_template_insert_trigger BEFORE INSERT ON submission_template FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,11 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<include file="trigger_functions.xml" relativeToChangelogFile="true"/>
<include file="item.xml" relativeToChangelogFile="true"/>
<include file="currency.xml" relativeToChangelogFile="true"/>
<include file="user.xml" relativeToChangelogFile="true"/>
<include file="opening.xml" relativeToChangelogFile="true"/>
<include file="submission_template.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,15 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="insert_trigger" dbms="postgresql">
<sqlFile encoding="utf8" path="sql/insert_trigger.sql"
relativeToChangelogFile="true"
splitStatements="false"/>
</changeSet>
<changeSet author="Sheldan" id="update_trigger" dbms="postgresql">
<sqlFile encoding="utf8" path="sql/update_trigger.sql"
relativeToChangelogFile="true"
splitStatements="false"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,25 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="user-table">
<createTable tableName="gw2_user">
<column name="id" type="VARCHAR(255)">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<sql>
DROP TRIGGER IF EXISTS gw2_user_update_trigger ON gw2_user;
CREATE TRIGGER gw2_user_update_trigger BEFORE UPDATE ON gw2_user FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS gw2_user_insert_trigger ON gw2_user;
CREATE TRIGGER gw2_user_insert_trigger BEFORE INSERT ON gw2_user FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,6 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<include file="0.0.1/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
FROM amazoncorretto:21.0.1-alpine3.18
RUN apk add entr
WORKDIR /app
ADD BOOT-INF/lib/ /app/lib
ADD snapshots/ /app/lib
ADD META-INF /app/META-INF
ADD BOOT-INF/classes /app
ENTRYPOINT java -cp .:./lib/* dev.sheldan.gw2.tools.ToolApplicationKt

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>
<artifactId>executable</artifactId>
<properties>
<main.class>dev.sheldan.gw2.tools.ToolApplicationKt</main.class>
</properties>
<build>
<finalName>gw2-tools</finalName>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>${main.class}</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>single</goal> </goals>
<configuration>
<archive>
<manifest>
<mainClass>${main.class}</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>rest-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,13 @@
package dev.sheldan.gw2.tools
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
@SpringBootApplication(exclude = [org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration::class])
@EnableJpaRepositories(basePackages = ["dev.sheldan.gw2"])
class ToolApplication
fun main(args: Array<String>) {
runApplication<ToolApplication>(*args)
}

View File

@@ -0,0 +1,18 @@
management.endpoints.web.exposure.include=mappings
security.basic.enabled=false
management.security.enabled=false
spring.datasource.url=jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}
spring.datasource.username= ${DB_USER}
spring.datasource.password= ${DB_PASS}
spring.datasource.hikari.maximum-pool-size=${hikariPoolSize}
spring.jpa.hibernate.default_schema=gw2
spring.jpa.properties.hibernate.default_schema=gw2
spring.quartz.jdbc.initialize-schema=never
management.metrics.tags.application=GW2-Tools
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true
spring.application.name=GW2-Tools

View File

@@ -0,0 +1,11 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%-5level] [%logger{36}]: %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>
<artifactId>gw2-api-client</artifactId>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>io.github.kryszak</groupId>
<artifactId>gwatlin</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>database</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.config
interface ApiKey {
fun getApiKey(): String
fun setApiKey(key: String)
}

View File

@@ -0,0 +1,38 @@
package dev.sheldan.gw2.tools.config
import io.github.kryszak.gwatlin.api.account.GWAccountClient
import io.github.kryszak.gwatlin.api.characters.GWCharactersClient
import io.github.kryszak.gwatlin.api.items.GWItemsClient
import io.github.kryszak.gwatlin.api.miscellaneous.GWMiscellaneousClient
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.ScopedProxyMode
import org.springframework.web.context.annotation.RequestScope
@Configuration
class BeanConfig(val apiKey: ApiKey) {
@Bean
@RequestScope(proxyMode = ScopedProxyMode.DEFAULT)
fun gwCharacterClient(): GWCharactersClient {
return GWCharactersClient(apiKey.getApiKey())
}
@Bean
@RequestScope(proxyMode = ScopedProxyMode.DEFAULT)
fun getAccountClient(): GWAccountClient {
return GWAccountClient(apiKey.getApiKey())
}
@Bean
fun getMiscellaneousClient(): GWMiscellaneousClient {
return GWMiscellaneousClient()
}
@Bean
fun gwItemClient(): GWItemsClient {
return GWItemsClient()
}
}

View File

@@ -0,0 +1,69 @@
package dev.sheldan.gw2.tools.loader
import dev.sheldan.gw2.tools.models.*
import dev.sheldan.gw2.tools.service.ItemManagement
import io.github.kryszak.gwatlin.api.account.GWAccountClient
import io.github.kryszak.gwatlin.api.characters.GWCharactersClient
import io.github.kryszak.gwatlin.api.miscellaneous.GWMiscellaneousClient
import org.springframework.stereotype.Component
import org.springframework.web.context.annotation.RequestScope
@Component
@RequestScope
class AccountLoader(
val charClient: GWCharactersClient,
val accountClient: GWAccountClient,
val miscellaneousClient: GWMiscellaneousClient,
val itemManagement: ItemManagement
) {
fun getCharacters(): List<String>? {
return charClient.getCharacters()
}
fun getWallet(): AccountWalletView {
val wallet = accountClient.getWallet()
val currencies = miscellaneousClient.getCurrencies().associateBy { it.id }
val enrichedCurrencies = wallet.mapNotNull { walletCurrency ->
val currency = currencies[walletCurrency.id]
currency?.let {
EnrichedCurrency(walletCurrency.id, walletCurrency.value, it.name, it.description, it.icon)
}
}
return AccountWalletView(enrichedCurrencies)
}
fun getBank(): AccountVaultView {
val bank = accountClient.getAccountVault()
val itemIds = mutableSetOf<Int>()
val items = bank.filterNotNull().mapNotNull {
itemIds.add(it.id)
EnrichedItem(it.id, it.count, it.boundTo)
}
val itemStats = itemManagement.getItemsAsMap(itemIds.toList())
items.forEach { item ->
itemStats[item.id]?.let {
item.setValuesFromDbItem(it)
}
}
return AccountVaultView(items)
}
fun getMaterials(): AccountMaterialView{
val materials = accountClient.getMaterials()
val itemIds = mutableSetOf<Int>()
val items = materials
.filter { it.count > 0 }
.map {
itemIds.add(it.id)
EnrichedItem(it.id, it.count)
}
val actualMaterials = itemManagement.getItemsAsMap(itemIds.toList())
items.forEach { item ->
actualMaterials[item.id]?.let {
item.setValuesFromDbItem(it)
}
}
return AccountMaterialView(items)
}
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.gw2.tools.loader
import dev.sheldan.gw2.tools.models.EnrichedCurrency
import io.github.kryszak.gwatlin.api.miscellaneous.GWMiscellaneousClient
import org.springframework.stereotype.Component
@Component
class CurrencyLoader(
var miscellaneousClient: GWMiscellaneousClient,
) {
fun getAllCurrencies(): List<EnrichedCurrency> {
return miscellaneousClient.getCurrencies().map { EnrichedCurrency(it.id, 0, it.name, it.description, it.icon) }
}
}

View File

@@ -0,0 +1,98 @@
package dev.sheldan.gw2.tools.loader
import dev.sheldan.gw2.tools.entity.Item
import dev.sheldan.gw2.tools.models.*
import dev.sheldan.gw2.tools.service.ItemManagement
import io.github.kryszak.gwatlin.api.account.GWAccountClient
import io.github.kryszak.gwatlin.api.account.model.InventoryItem
import io.github.kryszak.gwatlin.api.characters.GWCharactersClient
import io.github.kryszak.gwatlin.api.characters.model.character.inventory.Bag
import io.github.kryszak.gwatlin.api.characters.model.character.inventory.InventorySlot
import org.springframework.stereotype.Component
import org.springframework.web.context.annotation.RequestScope
@Component
@RequestScope
class InventoryLoader(
val charClient: GWCharactersClient,
val accountClient: GWAccountClient,
val itemManagement: ItemManagement
) {
fun getInventory(characterName: String): List<Bag?>? {
return charClient.getInventory(characterName)
}
fun getFullCharacterInventory(characterName: String): CharacterInventory {
val itemIds = mutableSetOf<Int>()
val characterInventory = getCharacterInventory(characterName, itemIds)
val itemStats = getFullItemInfos(itemIds)
enrichCharacterInventory(characterInventory, itemStats)
return characterInventory
}
private fun getCharacterInventory(characterName: String, itemIds: MutableSet<Int>): CharacterInventory {
val characterInventory = getInventory(characterName)
val convertedBags = mutableListOf<InventoryBag>()
characterInventory?.let {
for (bag in characterInventory.filterNotNull()) {
convertedBags.addLast(convertApiBagToCharacterInventory(bag, itemIds))
}
}
return CharacterInventory(characterName, convertedBags)
}
fun getAccountInventory(): AccountInventoryView {
val characters = charClient.getCharacters()
val itemIds = mutableSetOf<Int>()
val inventories = mutableListOf<CharacterInventory>()
for (characterName in characters) {
inventories.addLast(getCharacterInventory(characterName, itemIds))
}
val itemStats = getFullItemInfos(itemIds)
inventories.forEach { enrichCharacterInventory(it, itemStats) }
return AccountInventoryView(inventories)
}
private fun enrichCharacterInventory(characterInventory: CharacterInventory, itemStats: Map<Int, Item>) {
return characterInventory.bags.forEach { bag ->
bag.items.forEach { item ->
itemStats[item.id]?.let {
item.setValuesFromDbItem(it)
}
}
}
}
private fun convertApiBagToCharacterInventory(bag: Bag, itemIds: MutableSet<Int>): InventoryBag {
val items = bag.inventory.filterNotNull().map { convertInventorySlot(it, itemIds) }
return InventoryBag(bag.id, bag.size, items)
}
private fun convertInventorySlot(slot: InventorySlot, itemIds: MutableSet<Int>): EnrichedItem {
itemIds.add(slot.id)
return EnrichedItem(slot.id, slot.count, slot.boundTo)
}
private fun convertInventoryItem(item: InventoryItem, itemIds: MutableSet<Int>): EnrichedItem {
itemIds.add(item.id)
return EnrichedItem(item.id, item.count, item.binding)
}
fun getSharedInventory(): AccountInventory {
val itemIds = mutableSetOf<Int>()
val sharedInventorySlots = accountClient.getInventory().filterNotNull()
val sharedItems = sharedInventorySlots.map { convertInventoryItem(it, itemIds) }
val fullItemInfo = getFullItemInfos(itemIds)
sharedItems.forEach { item ->
fullItemInfo[item.id]?.let {
item.setValuesFromDbItem(it)
}
}
return AccountInventory(sharedItems)
}
private fun getFullItemInfos(itemIds: MutableSet<Int>) = itemManagement.getItemsAsMap(itemIds.toList())
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.gw2.tools.loader
import dev.sheldan.gw2.tools.models.EnrichedItem
import io.github.kryszak.gwatlin.api.items.GWItemsClient
import org.springframework.stereotype.Component
@Component
class ItemLoader(
val itemClient: GWItemsClient
) {
fun getAllItems(): List<EnrichedItem> {
return itemClient.getItemIds().chunked(200)
.flatMap { (itemClient.getItems(it)) }
.map {
val item = EnrichedItem(it.id, 1)
item.setValuesFromItem(it)
item
}
}
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.models
class AccountInventory(
val slots: List<EnrichedItem>
) {
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.models
data class AccountInventoryView(
val inventories: List<CharacterInventory>
) {
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.models
class AccountMaterialView(
val slots: List<EnrichedItem>
) {
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.models
class AccountVaultView(
val slots: List<EnrichedItem>
) {
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.models
class AccountWalletView(
val currencies: List<EnrichedCurrency>
) {
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.gw2.tools.models
data class CharacterInventory(
val name: String,
val bags: List<InventoryBag>
)
{
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.gw2.tools.models
class EnrichedCurrency(
val id: Int,
var amount: Int,
val name: String,
val description: String,
val iconUrl: String
) {
}

View File

@@ -0,0 +1,33 @@
package dev.sheldan.gw2.tools.models
import io.github.kryszak.gwatlin.api.items.model.item.Item
data class EnrichedItem(
val id: Int,
var count: Int,
val boundTo: String? = null,
var iconUrl: String? = null,
var name: String? = null,
var description: String? = null,
var type: Gw2TItemType? = null,
var level: Int? = null,
var rarity: Gw2TItemRarity? = null
) {
fun setValuesFromItem(item: Item) {
this.name = item.name
this.level = item.level
this.description = item.description
this.type = Gw2TItemType.convertFromAPI(item.type)
this.rarity = Gw2TItemRarity.convertFromAPI(item.rarity)
this.iconUrl = item.icon.ifBlank { "https://render.guildwars2.com/file/4BC52199DBDEEF5D4D90736B582DDA0F092B0DE4/434780.png" } // default icon if nothing is shown
}
fun setValuesFromDbItem(item: dev.sheldan.gw2.tools.entity.Item) {
this.name = item.name
this.description = item.description
this.type = Gw2TItemType.valueOf(item.type)
this.rarity = Gw2TItemRarity.valueOf(item.rarity)
this.iconUrl = item.iconUrl
}
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.gw2.tools.models
import io.github.kryszak.gwatlin.api.items.model.item.ItemRarity
enum class Gw2TItemRarity {
JUNK,
BASIC,
FINE,
MASTERWORK,
RARE,
EXOTIC,
ASCENDED,
LEGENDARY;
companion object {
fun convertFromAPI(rarity: ItemRarity): Gw2TItemRarity = when (rarity) {
ItemRarity.FINE -> FINE
ItemRarity.MASTERWORK -> MASTERWORK
ItemRarity.RARE -> RARE
ItemRarity.EXOTIC -> EXOTIC
ItemRarity.JUNK -> JUNK
ItemRarity.BASIC -> BASIC
ItemRarity.ASCENDED -> ASCENDED
ItemRarity.LEGENDARY -> LEGENDARY
}
}
}

View File

@@ -0,0 +1,51 @@
package dev.sheldan.gw2.tools.models
import io.github.kryszak.gwatlin.api.items.model.item.ItemType
enum class Gw2TItemType {
CONTAINER,
ARMOR,
BACK,
BAG,
CONSUMABLE,
CRAFTING_MATERIAL,
GATHERING,
GIZMO,
JADE_TECH_MODULE,
KEY,
MINI_PET,
POWER_CORE,
TOOL,
TRAIT,
TRINKET,
TROPHY,
UPGRADE_COMPONENT,
WEAPON,
RELIC;
companion object {
fun convertFromAPI(type: ItemType): Gw2TItemType = when (type) {
ItemType.CONTAINER -> CONTAINER
ItemType.ARMOR -> ARMOR
ItemType.BACK -> BACK
ItemType.BAG -> BAG
ItemType.CONSUMABLE -> CONSUMABLE
ItemType.CRAFTING_MATERIAL -> CRAFTING_MATERIAL
ItemType.GATHERING -> GATHERING
ItemType.GIZMO -> GIZMO
ItemType.JADE_TECH_MODULE -> JADE_TECH_MODULE
ItemType.KEY -> KEY
ItemType.MINI_PET -> MINI_PET
ItemType.POWER_CORE -> POWER_CORE
ItemType.TOOL -> TOOL
ItemType.TRAIT -> TRAIT
ItemType.TRINKET -> TRINKET
ItemType.TROPHY -> TROPHY
ItemType.UPGRADE_COMPONENT -> UPGRADE_COMPONENT
ItemType.WEAPON -> WEAPON
ItemType.RELIC -> RELIC
}
}
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.gw2.tools.models
data class InventoryBag(
val id: Int,
val size: Int,
val items: List<EnrichedItem>
) {
}

308
gw2-tools-backend/mvnw vendored Executable file
View File

@@ -0,0 +1,308 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.2.0
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "$(uname)" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
else
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
if $darwin ; then
javaHome="$(dirname "\"$javaExecutable\"")"
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
else
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
fi
javaHome="$(dirname "\"$javaExecutable\"")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
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
else
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=$(cd "$wdir/.." || exit 1; pwd)
fi
# end of workaround
done
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
# Remove \r in case we run on Windows within Git Bash
# and check out the repository with auto CRLF management
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
tr -s '\r\n' ' ' < "$1"
fi
}
log() {
if [ "$MVNW_VERBOSE" = true ]; then
printf '%s\n' "$1"
fi
}
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
fi
while IFS="=" read -r key value; do
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
safeValue=$(echo "$value" | tr -d '\r')
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
log "Downloading from: $wrapperUrl"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget > /dev/null; then
log "Found wget ... using wget"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
log "Found curl ... using curl"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
fi
else
log "Falling back to using Java to download"
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaSource" ]; then
if [ ! -e "$javaClass" ]; then
log " - Compiling MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/javac" "$javaSource")
fi
if [ -e "$javaClass" ]; then
log " - Running MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
wrapperSha256Sum=""
while IFS="=" read -r key value; do
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
if command -v sha256sum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
elif command -v shasum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
exit 1
fi
if [ $wrapperSha256Result = false ]; then
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
exit 1
fi
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
# shellcheck disable=SC2086 # safe args
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

205
gw2-tools-backend/mvnw.cmd vendored Normal file
View File

@@ -0,0 +1,205 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. 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,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.2.0
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %WRAPPER_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
SET WRAPPER_SHA_256_SUM=""
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" exit 1;"^
"}"^
"}"
if ERRORLEVEL 1 goto error
)
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>
<artifactId>packaging</artifactId>
<packaging>pom</packaging>
<properties>
<file.basedir>${project.basedir}/src/main/docker/</file.basedir>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<!-- backend jar -->
<artifactItem>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>executable</artifactId>
<version>${project.version}</version>
<classifier>exec</classifier>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${file.basedir}/tools</outputDirectory>
<destFileName>app.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,8 @@
FROM amazoncorretto:21.0.1-alpine3.18
MAINTAINER Sheldan
VOLUME /tmp
ADD config/* /config/
ADD wrapper/*.sh /
RUN chmod +x /start.sh
ADD tools/app.jar /app.jar
CMD ["/start.sh"]

View File

@@ -0,0 +1 @@
logging.config=config/logback.xml

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<property name="LOG_PATH" value="logs"/>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<appender name="logFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/log.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>
%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</Pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>
${LOG_PATH}/archived/log_%d{dd-MM-yyyy}.log
</fileNamePattern>
<maxHistory>10</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
</appender>
<logger name="dev.sheldan.gw2" level="INFO"/>
<root level="info">
<appender-ref ref="logFileAppender"/>
<appender-ref ref="stdout"/>
</root>
</configuration>

View File

@@ -0,0 +1,7 @@
#!/bin/sh
DEBUG_PARAMS=""
if [ "x$REMOTE_DEBUG" = 'xtrue' ]; then
DEBUG_PARAMS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
echo "Starting with remote debugging on port 5005"
fi;
java ${DEBUG_PARAMS} -jar app.jar

176
gw2-tools-backend/pom.xml Normal file
View File

@@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.1</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
<packaging>pom</packaging>
<name>gw2-tools</name>
<description>Tools for GW2</description>
<modules>
<module>executable</module>
<module>gw2-api-client</module>
<module>rest-api</module>
<module>packaging</module>
<module>database</module>
</modules>
<scm>
<connection>scm:git:${project.scm.url}</connection>
<developerConnection>scm:git:${project.scm.url}</developerConnection>
<url>https://github.com/Sheldan/gw2-tools.git</url>
<tag>HEAD</tag>
</scm>
<distributionManagement>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/Sheldan/gw2-tools</url>
</repository>
</distributionManagement>
<properties>
<java.version>21</java.version>
<kotlin.version>1.9.21</kotlin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>21</kotlin.compiler.jvmTarget>
<maven-surefire-plugin.version>3.2.5</maven-surefire-plugin.version>
<maven-failsafe-plugin.version>3.2.5</maven-failsafe-plugin.version>
<exec-maven-plugin.version>3.1.1</exec-maven-plugin.version>
<junit-jupiter-engine.version>5.10.1</junit-jupiter-engine.version>
<maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
<maven-assembly-plugin.version>3.6.0</maven-assembly-plugin.version>
<kotlin-logging.version>2.0.11</kotlin-logging.version>
<gwatlin.version>1.9.5</gwatlin.version>
<maven-release-plugin.version>2.5.3</maven-release-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
<plugin>jpa</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>${maven-release-plugin.version}</version>
<configuration>
<scmCommentPrefix>[RELEASE]</scmCommentPrefix>
<tagNameFormat>@{project.version}</tagNameFormat>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter-engine.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>io.github.kryszak</groupId>
<artifactId>gwatlin</artifactId>
<version>${gwatlin.version}</version>
</dependency>
<dependency>
<groupId>io.github.microutils</groupId>
<artifactId>kotlin-logging-jvm</artifactId>
<version>${kotlin-logging.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>
<artifactId>rest-api</artifactId>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-api-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>database</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.github.microutils</groupId>
<artifactId>kotlin-logging-jvm</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,4 @@
package dev.sheldan.gw2.tools
class ItemNotFoundException(message: String): Throwable(message) {
}

View File

@@ -0,0 +1,33 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.loader.AccountLoader
import dev.sheldan.gw2.tools.models.AccountMaterialView
import dev.sheldan.gw2.tools.models.AccountVaultView
import dev.sheldan.gw2.tools.models.AccountWalletView
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.context.annotation.RequestScope
@RestController
@RequestScope
class AccountController(var accountLoader: AccountLoader) {
@GetMapping("/characters")
fun inventory(): List<String>? {
return accountLoader.getCharacters()
}
@GetMapping("/wallet")
fun wallet(): AccountWalletView {
return accountLoader.getWallet()
}
@GetMapping("/bank")
fun bank(): AccountVaultView {
return accountLoader.getBank()
}
@GetMapping("/materials")
fun materials(): AccountMaterialView {
return accountLoader.getMaterials()
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.service.CacheService
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class CurrencyController(val cacheService: CacheService) {
@PostMapping("/currency-cache")
fun updateCurrencyCache() {
cacheService.reloadCurrencyCache()
}
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.gw2.tools.api
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class Health {
@GetMapping("/health-check")
fun healCheck(): String {
return "yes"
}
}

View File

@@ -0,0 +1,31 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.loader.InventoryLoader
import dev.sheldan.gw2.tools.models.AccountInventory
import dev.sheldan.gw2.tools.models.AccountInventoryView
import dev.sheldan.gw2.tools.models.CharacterInventory
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.context.annotation.RequestScope
@RestController
@RequestScope
class InventoryController(var inventoryLoader: InventoryLoader) {
@GetMapping("/inventory/{name}")
fun characterInventory(@PathVariable("name") characterName: String): CharacterInventory {
return inventoryLoader.getFullCharacterInventory(characterName)
}
@GetMapping("/inventory")
fun completeCharacterInventory(): AccountInventoryView {
return inventoryLoader.getAccountInventory()
}
@GetMapping("/sharedInventory")
fun accountInventory(): AccountInventory {
return inventoryLoader.getSharedInventory()
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.service.CacheService
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class ItemController(val cacheService: CacheService) {
@PostMapping("/item-cache")
fun updateItemCache() {
cacheService.reloadItemCache()
}
}

View File

@@ -0,0 +1,46 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.config.ApiKeyInterceptor
import dev.sheldan.gw2.tools.model.ItemRates
import dev.sheldan.gw2.tools.model.OpeningRequest
import dev.sheldan.gw2.tools.model.OpeningsView
import dev.sheldan.gw2.tools.service.OpeningService
import jakarta.servlet.http.HttpServletRequest
import mu.KotlinLogging
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
import org.springframework.web.context.annotation.RequestScope
@RestController
@RequestScope
class OpeningController(val openingService: OpeningService) {
private val logger = KotlinLogging.logger {}
@PostMapping("/openings")
@ResponseStatus(HttpStatus.CREATED)
fun createOpenings(request: HttpServletRequest, @RequestBody openingRequest: OpeningRequest) {
val apiKey: String = request.getHeader(ApiKeyInterceptor.API_KEY_HEADER_NAME) ?: throw IllegalArgumentException("API key not provided.")
openingService.createOpening(openingRequest, apiKey)
}
@GetMapping("/openings")
fun getOpenings(request: HttpServletRequest, @RequestParam("showOwnOnly") ownOnly: String?): OpeningsView {
val apiKey: String? = request.getHeader(ApiKeyInterceptor.API_KEY_HEADER_NAME)
val showOnOnly = ownOnly.toBoolean()
logger.info { "Retrieving openings" }
val loadedOpenings = openingService.loadOpenings(apiKey, showOnOnly)
logger.info { "Loaded ${loadedOpenings.openings.size} openings" }
return loadedOpenings
}
@DeleteMapping("/openings/{id}")
fun deleteOpening(request: HttpServletRequest, @PathVariable("id") openingId: Int) {
val apiKey: String = request.getHeader(ApiKeyInterceptor.API_KEY_HEADER_NAME) ?: throw IllegalArgumentException("API key not provided.")
openingService.deleteOpening(apiKey, openingId)
}
@GetMapping("/itemRates")
fun getItemRates(): ItemRates {
return openingService.loadItemRates()
}
}

View File

@@ -0,0 +1,23 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.ItemNotFoundException
import dev.sheldan.gw2.tools.service.SubmissionTemplateService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@RestController
class SubmissionTemplateController(
val submissionTemplateService: SubmissionTemplateService
) {
@GetMapping("/submissionTemplates")
@ResponseBody
fun getSubmissionTemplatesForItem(@RequestParam("itemId") itemId: Int?): ResponseEntity<Any> {
try {
val responseObj = itemId?.let { submissionTemplateService.getSubmissionTemplateForItem(itemId) } ?: submissionTemplateService.getSubmissionTemplates()
return ResponseEntity(responseObj, HttpStatus.OK)
} catch (e: ItemNotFoundException) {
return ResponseEntity(e.message, HttpStatus.BAD_REQUEST)
}
}
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.gw2.tools.config
open class ApiKeyContainer : ApiKey {
var apiKeyValue: String = "";
override fun getApiKey(): String {
return apiKeyValue
}
override fun setApiKey(key: String) {
this.apiKeyValue = key
}
}

View File

@@ -0,0 +1,18 @@
package dev.sheldan.gw2.tools.config
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.web.servlet.HandlerInterceptor
class ApiKeyInterceptor(private val apiKeyContainer: ApiKey) : HandlerInterceptor {
companion object {
const val API_KEY_HEADER_NAME = "api-key"
}
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
val apiKey: String? = request.getHeader(API_KEY_HEADER_NAME)
apiKey?.let { apiKeyContainer.setApiKey(it) }
return true
}
}

View File

@@ -0,0 +1,26 @@
package dev.sheldan.gw2.tools.config
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.context.annotation.RequestScope
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration
class HeaderInterceptorConfig : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(apiKeyInterceptor())
}
@Bean
fun apiKeyInterceptor(): ApiKeyInterceptor {
return ApiKeyInterceptor(apiContainer())
}
@Bean
@RequestScope
fun apiContainer(): ApiKeyContainer {
return ApiKeyContainer()
}
}

View File

@@ -0,0 +1,5 @@
package dev.sheldan.gw2.tools.model
data class DisplayOpeningItem(val itemId: Int, val change: Int, val itemType: OpeningItemType) {
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.gw2.tools.model
class ItemDisplay(
val id: Int,
val name: String,
val iconUrl: String,
val description: String,
val rarity: String
) {
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.gw2.tools.model
import dev.sheldan.gw2.tools.models.EnrichedCurrency
import dev.sheldan.gw2.tools.models.EnrichedItem
class ItemRate(val item: EnrichedItem, val receivedItems: List<EnrichedItem>, val receivedCurrencies: List<EnrichedCurrency>) {
}

View File

@@ -0,0 +1,4 @@
package dev.sheldan.gw2.tools.model
class ItemRates(val itemRates: List<ItemRate>) {
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.gw2.tools.model
class ItemSubmissionTemplate(
val submissionTemplates: List<SubmissionTemplateDisplay>,
val item: ItemDisplay
) {
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.model
class ItemSubmissionTemplateList(
val itemSubmissionTemplates: List<ItemSubmissionTemplate>
) {
}

View File

@@ -0,0 +1,5 @@
package dev.sheldan.gw2.tools.model
enum class OpeningItemType {
CURRENCY, ITEM
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.gw2.tools.model
data class OpeningRequest(
val items: List<DisplayOpeningItem>,
val description: String?=null,
) {
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.gw2.tools.model
import dev.sheldan.gw2.tools.models.EnrichedCurrency
import dev.sheldan.gw2.tools.models.EnrichedItem
import java.time.Instant
class OpeningView(
val itemChanges: List<EnrichedItem>,
val currencyChanges: List<EnrichedCurrency>,
val openingId: Int,
val description: String?,
val openingDate: Instant) {
}

View File

@@ -0,0 +1,4 @@
package dev.sheldan.gw2.tools.model
class OpeningsView(val openings: List<OpeningView>) {
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.gw2.tools.model
class SubmissionTemplateDisplay(
val name: String,
val description: String,
val templateText: String
) {
}

View File

@@ -0,0 +1,62 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.loader.CurrencyLoader
import dev.sheldan.gw2.tools.loader.ItemLoader
import mu.KotlinLogging
import org.springframework.stereotype.Component
@Component
class CacheService(
val itemLoader: ItemLoader,
val itemManagement: ItemManagement,
val currencyLoader: CurrencyLoader,
val currencyManagement: CurrencyManagement
) {
private val logger = KotlinLogging.logger {}
fun reloadItemCache() {
val allItemsApi = itemLoader.getAllItems().associateBy { it.id }
logger.info { "Loaded ${allItemsApi.size} items from API" }
val allItemsDb = itemManagement.getItems().associateBy { it.id }
logger.info { "Loaded ${allItemsDb.size} items from DB" }
val missingItems = allItemsApi.keys.minus(allItemsDb.keys)
val newDbItems = missingItems.mapNotNull { itemId ->
val item = allItemsApi[itemId]
item?.let {
itemManagement.createItem(
it.id,
item.name!!,
item.description!!,
item.iconUrl!!,
it.type!!.name,
item.rarity!!.name
)
}
}.toList()
logger.info { "Creating ${newDbItems.size} new items" }
itemManagement.saveItems(newDbItems)
}
fun reloadCurrencyCache() {
val allCurrenciesApi = currencyLoader.getAllCurrencies().associateBy { it.id }
logger.info { "Loaded ${allCurrenciesApi.size} currencies from API" }
val allCurrenciesDb = currencyManagement.getCurrencies().associateBy { it.id }
logger.info { "Loaded ${allCurrenciesDb.size} currencies from DB" }
val missingCurrencies = allCurrenciesApi.keys.minus(allCurrenciesDb.keys)
val newDbCurrencies = missingCurrencies.mapNotNull {
itemId ->
val item = allCurrenciesApi[itemId]
item?.let {
currencyManagement.createCurrency(
it.id,
item.name,
item.description,
item.iconUrl
)
}
}
logger.info { "Creating ${newDbCurrencies.size} new items" }
currencyManagement.saveCurrencies(newDbCurrencies)
}
}

View File

@@ -0,0 +1,138 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.Currency
import dev.sheldan.gw2.tools.entity.Item
import dev.sheldan.gw2.tools.entity.Opening
import dev.sheldan.gw2.tools.entity.OpeningItem
import dev.sheldan.gw2.tools.model.*
import dev.sheldan.gw2.tools.models.EnrichedCurrency
import dev.sheldan.gw2.tools.models.EnrichedItem
import dev.sheldan.gw2.tools.models.Gw2TItemType
import org.springframework.stereotype.Component
@Component
class OpeningService(
val userManagement: UserManagement,
val openingManagement: OpeningManagement,
val itemManagement: ItemManagement,
val currencyManagement: CurrencyManagement
) {
fun createOpening(openingRequest: OpeningRequest, token: String) {
val user = userManagement.getOrCreateUser(token)
val itemsWithIds = openingRequest.items.filter { it.itemType == OpeningItemType.ITEM }.associateBy { it.itemId }
val fullItems =
itemManagement.getItems(itemsWithIds.keys.toList()).associateWith { itemsWithIds[it.id]!!.change }
val currenciesWithIds =
openingRequest.items.filter { it.itemType == OpeningItemType.CURRENCY }.associateBy { it.itemId }
val fullCurrencies = currencyManagement.getCurrencies(currenciesWithIds.keys.toList())
.associateWith { currenciesWithIds[it.id]!!.change }
openingManagement.createOpening(user, fullItems, fullCurrencies, openingRequest.description)
}
fun loadOpenings(token: String?, ownOnly: Boolean): OpeningsView {
val openings: List<Opening>;
if (ownOnly && token != null) {
val user = userManagement.getOrCreateUser(token)
openings = openingManagement.getOpeningsByUser(user)
} else {
openings = openingManagement.getAllOpenings()
}
val items = openings.filter { it.items != null }.flatMap { it.items!! }
val itemIds = items.map { it.item.id }
val loadedItemsMap = itemManagement.getItemsAsMap(itemIds)
val currencies = openings.filter { it.currencies != null }.flatMap { it.currencies!! }
val currencyIds = currencies.map { it.currency.id }
val loadedCurrenciesMapMap = currencyManagement.getCurrenciesAsMap(currencyIds)
val openingViews = openings.map { convertOpening(it, loadedItemsMap, loadedCurrenciesMapMap) }
return OpeningsView(openingViews)
}
fun deleteOpening(token: String, openingId: Int) {
val user = userManagement.getOrCreateUser(token)
val opening = openingManagement.getOpening(openingId)
opening.map {
if (user.id == it.user.id) {
openingManagement.deleteOpening(it)
}
}
}
fun convertOpening(opening: Opening, itemMap: Map<Int, Item>, currencyMap: Map<Int, Currency>): OpeningView {
val items = mutableListOf<EnrichedItem>()
opening.items?.mapNotNull {
val enrichedItem = EnrichedItem(it.item.id, it.count)
itemMap[it.item.id]?.let { item ->
enrichedItem.setValuesFromDbItem(item)
return@mapNotNull enrichedItem
}
}?.let { items.addAll(it) }
val currencies = mutableListOf<EnrichedCurrency>()
opening.currencies?.mapNotNull {
currencyMap[it.currency.id]?.let { currency ->
EnrichedCurrency(currency.id, it.amount, currency.name, currency.description, currency.iconUrl)
}
}?.let { currencies.addAll(it) }
return OpeningView(items, currencies, opening.id!!, opening.description, opening.creationDate!!)
}
fun loadItemRates(): ItemRates {
val openings = openingManagement.getAllOpenings()
val presentItems = openings.filter { it.items != null }.flatMap { it.items!! }.map { it.item }
val presentCurrencies = openings.filter { it.currencies != null }.flatMap { it.currencies!! }.map { it.currency }
val itemIds = presentItems.map { it.id }
val currencyIds = presentCurrencies.map { it.id }
val loadedItemsMap = itemManagement.getItemsAsMap(itemIds)
val loadedCurrenciesMap = currencyManagement.getCurrenciesAsMap(currencyIds)
val allItems = openings.mapNotNull { opening ->
val containers = getListOfContainersFromOpening(opening)
if(containers.size == 1) {
val containerOpeningItem = containers[0]
val enrichedContainerItem = EnrichedItem(containerOpeningItem.item.id, containerOpeningItem.count)
loadedItemsMap[containerOpeningItem.item.id]?.let { item ->
enrichedContainerItem.setValuesFromDbItem(item)
}
val resultingItems = opening.items?.filter { it.count > 0 }?.mapNotNull itemMap@ {
val enrichedItem = EnrichedItem(it.item.id, it.count)
loadedItemsMap[it.item.id]?.let { item ->
enrichedItem.setValuesFromDbItem(item)
return@itemMap enrichedItem
}
}?: listOf()
val resultingCurrencies = opening.currencies?.filter { it.amount > 0 }?.mapNotNull currencyMap@ {
loadedCurrenciesMap[it.currency.id]?.let { currency ->
val enrichedCurrency = EnrichedCurrency(it.currency.id, it.amount, currency.name, currency.description, currency.iconUrl)
return@currencyMap enrichedCurrency
}
}?: listOf()
return@mapNotNull ItemRate(enrichedContainerItem, resultingItems, resultingCurrencies)
} else {
null
}
}
val itemRateMap = mutableMapOf<Int, ItemRate>()
allItems.forEach{ item ->
itemRateMap[item.item.id]?.let { itemRate ->
val existingEnrichedItems = itemRate.receivedItems.associateBy { receivedItem -> receivedItem.id }
item.receivedItems.forEach{receivedItem ->
existingEnrichedItems[receivedItem.id]?.let{
it.count += receivedItem.count
}?: itemRate.receivedItems.addLast(receivedItem)
}
val existingEnrichedCurrencies = itemRate.receivedCurrencies.associateBy { receivedCurrency -> receivedCurrency.id }
item.receivedCurrencies.forEach{receivedCurrency ->
existingEnrichedCurrencies[receivedCurrency.id]?.let{
it.amount += receivedCurrency.amount
}?: itemRate.receivedCurrencies.addLast(receivedCurrency)
}
itemRate.item.count += item.item.count
}?: itemRateMap.put(item.item.id, item)
}
return ItemRates(itemRateMap.values.toList())
}
private fun getListOfContainersFromOpening(opening: Opening): List<OpeningItem> {
return opening.items?.filter { item -> item.count < 0 && item.item.type == Gw2TItemType.CONTAINER.name } ?: listOf()
}
}

View File

@@ -0,0 +1,38 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.ItemNotFoundException
import dev.sheldan.gw2.tools.model.ItemDisplay
import dev.sheldan.gw2.tools.model.SubmissionTemplateDisplay
import dev.sheldan.gw2.tools.model.ItemSubmissionTemplate
import dev.sheldan.gw2.tools.model.ItemSubmissionTemplateList
import org.springframework.stereotype.Component
@Component
class SubmissionTemplateService(
val submissionTemplateManagement: SubmissionTemplateManagement,
val itemManagement: ItemManagement
) {
fun getSubmissionTemplateForItem(itemId: Int): ItemSubmissionTemplate {
val item = itemManagement.getItem(itemId)?: throw ItemNotFoundException("Item not found.")
val submissionTemplates = submissionTemplateManagement.getSubmissionTemplatesForItem(item)
val itemDisplay = ItemDisplay(item.id, item.name, item.iconUrl, item.description, item.rarity)
val templateDisplays = submissionTemplates.map {
SubmissionTemplateDisplay(it.name, it.description, it.templateText)
}
return ItemSubmissionTemplate(templateDisplays, itemDisplay)
}
fun getSubmissionTemplates(): ItemSubmissionTemplateList {
val submissionTemplates = submissionTemplateManagement.getAllSubmissionTemplates()
val items = submissionTemplates.associateBy { it.item }.keys
val itemSubmissionTemplates = items.map { item ->
val itemDisplay = ItemDisplay(item.id, item.name, item.iconUrl, item.description, item.rarity)
val submissionTemplatesOfItem = submissionTemplates.filter { it.item.id == item.id }
val templateDisplays = submissionTemplatesOfItem.map {
SubmissionTemplateDisplay(it.name, it.description, it.templateText)
}
ItemSubmissionTemplate(templateDisplays, itemDisplay)
}
return ItemSubmissionTemplateList(itemSubmissionTemplates)
}
}

View File

@@ -0,0 +1,19 @@
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>github</id>
<configuration>
<httpHeaders>
<property>
<name>Authorization</name>
<value>Bearer ${env.GITHUB_TOKEN}</value>
</property>
</httpHeaders>
</configuration>
</server>
</servers>
</settings>