Add swagger doc for the link pusher -> admin (#2146)

* Dissociate admin API from external API
* Add adminAPI doc for self-hosted
This commit is contained in:
César Cardinale
2022-05-11 17:43:25 +02:00
committed by GitHub
parent 253ae60478
commit 5016c85ca4
9 changed files with 350 additions and 21 deletions
@@ -7,6 +7,8 @@ import { openIDClient } from "../Services/OpenIDClient";
import { DISABLE_ANONYMOUS } from "../Enum/EnvironmentVariable";
import { RegisterData } from "../Messages/JsonMessages/RegisterData";
import { adminService } from "../Services/AdminService";
import Axios from "axios";
import { isErrorApiData } from "../Messages/JsonMessages/ErrorApiData";
export interface TokenInterface {
userUuid: string;
@@ -197,6 +199,13 @@ export class AuthenticateController extends BaseHttpController {
locale: authTokenData?.locale,
});
} catch (err) {
if (Axios.isAxiosError(err)) {
const errorType = isErrorApiData.safeParse(err?.response?.data);
if (errorType.success) {
res.sendStatus(err?.response?.status ?? 500);
return res.json(errorType.data);
}
}
console.info("User was not connected", err);
}
}
+158 -4
View File
@@ -52,13 +52,166 @@ export class SwaggerController extends BaseHttpController {
in: "header",
},
},
...SwaggerGenerator.definitions(),
...SwaggerGenerator.definitions(null),
},
apis: ["./src/Services/*.ts"],
};
res.json(swaggerJsdoc(options));
});
this.app.get("/openapi/external-admin", (req, res) => {
// Let's load the module dynamically (it may not exist in prod because part of the -dev packages)
const options = {
swagger: "2.0",
//openapi: "3.0.0",
info: {
title: "WorkAdventure External Admin",
version: "1.0.0",
description:
"This is a documentation about the external endpoints called by the pusher (aka the Admin API). \n Those endpoints should be implemented by the Admin API. The pusher will access those endpoints (just like webhooks). You can find out more about WorkAdventure and the Admin API on [GitHub](https://github.com/thecodingmachine/workadventure/blob/develop/docs/dev/adminAPI.md).",
contact: {
email: "hello@workadventu.re",
},
},
tags: [
{
name: "ExternalAdminAPI",
description: "Access to end points of the external admin from the pusher",
},
],
securityDefinitions: {
Header: {
type: "apiKey",
name: "Authorization",
in: "header",
},
},
...SwaggerGenerator.definitions("external"),
paths: {
"/api/mapinformation": {
get: {
security: [
{
Header: [],
},
],
tags: ["ExternalAdminAPI"],
parameters: [
{
name: "playUri",
in: "query",
description: "The full URL of WorkAdventure",
required: true,
type: "string",
example: "http://example.com/@/teamSlug/worldSLug/roomSlug",
},
],
responses: {
200: {
description: "The details of the map",
schema: {
$ref: "#/definitions/MapDetailsData",
},
},
401: {
description: "Error while retrieving the data because you are not authorized",
schema: {
$ref: "#/definitions/ErrorApiUnauthorizedData",
},
},
},
},
},
"/api/roomaccess": {
get: {
security: [
{
Header: [],
},
],
tags: ["ExternalAdminAPI"],
parameters: [
{
name: "playUri",
in: "query",
description: "The full URL of WorkAdventure",
required: true,
type: "string",
example: "http://example.com/@/teamSlug/worldSLug/roomSlug",
},
{
name: "ipAddress",
in: "query",
description:
"IP Address of the user logged in, allows you to check whether a user has been banned or not",
required: true,
type: "string",
example: "127.0.0.1",
},
{
name: "userIdentifier",
in: "query",
description:
"The identifier of the current user \n It can be null or an uuid or an email",
type: "string",
example: "998ce839-3dea-4698-8b41-ebbdf7688ad9",
},
],
responses: {
200: {
description: "The details of the member if he can access this room",
schema: {
$ref: "#/definitions/FetchMemberDataByUuidResponse",
},
},
401: {
description: "Error while retrieving the data because you are not authorized",
schema: {
$ref: "#/definitions/ErrorApiUnauthorizedData",
},
},
},
},
},
"/api/loginurl/{organizationMemberToken}": {
get: {
security: [
{
Header: [],
},
],
description: "Returns a member from the token",
tags: ["ExternalAdminAPI"],
parameters: [
{
name: "organizationMemberToken",
in: "path",
description: "The token of member in the organization",
required: true,
type: "string",
},
],
responses: {
200: {
description: "The details of the member",
schema: {
$ref: "#/definitions/AdminApiData",
},
},
401: {
description: "Error while retrieving the data because you are not authorized",
schema: {
$ref: "#/definitions/ErrorApiUnauthorizedData",
},
},
},
},
},
},
};
res.json(options);
});
// Create a LiveDirectory instance to virtualize directory with our assets
// @ts-ignore
const LiveDirectory = require("live-directory");
@@ -80,13 +233,14 @@ export class SwaggerController extends BaseHttpController {
}
const urls = [
{ url: "/openapi/pusher", name: "Front -> Pusher" },
{ url: "/openapi/admin", name: "Pusher <- Admin" },
{ url: "/openapi/pusher", name: "Front <- Pusher" },
{ url: "/openapi/admin", name: "Pusher -> Admin" },
{ url: "/openapi/external-admin", name: "Admin -> External Admin" },
];
const result = data.replace(
/url: "https:\/\/petstore\.swagger\.io\/v2\/swagger.json"/g,
`urls: ${JSON.stringify(urls)}, "urls.primaryName": "Pusher <- Admin"`
`urls: ${JSON.stringify(urls)}, "urls.primaryName": "Admin -> External Admin"`
);
response.send(result);
+12 -5
View File
@@ -34,11 +34,17 @@ export const isFetchMemberDataByUuidResponse = z.object({
description: "URL of the visitCard of the user fetched.",
example: "https://mycompany.com/contact/me",
}),
textures: extendApi(z.array(isWokaDetail), { $ref: "#/definitions/WokaDetail" }),
messages: extendApi(z.array(z.unknown()), { description: "List of user's messages." }),
textures: extendApi(z.array(isWokaDetail), {
description: "This data represents the textures (WOKA) that will be available to users.",
$ref: "#/definitions/WokaDetail",
}),
messages: extendApi(z.array(z.unknown()), {
description:
"Sets messages that will be displayed when the user logs in to the WA room. These messages are used for ban or ban warning.",
}),
anonymous: extendApi(z.optional(z.boolean()), {
description: "Whether the user if logged as anonymous or not",
description: "Defines whether it is possible to login as anonymous on a WorkAdventure room.",
example: false,
}),
userRoomToken: extendApi(z.optional(z.string()), { description: "", example: "" }),
@@ -110,7 +116,7 @@ class AdminApi implements AdminInterface {
* example: "998ce839-3dea-4698-8b41-ebbdf7688ad9"
* responses:
* 200:
* description: The details of the member
* description: The details of the map
* schema:
* $ref: "#/definitions/MapDetailsData"
* 401:
@@ -162,7 +168,7 @@ class AdminApi implements AdminInterface {
* /api/room/access:
* get:
* tags: ["AdminAPI"]
* description: Returns member's informations if he can access this room
* description: Returns the member's information if he can access this room
* security:
* - Bearer: []
* produces:
@@ -181,6 +187,7 @@ class AdminApi implements AdminInterface {
* example: "http://play.workadventure.localhost/@/teamSlug/worldSLug/roomSlug"
* - name: "ipAddress"
* in: "query"
* description: "IP Address of the user logged in, allows you to check whether a user has been banned or not"
* required: true
* type: "string"
* example: "127.0.0.1"
+35
View File
@@ -8,6 +8,41 @@ class AdminWokaService implements WokaServiceInterface {
* Returns the list of all available Wokas for the current user.
*/
getWokaList(roomUrl: string, token: string): Promise<WokaList | undefined> {
/**
* @openapi
* /api/woka/list:
* get:
* tags: ["AdminAPI"]
* description: Get all the woka from the world specified
* security:
* - Bearer: []
* produces:
* - "application/json"
* parameters:
* - name: "roomUrl"
* in: "query"
* description: "The slug of the room"
* type: "string"
* required: true
* example: "/@/teamSlug/worldSlug/roomSlug"
* - name: "uuid"
* in: "query"
* description: "The uuid of the user \n It can be an uuid or an email"
* type: "string"
* required: true
* example: "998ce839-3dea-4698-8b41-ebbdf7688ad8"
* responses:
* 200:
* description: The list of the woka
* schema:
* type: array
* items:
* $ref: '#/definitions/WokaList'
* 404:
* description: Error while retrieving the data
* schema:
* $ref: '#/definitions/ErrorApiErrorData'
*/
return axios
.get<unknown, AxiosResponse<unknown>>(`${ADMIN_API_URL}/api/woka/list`, {
headers: { Authorization: `${ADMIN_API_TOKEN}` },
+2
View File
@@ -658,6 +658,8 @@ export class SocketManager implements ZoneEventListener {
errorMessage.setSubtitle(new StringValue().setValue(errorApi.subtitle));
errorMessage.setDetails(new StringValue().setValue(errorApi.details));
errorMessage.setImage(new StringValue().setValue(errorApi.image));
if (errorApi.type == "unauthorized" && errorApi.buttonTitle)
errorMessage.setButtontitle(new StringValue().setValue(errorApi.buttonTitle));
}
if (errorApi.type == "retry") {
if (errorApi.buttonTitle) errorMessage.setButtontitle(new StringValue().setValue(errorApi.buttonTitle));
+16 -2
View File
@@ -8,10 +8,22 @@ import {
} from "../Messages/JsonMessages/ErrorApiData";
import { isMapDetailsData } from "../Messages/JsonMessages/MapDetailsData";
import { isFetchMemberDataByUuidResponse } from "./AdminApi";
import { isWokaDetail } from "../Messages/JsonMessages/PlayerTextures";
import { isWokaDetail, wokaList, wokaTexture } from "../Messages/JsonMessages/PlayerTextures";
class SwaggerGenerator {
definitions() {
definitions(type: string | null) {
const definitions = {
definitions: {
AdminApiData: generateSchema(isAdminApiData),
ErrorApiUnauthorizedData: generateSchema(isErrorApiUnauthorizedData),
FetchMemberDataByUuidResponse: generateSchema(isFetchMemberDataByUuidResponse),
MapDetailsData: generateSchema(isMapDetailsData),
WokaDetail: generateSchema(isWokaDetail),
},
};
if (type === "external") {
return definitions;
}
return {
definitions: {
AdminApiData: generateSchema(isAdminApiData),
@@ -27,6 +39,8 @@ class SwaggerGenerator {
//RoomRedirect: generateSchema(isRoomRedirect),
//UserMessageAdminMessageInterface: generateSchema(isUserMessageAdminMessageInterface),
WokaDetail: generateSchema(isWokaDetail),
WokaList: generateSchema(wokaList),
WokaTexture: generateSchema(wokaTexture),
},
};
}