feat(pong): added history api to get list of games

This commit is contained in:
Maieul BOYER 2026-01-06 16:23:23 +01:00 committed by Nigel
parent 321f636672
commit 40dea32048
14 changed files with 1089 additions and 74 deletions

View file

@ -47,6 +47,11 @@ models/LoginOtp500Response.ts
models/LoginOtpRequest.ts
models/LoginRequest.ts
models/Logout200Response.ts
models/PongHistory200Response.ts
models/PongHistory200ResponsePayload.ts
models/PongHistory200ResponsePayloadDataInner.ts
models/PongHistory200ResponsePayloadDataInnerLeft.ts
models/PongHistory404Response.ts
models/ProviderList200Response.ts
models/ProviderList200ResponsePayload.ts
models/ProviderList200ResponsePayloadListInner.ts

View file

@ -54,6 +54,8 @@ import type {
LoginOtpRequest,
LoginRequest,
Logout200Response,
PongHistory200Response,
PongHistory404Response,
ProviderList200Response,
Signin200Response,
Signin400Response,
@ -141,6 +143,10 @@ import {
LoginRequestToJSON,
Logout200ResponseFromJSON,
Logout200ResponseToJSON,
PongHistory200ResponseFromJSON,
PongHistory200ResponseToJSON,
PongHistory404ResponseFromJSON,
PongHistory404ResponseToJSON,
ProviderList200ResponseFromJSON,
ProviderList200ResponseToJSON,
Signin200ResponseFromJSON,
@ -185,6 +191,10 @@ export interface LoginOtpOperationRequest {
loginOtpRequest: LoginOtpRequest;
}
export interface PongHistoryRequest {
user: string;
}
export interface SigninRequest {
loginRequest: LoginRequest;
}
@ -823,6 +833,60 @@ export class OpenapiOtherApi extends runtime.BaseAPI {
return await response.value();
}
/**
*/
async pongHistoryRaw(requestParameters: PongHistoryRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PongHistory200Response | StatusOtp401Response | PongHistory404Response>> {
if (requestParameters['user'] == null) {
throw new runtime.RequiredError(
'user',
'Required parameter "user" was null or undefined when calling pongHistory().'
);
}
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
let urlPath = `/api/pong/history/{user}`;
urlPath = urlPath.replace(`{${"user"}}`, encodeURIComponent(String(requestParameters['user'])));
const response = await this.request({
path: urlPath,
method: 'GET',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
// CHANGED: Handle all status codes defined in the OpenAPI spec, not just 2xx responses
// This allows typed access to error responses (4xx, 5xx) and other status codes.
// The code routes responses based on the actual HTTP status code and returns
// appropriately typed ApiResponse wrappers for each status code.
if (response.status === 200) {
// Object response for status 200
return new runtime.JSONApiResponse(response, (jsonValue) => PongHistory200ResponseFromJSON(jsonValue));
}
if (response.status === 401) {
// Object response for status 401
return new runtime.JSONApiResponse(response, (jsonValue) => StatusOtp401ResponseFromJSON(jsonValue));
}
if (response.status === 404) {
// Object response for status 404
return new runtime.JSONApiResponse(response, (jsonValue) => PongHistory404ResponseFromJSON(jsonValue));
}
// CHANGED: Throw error if status code is not handled by any of the defined responses
// This ensures all code paths return a value and provides clear error messages for unexpected status codes
// Only throw if responses were defined but none matched the actual status code
throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 401, 404`);
}
/**
*/
async pongHistory(requestParameters: PongHistoryRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<PongHistory200Response | StatusOtp401Response | PongHistory404Response> {
const response = await this.pongHistoryRaw(requestParameters, initOverrides);
return await response.value();
}
/**
*/
async providerListRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ProviderList200Response>> {

View file

@ -0,0 +1,110 @@
/* tslint:disable */
/* eslint-disable */
/**
* @fastify/swagger
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 9.6.1
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { PongHistory200ResponsePayload } from './PongHistory200ResponsePayload';
import {
PongHistory200ResponsePayloadFromJSON,
PongHistory200ResponsePayloadFromJSONTyped,
PongHistory200ResponsePayloadToJSON,
PongHistory200ResponsePayloadToJSONTyped,
} from './PongHistory200ResponsePayload';
/**
*
* @export
* @interface PongHistory200Response
*/
export interface PongHistory200Response {
/**
*
* @type {string}
* @memberof PongHistory200Response
*/
kind: PongHistory200ResponseKindEnum;
/**
*
* @type {string}
* @memberof PongHistory200Response
*/
msg: PongHistory200ResponseMsgEnum;
/**
*
* @type {PongHistory200ResponsePayload}
* @memberof PongHistory200Response
*/
payload: PongHistory200ResponsePayload;
}
/**
* @export
*/
export const PongHistory200ResponseKindEnum = {
Success: 'success'
} as const;
export type PongHistory200ResponseKindEnum = typeof PongHistory200ResponseKindEnum[keyof typeof PongHistory200ResponseKindEnum];
/**
* @export
*/
export const PongHistory200ResponseMsgEnum = {
PonghistorySuccess: 'ponghistory.success'
} as const;
export type PongHistory200ResponseMsgEnum = typeof PongHistory200ResponseMsgEnum[keyof typeof PongHistory200ResponseMsgEnum];
/**
* Check if a given object implements the PongHistory200Response interface.
*/
export function instanceOfPongHistory200Response(value: object): value is PongHistory200Response {
if (!('kind' in value) || value['kind'] === undefined) return false;
if (!('msg' in value) || value['msg'] === undefined) return false;
if (!('payload' in value) || value['payload'] === undefined) return false;
return true;
}
export function PongHistory200ResponseFromJSON(json: any): PongHistory200Response {
return PongHistory200ResponseFromJSONTyped(json, false);
}
export function PongHistory200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): PongHistory200Response {
if (json == null) {
return json;
}
return {
'kind': json['kind'],
'msg': json['msg'],
'payload': PongHistory200ResponsePayloadFromJSON(json['payload']),
};
}
export function PongHistory200ResponseToJSON(json: any): PongHistory200Response {
return PongHistory200ResponseToJSONTyped(json, false);
}
export function PongHistory200ResponseToJSONTyped(value?: PongHistory200Response | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'kind': value['kind'],
'msg': value['msg'],
'payload': PongHistory200ResponsePayloadToJSON(value['payload']),
};
}

View file

@ -0,0 +1,74 @@
/* tslint:disable */
/* eslint-disable */
/**
* @fastify/swagger
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 9.6.1
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { PongHistory200ResponsePayloadDataInner } from './PongHistory200ResponsePayloadDataInner';
import {
PongHistory200ResponsePayloadDataInnerFromJSON,
PongHistory200ResponsePayloadDataInnerFromJSONTyped,
PongHistory200ResponsePayloadDataInnerToJSON,
PongHistory200ResponsePayloadDataInnerToJSONTyped,
} from './PongHistory200ResponsePayloadDataInner';
/**
*
* @export
* @interface PongHistory200ResponsePayload
*/
export interface PongHistory200ResponsePayload {
/**
*
* @type {Array<PongHistory200ResponsePayloadDataInner>}
* @memberof PongHistory200ResponsePayload
*/
data: Array<PongHistory200ResponsePayloadDataInner>;
}
/**
* Check if a given object implements the PongHistory200ResponsePayload interface.
*/
export function instanceOfPongHistory200ResponsePayload(value: object): value is PongHistory200ResponsePayload {
if (!('data' in value) || value['data'] === undefined) return false;
return true;
}
export function PongHistory200ResponsePayloadFromJSON(json: any): PongHistory200ResponsePayload {
return PongHistory200ResponsePayloadFromJSONTyped(json, false);
}
export function PongHistory200ResponsePayloadFromJSONTyped(json: any, ignoreDiscriminator: boolean): PongHistory200ResponsePayload {
if (json == null) {
return json;
}
return {
'data': ((json['data'] as Array<any>).map(PongHistory200ResponsePayloadDataInnerFromJSON)),
};
}
export function PongHistory200ResponsePayloadToJSON(json: any): PongHistory200ResponsePayload {
return PongHistory200ResponsePayloadToJSONTyped(json, false);
}
export function PongHistory200ResponsePayloadToJSONTyped(value?: PongHistory200ResponsePayload | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'data': ((value['data'] as Array<any>).map(PongHistory200ResponsePayloadDataInnerToJSON)),
};
}

View file

@ -0,0 +1,131 @@
/* tslint:disable */
/* eslint-disable */
/**
* @fastify/swagger
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 9.6.1
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { PongHistory200ResponsePayloadDataInnerLeft } from './PongHistory200ResponsePayloadDataInnerLeft';
import {
PongHistory200ResponsePayloadDataInnerLeftFromJSON,
PongHistory200ResponsePayloadDataInnerLeftFromJSONTyped,
PongHistory200ResponsePayloadDataInnerLeftToJSON,
PongHistory200ResponsePayloadDataInnerLeftToJSONTyped,
} from './PongHistory200ResponsePayloadDataInnerLeft';
/**
*
* @export
* @interface PongHistory200ResponsePayloadDataInner
*/
export interface PongHistory200ResponsePayloadDataInner {
/**
* gameId
* @type {string}
* @memberof PongHistory200ResponsePayloadDataInner
*/
gameId: string;
/**
*
* @type {PongHistory200ResponsePayloadDataInnerLeft}
* @memberof PongHistory200ResponsePayloadDataInner
*/
left: PongHistory200ResponsePayloadDataInnerLeft;
/**
*
* @type {PongHistory200ResponsePayloadDataInnerLeft}
* @memberof PongHistory200ResponsePayloadDataInner
*/
right: PongHistory200ResponsePayloadDataInnerLeft;
/**
*
* @type {boolean}
* @memberof PongHistory200ResponsePayloadDataInner
*/
local: boolean;
/**
*
* @type {string}
* @memberof PongHistory200ResponsePayloadDataInner
*/
date: string;
/**
*
* @type {string}
* @memberof PongHistory200ResponsePayloadDataInner
*/
outcome: PongHistory200ResponsePayloadDataInnerOutcomeEnum;
}
/**
* @export
*/
export const PongHistory200ResponsePayloadDataInnerOutcomeEnum = {
WinL: 'winL',
WinR: 'winR',
Other: 'other'
} as const;
export type PongHistory200ResponsePayloadDataInnerOutcomeEnum = typeof PongHistory200ResponsePayloadDataInnerOutcomeEnum[keyof typeof PongHistory200ResponsePayloadDataInnerOutcomeEnum];
/**
* Check if a given object implements the PongHistory200ResponsePayloadDataInner interface.
*/
export function instanceOfPongHistory200ResponsePayloadDataInner(value: object): value is PongHistory200ResponsePayloadDataInner {
if (!('gameId' in value) || value['gameId'] === undefined) return false;
if (!('left' in value) || value['left'] === undefined) return false;
if (!('right' in value) || value['right'] === undefined) return false;
if (!('local' in value) || value['local'] === undefined) return false;
if (!('date' in value) || value['date'] === undefined) return false;
if (!('outcome' in value) || value['outcome'] === undefined) return false;
return true;
}
export function PongHistory200ResponsePayloadDataInnerFromJSON(json: any): PongHistory200ResponsePayloadDataInner {
return PongHistory200ResponsePayloadDataInnerFromJSONTyped(json, false);
}
export function PongHistory200ResponsePayloadDataInnerFromJSONTyped(json: any, ignoreDiscriminator: boolean): PongHistory200ResponsePayloadDataInner {
if (json == null) {
return json;
}
return {
'gameId': json['gameId'],
'left': PongHistory200ResponsePayloadDataInnerLeftFromJSON(json['left']),
'right': PongHistory200ResponsePayloadDataInnerLeftFromJSON(json['right']),
'local': json['local'],
'date': json['date'],
'outcome': json['outcome'],
};
}
export function PongHistory200ResponsePayloadDataInnerToJSON(json: any): PongHistory200ResponsePayloadDataInner {
return PongHistory200ResponsePayloadDataInnerToJSONTyped(json, false);
}
export function PongHistory200ResponsePayloadDataInnerToJSONTyped(value?: PongHistory200ResponsePayloadDataInner | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'gameId': value['gameId'],
'left': PongHistory200ResponsePayloadDataInnerLeftToJSON(value['left']),
'right': PongHistory200ResponsePayloadDataInnerLeftToJSON(value['right']),
'local': value['local'],
'date': value['date'],
'outcome': value['outcome'],
};
}

View file

@ -0,0 +1,84 @@
/* tslint:disable */
/* eslint-disable */
/**
* @fastify/swagger
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 9.6.1
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface PongHistory200ResponsePayloadDataInnerLeft
*/
export interface PongHistory200ResponsePayloadDataInnerLeft {
/**
*
* @type {number}
* @memberof PongHistory200ResponsePayloadDataInnerLeft
*/
score: number;
/**
*
* @type {string}
* @memberof PongHistory200ResponsePayloadDataInnerLeft
*/
id: string;
/**
*
* @type {string}
* @memberof PongHistory200ResponsePayloadDataInnerLeft
*/
name: string;
}
/**
* Check if a given object implements the PongHistory200ResponsePayloadDataInnerLeft interface.
*/
export function instanceOfPongHistory200ResponsePayloadDataInnerLeft(value: object): value is PongHistory200ResponsePayloadDataInnerLeft {
if (!('score' in value) || value['score'] === undefined) return false;
if (!('id' in value) || value['id'] === undefined) return false;
if (!('name' in value) || value['name'] === undefined) return false;
return true;
}
export function PongHistory200ResponsePayloadDataInnerLeftFromJSON(json: any): PongHistory200ResponsePayloadDataInnerLeft {
return PongHistory200ResponsePayloadDataInnerLeftFromJSONTyped(json, false);
}
export function PongHistory200ResponsePayloadDataInnerLeftFromJSONTyped(json: any, ignoreDiscriminator: boolean): PongHistory200ResponsePayloadDataInnerLeft {
if (json == null) {
return json;
}
return {
'score': json['score'],
'id': json['id'],
'name': json['name'],
};
}
export function PongHistory200ResponsePayloadDataInnerLeftToJSON(json: any): PongHistory200ResponsePayloadDataInnerLeft {
return PongHistory200ResponsePayloadDataInnerLeftToJSONTyped(json, false);
}
export function PongHistory200ResponsePayloadDataInnerLeftToJSONTyped(value?: PongHistory200ResponsePayloadDataInnerLeft | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'score': value['score'],
'id': value['id'],
'name': value['name'],
};
}

View file

@ -0,0 +1,93 @@
/* tslint:disable */
/* eslint-disable */
/**
* @fastify/swagger
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 9.6.1
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface PongHistory404Response
*/
export interface PongHistory404Response {
/**
*
* @type {string}
* @memberof PongHistory404Response
*/
kind: PongHistory404ResponseKindEnum;
/**
*
* @type {string}
* @memberof PongHistory404Response
*/
msg: PongHistory404ResponseMsgEnum;
}
/**
* @export
*/
export const PongHistory404ResponseKindEnum = {
Failure: 'failure'
} as const;
export type PongHistory404ResponseKindEnum = typeof PongHistory404ResponseKindEnum[keyof typeof PongHistory404ResponseKindEnum];
/**
* @export
*/
export const PongHistory404ResponseMsgEnum = {
PonghistoryFailureNotfound: 'ponghistory.failure.notfound'
} as const;
export type PongHistory404ResponseMsgEnum = typeof PongHistory404ResponseMsgEnum[keyof typeof PongHistory404ResponseMsgEnum];
/**
* Check if a given object implements the PongHistory404Response interface.
*/
export function instanceOfPongHistory404Response(value: object): value is PongHistory404Response {
if (!('kind' in value) || value['kind'] === undefined) return false;
if (!('msg' in value) || value['msg'] === undefined) return false;
return true;
}
export function PongHistory404ResponseFromJSON(json: any): PongHistory404Response {
return PongHistory404ResponseFromJSONTyped(json, false);
}
export function PongHistory404ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): PongHistory404Response {
if (json == null) {
return json;
}
return {
'kind': json['kind'],
'msg': json['msg'],
};
}
export function PongHistory404ResponseToJSON(json: any): PongHistory404Response {
return PongHistory404ResponseToJSONTyped(json, false);
}
export function PongHistory404ResponseToJSONTyped(value?: PongHistory404Response | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'kind': value['kind'],
'msg': value['msg'],
};
}

View file

@ -46,6 +46,11 @@ export * from './LoginOtp500Response';
export * from './LoginOtpRequest';
export * from './LoginRequest';
export * from './Logout200Response';
export * from './PongHistory200Response';
export * from './PongHistory200ResponsePayload';
export * from './PongHistory200ResponsePayloadDataInner';
export * from './PongHistory200ResponsePayloadDataInnerLeft';
export * from './PongHistory404Response';
export * from './ProviderList200Response';
export * from './ProviderList200ResponsePayload';
export * from './ProviderList200ResponsePayloadListInner';

View file

@ -19,6 +19,11 @@ export interface IPongDb extends Database {
this: IPongDb,
id: PongGameId,
): PongGame | undefined,
getAllPongGameForUser(
this: IPongDb,
id: UserId,
): (PongGame & { nameL: string, nameR: string })[],
}
export const PongImpl: Omit<IPongDb, keyof Database> = {
@ -49,6 +54,35 @@ export const PongImpl: Omit<IPongDb, keyof Database> = {
const q = this.prepare('SELECT * FROM pong WHERE id = @id').get({ id }) as Partial<PongGameTable> | undefined;
return pongGameFromRow(q);
},
getAllPongGameForUser(
this: IPongDb,
id: UserId,
): (PongGame & { nameL: string, nameR: string })[] {
const q = this.prepare(`
SELECT
pong.*,
userL.name AS nameL,
userR.name AS nameR
FROM pong
INNER JOIN user AS userL
ON pong.playerL = userL.id
INNER JOIN user AS userR
ON pong.playerR = userR.id
WHERE
pong.playerL = @id
OR pong.playerR = @id;
`);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return q.all({ id }).map((s: any) => {
const g: (PongGame & { nameL?: string, nameR?: string }) | undefined = pongGameFromRow(s);
if (isNullish(g)) return undefined;
g.nameL = s.nameL;
g.nameR = s.nameR;
if (isNullish(g.nameL) || isNullish(g.nameR)) return undefined;
return g as PongGame & { nameL: string, nameR: string };
}).filter(v => !isNullish(v));
},
};
export type PongGameId = UUID & { readonly __uuid: unique symbol };
@ -59,6 +93,7 @@ export type PongGame = {
readonly right: { id: UserId, score: number };
readonly outcome: PongGameOutcome;
readonly time: Date;
readonly local: boolean;
};
// this is an internal type, never to be seen outside
@ -70,6 +105,7 @@ type PongGameTable = {
scoreR: number,
outcome: PongGameOutcome,
time: string,
local: number,
};
function pongGameFromRow(r: Partial<PongGameTable> | undefined): PongGame | undefined {
@ -81,6 +117,7 @@ function pongGameFromRow(r: Partial<PongGameTable> | undefined): PongGame | unde
if (isNullish(r.scoreR)) return undefined;
if (isNullish(r.outcome)) return undefined;
if (isNullish(r.time)) return undefined;
if (isNullish(r.local)) return undefined;
if (r.outcome !== 'winR' && r.outcome !== 'winL' && r.outcome !== 'other') return undefined;
const date = Date.parse(r.time);
@ -93,18 +130,6 @@ function pongGameFromRow(r: Partial<PongGameTable> | undefined): PongGame | unde
right: { id: r.playerR, score: r.scoreR },
outcome: r.outcome,
time: new Date(date),
local: r.local !== 0,
};
}
// this function will be able to be called from everywhere
// export async function freeFloatingExportedFunction(): Promise<boolean> {
// return false;
// }
// this function will never be able to be called outside of this module
// async function privateFunction(): Promise<string | undefined> {
// return undefined;
// }
// silence warnings
// void privateFunction;

View file

@ -1916,6 +1916,214 @@
"openapi_other"
]
}
},
"/api/pong/history/{user}": {
"get": {
"operationId": "pongHistory",
"parameters": [
{
"schema": {
"type": "string"
},
"in": "path",
"name": "user",
"required": true,
"description": "'me' | <userid>"
}
],
"responses": {
"200": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg",
"payload"
],
"properties": {
"kind": {
"enum": [
"success"
]
},
"msg": {
"enum": [
"ponghistory.success"
]
},
"payload": {
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"required": [
"gameId",
"left",
"right",
"local",
"date",
"outcome"
],
"properties": {
"gameId": {
"type": "string",
"description": "gameId"
},
"left": {
"type": "object",
"required": [
"score",
"id",
"name"
],
"properties": {
"score": {
"type": "integer"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"right": {
"type": "object",
"required": [
"score",
"id",
"name"
],
"properties": {
"score": {
"type": "integer"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"local": {
"type": "boolean"
},
"date": {
"type": "string"
},
"outcome": {
"enum": [
"winL",
"winR",
"other"
]
}
}
}
}
}
}
}
}
}
}
},
"401": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"notLoggedIn"
]
},
"msg": {
"enum": [
"auth.noCookie",
"auth.invalidKind",
"auth.noUser",
"auth.invalid"
]
}
}
},
{
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"notLoggedIn"
]
},
"msg": {
"enum": [
"auth.noCookie",
"auth.invalidKind",
"auth.noUser",
"auth.invalid"
]
}
}
}
]
}
}
}
},
"404": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"failure"
]
},
"msg": {
"enum": [
"ponghistory.failure.notfound"
]
}
}
}
}
}
}
},
"tags": [
"openapi_other"
]
}
}
},
"components": {

View file

@ -7,7 +7,213 @@
"components": {
"schemas": {}
},
"paths": {},
"paths": {
"/api/pong/history/{user}": {
"get": {
"operationId": "pongHistory",
"parameters": [
{
"schema": {
"type": "string"
},
"in": "path",
"name": "user",
"required": true,
"description": "'me' | <userid>"
}
],
"responses": {
"200": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg",
"payload"
],
"properties": {
"kind": {
"enum": [
"success"
]
},
"msg": {
"enum": [
"ponghistory.success"
]
},
"payload": {
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"required": [
"gameId",
"left",
"right",
"local",
"date",
"outcome"
],
"properties": {
"gameId": {
"type": "string",
"description": "gameId"
},
"left": {
"type": "object",
"required": [
"score",
"id",
"name"
],
"properties": {
"score": {
"type": "integer"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"right": {
"type": "object",
"required": [
"score",
"id",
"name"
],
"properties": {
"score": {
"type": "integer"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"local": {
"type": "boolean"
},
"date": {
"type": "string"
},
"outcome": {
"enum": [
"winL",
"winR",
"other"
]
}
}
}
}
}
}
}
}
}
}
},
"401": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"notLoggedIn"
]
},
"msg": {
"enum": [
"auth.noCookie",
"auth.invalidKind",
"auth.noUser",
"auth.invalid"
]
}
}
},
{
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"notLoggedIn"
]
},
"msg": {
"enum": [
"auth.noCookie",
"auth.invalidKind",
"auth.noUser",
"auth.invalid"
]
}
}
}
]
}
}
}
},
"404": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"kind",
"msg"
],
"properties": {
"kind": {
"enum": [
"failure"
]
},
"msg": {
"enum": [
"ponghistory.failure.notfound"
]
}
}
}
}
}
}
}
}
}
},
"servers": [
{
"url": "https://local.maix.me:8888",

View file

@ -1,60 +0,0 @@
import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from 'typebox';
export const PongReq = Type.Object({
message: Type.String(),
});
export type PongReq = Static<typeof PongReq>;
const route: FastifyPluginAsync = async (fastify): Promise<void> => {
fastify.post<{ Body: PongReq }>(
'/api/pong/broadcast',
{
schema: {
body: PongReq,
hide: true,
},
config: { requireAuth: false },
},
async function(req, res) {
void res;
},
);
};
export default route;
/**
*
* try this in a terminal
*
* curl -k --data-raw '{"message": "Message SENT from the terminal en REMOTE"}' 'https://local.maix.me:8888/api/pong/broadcast' -H "Content-Type: application/json"
*
* send message info to the fronatend via the route '/api/pong/broadcast'
*/
// const route: FastifyPluginAsync = async (fastify): Promise<void> => {
// fastify.post('/api/chat/broadcast', {
// schema: {
// body: {
// type: 'object',
// required: ['nextGame'],
// properties: {
// nextGame: { type: 'string' }
// }
// }
// }
// }, async (req, reply) => {
// // Body only contains nextGame now
// const gameLink: Promise<string> = Promise.resolve(req.body as string );
// // Broadcast nextGame
// if (gameLink)
// broadcastNextGame(fastify, gameLink);
// return reply.send({ status: 'ok' });
// });
// };
// export default route;

View file

@ -0,0 +1,68 @@
import { UserId } from '@shared/database/mixin/user';
import { isNullish, MakeStaticResponse, typeResponse } from '@shared/utils';
import { FastifyPluginAsync } from 'fastify';
import { Static, Type } from 'typebox';
const PongHistoryParams = Type.Object({
user: Type.String({ description: '\'me\' | <userid>' }),
});
type PongHistoryParams = Static<typeof PongHistoryParams>;
const PongHistoryResponse = {
'200': typeResponse('success', 'ponghistory.success', {
data: Type.Array(
Type.Object({
gameId: Type.String({ description: 'gameId' }),
left: Type.Object({
score: Type.Integer(),
id: Type.String(),
name: Type.String(),
}),
right: Type.Object({
score: Type.Integer(),
id: Type.String(),
name: Type.String(),
}),
local: Type.Boolean(),
date: Type.String(),
outcome: Type.Enum(['winL', 'winR', 'other']),
}),
),
}),
'404': typeResponse('failure', 'ponghistory.failure.notfound'),
};
type PongHistoryResponse = MakeStaticResponse<typeof PongHistoryResponse>;
const route: FastifyPluginAsync = async (fastify): Promise<void> => {
fastify.get<{ Params: PongHistoryParams }>(
'/api/pong/history/:user',
{
schema: {
params: PongHistoryParams,
response: PongHistoryResponse,
operationId: 'pongHistory',
},
config: { requireAuth: true },
},
async function(req, res) {
if (req.params.user === 'me') { req.params.user = req.authUser!.id; }
const user = this.db.getUser(req.params.user);
if (isNullish(user)) { return res.makeResponse(404, 'failure', 'ponghistory.failure.notfound'); }
const data = this.db.getAllPongGameForUser(req.params.user as UserId);
if (isNullish(data)) { return res.makeResponse(404, 'failure', 'ponghistory.failure.notfound'); }
return res.makeResponse(200, 'success', 'ponghistory.success', {
data: data.map(v => ({
gameId: v.id,
left: { score: v.left.score, id: v.left.id, name: v.nameL },
right: { score: v.right.score, id: v.right.id, name: v.nameR },
local: v.local,
date: v.time.toString(),
outcome: v.outcome,
})),
});
},
);
};
export default route;

View file

@ -7,6 +7,8 @@ apis:
root: ./chat/openapi.json
ttt:
root: ./tic-tac-toe/openapi.json
pong:
root: ./pong/openapi.json
rules:
info-license: warn