diff --git a/docs/mammon-api.yml b/docs/mammon-api.yml index 406ff22..2974a14 100644 --- a/docs/mammon-api.yml +++ b/docs/mammon-api.yml @@ -57,7 +57,10 @@ paths: schema: $ref: "#/components/schemas/RuleBookResponse" "400": - description: Invalid request (e.g. blank name) + description: |- + Returned when: + - the request is invalid (e.g. blank name) + - the rule book is the default rule book, which cannot be modified delete: tags: - rule-book @@ -75,7 +78,10 @@ paths: "204": description: The rule book was deleted "400": - description: The rule book is associated to a character + description: |- + Returned when: + - the rule book is associated to a character + - the rule book is the default rule book, which cannot be deleted /ledgers/main/{ledgerId}: put: tags: @@ -105,7 +111,10 @@ paths: schema: $ref: "#/components/schemas/MainLedgerResponse" "400": - description: "The ledger is not a main ledger, or the request is invalid" + description: |- + Returned when: + - the ledger is not a main ledger + - the request is invalid "404": description: No ledger with this id /ledgers/combined/{ledgerId}: @@ -137,7 +146,10 @@ paths: schema: $ref: "#/components/schemas/CombinedLedgerResponse" "400": - description: "The ledger is not a combined ledger, or the request is invalid" + description: |- + Returned when: + - the ledger is not a combined ledger + - the request is invalid "404": description: No ledger with this id /characters/{characterId}/rule-book: @@ -193,8 +205,12 @@ paths: schema: $ref: "#/components/schemas/CharacterRuleBookResponse" "400": - description: "The referenced rule book or a bound ledger does not exist,\ - \ or a ledger binding is missing" + description: |- + Returned when: + - the referenced rule book does not exist + - a bound ledger does not exist + - a bound ledger is not a main or system ledger + - a required ledger binding is missing /rule-books: get: tags: @@ -225,6 +241,13 @@ paths: responses: "201": description: The created rule book + headers: + Location: + description: URL of the created rule book + style: simple + schema: + type: string + format: uri content: '*/*': schema: @@ -256,6 +279,13 @@ paths: responses: "201": description: The created main ledger + headers: + Location: + description: URL of the created main ledger + style: simple + schema: + type: string + format: uri content: '*/*': schema: @@ -278,6 +308,13 @@ paths: responses: "201": description: The created combined ledger + headers: + Location: + description: URL of the created combined ledger + style: simple + schema: + type: string + format: uri content: '*/*': schema: @@ -326,6 +363,86 @@ paths: text/plain: schema: type: string + /market/{marketTypeId}/history: + get: + tags: + - market + summary: "Find the market history of a type, most recent first" + operationId: findHistory + parameters: + - name: marketTypeId + in: path + description: Id of the market type + required: true + schema: + type: integer + format: int64 + - name: days + in: query + description: Optional number of most recent days to return; omit for the full + history + required: false + schema: + type: string + minimum: 1 + responses: + "200": + description: The market history of the type + content: + '*/*': + schema: + type: array + items: + $ref: "#/components/schemas/MarketHistoryResponse" + "400": + description: The days parameter is not greater than 0 + /market/scan: + get: + tags: + - market + summary: "Scan every tracked market type, returning volume-weighted price quartiles\ + \ for each" + operationId: scanMarket + parameters: + - name: days + in: query + description: Number of most recent days of history to analyse + required: false + schema: + type: string + default: "365" + minimum: 1 + - name: brokerFee + in: query + description: "Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both\ + \ buy and sell orders" + required: false + schema: + type: string + default: "0.015" + maximum: 1 + minimum: 0 + - name: salesTax + in: query + description: "Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell\ + \ orders" + required: false + schema: + type: string + default: "0.036" + maximum: 1 + minimum: 0 + responses: + "200": + description: "The scan results, one entry per tracked market type" + content: + '*/*': + schema: + type: array + items: + $ref: "#/components/schemas/MarketScanResponse" + "400": + description: The days parameter is not greater than 0 /ledgers: get: tags: @@ -429,6 +546,21 @@ paths: type: array items: $ref: "#/components/schemas/CharacterResponse" + /characters/rule-books: + get: + tags: + - character-rule-book + summary: Find the rule books of all characters that have a token + operationId: findAllCharacterRuleBooks + responses: + "200": + description: Rule book assignments of all characters with a token + content: + '*/*': + schema: + type: array + items: + $ref: "#/components/schemas/CharacterRuleBookResponse" /acquisitions: get: tags: @@ -560,15 +692,24 @@ components: required: - bindings - ruleBookId - CharacterRuleBookResponse: + CharacterResponse: type: object properties: characterId: type: integer format: int64 - ruleBookId: + name: type: string - format: uuid + required: + - characterId + - name + CharacterRuleBookResponse: + type: object + properties: + character: + $ref: "#/components/schemas/CharacterResponse" + ruleBook: + $ref: "#/components/schemas/RuleBookSummaryResponse" bindings: type: object additionalProperties: @@ -576,7 +717,18 @@ components: format: uuid required: - bindings - - characterId + - character + - ruleBook + RuleBookSummaryResponse: + type: object + properties: + ruleBookId: + type: string + format: uuid + name: + type: string + required: + - name - ruleBookId CreateRuleBookRequest: type: object @@ -617,6 +769,62 @@ components: required: - memberLedgerIds - name + MarketHistoryResponse: + type: object + properties: + marketTypeId: + type: integer + format: int64 + date: + type: string + format: date + average: + type: number + highest: + type: number + lowest: + type: number + orderCount: + type: integer + format: int64 + volume: + type: integer + format: int64 + required: + - average + - date + - highest + - lowest + - marketTypeId + - orderCount + - volume + MarketScanResponse: + type: object + properties: + marketTypeId: + type: integer + format: int64 + q1: + type: number + median: + type: number + q3: + type: number + totalVolume: + type: integer + format: int64 + profit: + type: number + score: + type: number + required: + - marketTypeId + - median + - profit + - q1 + - q3 + - score + - totalVolume LedgerResponse: discriminator: propertyName: type @@ -732,17 +940,6 @@ components: required: - quantity - typeId - CharacterResponse: - type: object - properties: - characterId: - type: integer - format: int64 - name: - type: string - required: - - characterId - - name AcquisitionResponse: type: object properties: diff --git a/src/generated/mammon/api.ts b/src/generated/mammon/api.ts index df4b63f..fee3b45 100644 --- a/src/generated/mammon/api.ts +++ b/src/generated/mammon/api.ts @@ -50,8 +50,8 @@ export interface CharacterResponse { 'name': string; } export interface CharacterRuleBookResponse { - 'characterId': number; - 'ruleBookId': string; + 'character': CharacterResponse; + 'ruleBook': RuleBookSummaryResponse; 'bindings': { [key: string]: string; }; } export interface CombinedLedgerResponse extends LedgerResponse { @@ -98,6 +98,24 @@ export interface MainLedgerResponse extends LedgerResponse { 'name': string; 'balance': number; } +export interface MarketHistoryResponse { + 'marketTypeId': number; + 'date': string; + 'average': number; + 'highest': number; + 'lowest': number; + 'orderCount': number; + 'volume': number; +} +export interface MarketScanResponse { + 'marketTypeId': number; + 'q1': number; + 'median': number; + 'q3': number; + 'totalVolume': number; + 'profit': number; + 'score': number; +} export interface RuleBookResponse { 'ruleBookId': string; 'name': string; @@ -105,6 +123,10 @@ export interface RuleBookResponse { 'ledgerRefs': Array; 'script': string; } +export interface RuleBookSummaryResponse { + 'ruleBookId': string; + 'name': string; +} export interface SetCharacterRuleBookRequest { 'ruleBookId': string; 'bindings': { [key: string]: string; }; @@ -488,6 +510,36 @@ export class CharacterApi extends BaseAPI { */ export const CharacterRuleBookApiAxiosParamCreator = function (configuration?: Configuration) { return { + /** + * + * @summary Find the rule books of all characters that have a token + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + findAllCharacterRuleBooks: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/characters/rule-books`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + localVarHeaderParameter['Accept'] = '*/*'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @summary Find the rule book assigned to a character @@ -570,6 +622,18 @@ export const CharacterRuleBookApiAxiosParamCreator = function (configuration?: C export const CharacterRuleBookApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CharacterRuleBookApiAxiosParamCreator(configuration) return { + /** + * + * @summary Find the rule books of all characters that have a token + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async findAllCharacterRuleBooks(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.findAllCharacterRuleBooks(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['CharacterRuleBookApi.findAllCharacterRuleBooks']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, /** * * @summary Find the rule book assigned to a character @@ -606,6 +670,15 @@ export const CharacterRuleBookApiFp = function(configuration?: Configuration) { export const CharacterRuleBookApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CharacterRuleBookApiFp(configuration) return { + /** + * + * @summary Find the rule books of all characters that have a token + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + findAllCharacterRuleBooks(options?: RawAxiosRequestConfig): AxiosPromise> { + return localVarFp.findAllCharacterRuleBooks(options).then((request) => request(axios, basePath)); + }, /** * * @summary Find the rule book assigned to a character @@ -634,6 +707,16 @@ export const CharacterRuleBookApiFactory = function (configuration?: Configurati * CharacterRuleBookApi - object-oriented interface */ export class CharacterRuleBookApi extends BaseAPI { + /** + * + * @summary Find the rule books of all characters that have a token + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public findAllCharacterRuleBooks(options?: RawAxiosRequestConfig) { + return CharacterRuleBookApiFp(this.configuration).findAllCharacterRuleBooks(options).then((request) => request(this.axios, this.basePath)); + } + /** * * @summary Find the rule book assigned to a character @@ -1180,6 +1263,200 @@ export class LedgerApi extends BaseAPI { +/** + * MarketApi - axios parameter creator + */ +export const MarketApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary Find the market history of a type, most recent first + * @param {number} marketTypeId Id of the market type + * @param {string} [days] Optional number of most recent days to return; omit for the full history + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + findHistory: async (marketTypeId: number, days?: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'marketTypeId' is not null or undefined + assertParamExists('findHistory', 'marketTypeId', marketTypeId) + const localVarPath = `/market/{marketTypeId}/history` + .replace('{marketTypeId}', encodeURIComponent(String(marketTypeId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + if (days !== undefined) { + localVarQueryParameter['days'] = days; + } + + localVarHeaderParameter['Accept'] = '*/*'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Scan every tracked market type, returning volume-weighted price quartiles for each + * @param {string} [days] Number of most recent days of history to analyse + * @param {string} [brokerFee] Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both buy and sell orders + * @param {string} [salesTax] Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell orders + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + scanMarket: async (days?: string, brokerFee?: string, salesTax?: string, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/market/scan`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + if (days !== undefined) { + localVarQueryParameter['days'] = days; + } + + if (brokerFee !== undefined) { + localVarQueryParameter['brokerFee'] = brokerFee; + } + + if (salesTax !== undefined) { + localVarQueryParameter['salesTax'] = salesTax; + } + + localVarHeaderParameter['Accept'] = '*/*'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * MarketApi - functional programming interface + */ +export const MarketApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = MarketApiAxiosParamCreator(configuration) + return { + /** + * + * @summary Find the market history of a type, most recent first + * @param {number} marketTypeId Id of the market type + * @param {string} [days] Optional number of most recent days to return; omit for the full history + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async findHistory(marketTypeId: number, days?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.findHistory(marketTypeId, days, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['MarketApi.findHistory']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary Scan every tracked market type, returning volume-weighted price quartiles for each + * @param {string} [days] Number of most recent days of history to analyse + * @param {string} [brokerFee] Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both buy and sell orders + * @param {string} [salesTax] Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell orders + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async scanMarket(days?: string, brokerFee?: string, salesTax?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.scanMarket(days, brokerFee, salesTax, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['MarketApi.scanMarket']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * MarketApi - factory interface + */ +export const MarketApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = MarketApiFp(configuration) + return { + /** + * + * @summary Find the market history of a type, most recent first + * @param {number} marketTypeId Id of the market type + * @param {string} [days] Optional number of most recent days to return; omit for the full history + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + findHistory(marketTypeId: number, days?: string, options?: RawAxiosRequestConfig): AxiosPromise> { + return localVarFp.findHistory(marketTypeId, days, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Scan every tracked market type, returning volume-weighted price quartiles for each + * @param {string} [days] Number of most recent days of history to analyse + * @param {string} [brokerFee] Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both buy and sell orders + * @param {string} [salesTax] Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell orders + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + scanMarket(days?: string, brokerFee?: string, salesTax?: string, options?: RawAxiosRequestConfig): AxiosPromise> { + return localVarFp.scanMarket(days, brokerFee, salesTax, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * MarketApi - object-oriented interface + */ +export class MarketApi extends BaseAPI { + /** + * + * @summary Find the market history of a type, most recent first + * @param {number} marketTypeId Id of the market type + * @param {string} [days] Optional number of most recent days to return; omit for the full history + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public findHistory(marketTypeId: number, days?: string, options?: RawAxiosRequestConfig) { + return MarketApiFp(this.configuration).findHistory(marketTypeId, days, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Scan every tracked market type, returning volume-weighted price quartiles for each + * @param {string} [days] Number of most recent days of history to analyse + * @param {string} [brokerFee] Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both buy and sell orders + * @param {string} [salesTax] Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell orders + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public scanMarket(days?: string, brokerFee?: string, salesTax?: string, options?: RawAxiosRequestConfig) { + return MarketApiFp(this.configuration).scanMarket(days, brokerFee, salesTax, options).then((request) => request(this.axios, this.basePath)); + } +} + + + /** * ProcessingApi - axios parameter creator */ diff --git a/src/pages/rules/EditCharacterRuleBook.vue b/src/pages/rules/EditCharacterRuleBook.vue index 13c8524..8612e1a 100644 --- a/src/pages/rules/EditCharacterRuleBook.vue +++ b/src/pages/rules/EditCharacterRuleBook.vue @@ -4,9 +4,8 @@ import {useRoute} from "vue-router"; import {computed, ref, watch, watchEffect} from "vue"; import log from "loglevel"; import { - findCharacterRuleBookByCharacterId, RuleBook, - setCharacterRuleBookForCharacter, + useCharacterRuleBooksStore, useRuleBooksStore } from "@/rules"; import {storeToRefs} from "pinia"; @@ -17,6 +16,7 @@ type Bindings = { [key: string]: Ledger; }; const ruleBookStore = useRuleBooksStore(); const {findById: findRuleBookById} = ruleBookStore; const {ruleBooks} = storeToRefs(ruleBookStore); +const {findByCharacterId, setForCharacter} = useCharacterRuleBooksStore(); const {findById: findCharacterById} = useCharactersStore(); const {ledgers} = storeToRefs(useLedgersStore()); @@ -31,11 +31,11 @@ watchEffect(async () => { const characterId = character.value?.characterId; if (characterId) { - const characterRuleBook = await findCharacterRuleBookByCharacterId(characterId); + const characterRuleBook = findByCharacterId(characterId); - ruleBook.value = findRuleBookById(characterRuleBook.ruleBookId); + ruleBook.value = findRuleBookById(characterRuleBook?.ruleBook.ruleBookId ?? ''); bindings.value = Object.fromEntries( - Object.entries(characterRuleBook.bindings) + Object.entries(characterRuleBook?.bindings ?? {}) .map(([key, id]) => [key, ledgersToUse.value.find(l => l.ledgerId === id) ?? systemLedger]) ); } @@ -46,7 +46,7 @@ const save = () => { const ruleBookId = ruleBook.value?.ruleBookId; if (characterId && ruleBookId) { - setCharacterRuleBookForCharacter(characterId, { + setForCharacter(characterId, { ruleBookId, bindings: Object.fromEntries( Object.entries(bindings.value) diff --git a/src/pages/rules/ListCharacterRuleBooks.vue b/src/pages/rules/ListCharacterRuleBooks.vue index 2004e19..0a0d3fe 100644 --- a/src/pages/rules/ListCharacterRuleBooks.vue +++ b/src/pages/rules/ListCharacterRuleBooks.vue @@ -1,34 +1,23 @@