From d6cafc67b2f925737bd8eb65a966bfbe2f5f8743 Mon Sep 17 00:00:00 2001 From: alexz Date: Fri, 9 Jan 2026 10:33:58 +0000 Subject: [PATCH] add scalar as an app --- scalar/apidocs/admin.json | 685 ++++++++++++++ scalar/apidocs/cl.json | 351 +++++++ scalar/apidocs/database.json | 394 ++++++++ scalar/apidocs/files.json | 404 ++++++++ scalar/apidocs/health.json | 76 ++ scalar/apidocs/ifs.json | 530 +++++++++++ scalar/apidocs/index.json | 149 +++ scalar/apidocs/pdf.json | 287 ++++++ scalar/apidocs/qsys.json | 225 +++++ scalar/apidocs/security.json | 1585 ++++++++++++++++++++++++++++++++ scalar/apidocs/session.json | 477 ++++++++++ scalar/apidocs/sql.json | 207 +++++ scalar/config.json | 41 + scalar/docker-compose.json | 36 + scalar/docker-compose.yml | 45 + scalar/metadata/description.md | 10 + scalar/metadata/logo.jpg | Bin 0 -> 3023042 bytes 17 files changed, 5502 insertions(+) create mode 100644 scalar/apidocs/admin.json create mode 100644 scalar/apidocs/cl.json create mode 100644 scalar/apidocs/database.json create mode 100644 scalar/apidocs/files.json create mode 100644 scalar/apidocs/health.json create mode 100644 scalar/apidocs/ifs.json create mode 100644 scalar/apidocs/index.json create mode 100644 scalar/apidocs/pdf.json create mode 100644 scalar/apidocs/qsys.json create mode 100644 scalar/apidocs/security.json create mode 100644 scalar/apidocs/session.json create mode 100644 scalar/apidocs/sql.json create mode 100644 scalar/config.json create mode 100644 scalar/docker-compose.json create mode 100644 scalar/docker-compose.yml create mode 100644 scalar/metadata/description.md create mode 100644 scalar/metadata/logo.jpg diff --git a/scalar/apidocs/admin.json b/scalar/apidocs/admin.json new file mode 100644 index 0000000..2643568 --- /dev/null +++ b/scalar/apidocs/admin.json @@ -0,0 +1,685 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - Administration Services", + "description": "Administration Services provide APIs that give information about RSE API and the runtime environment of the RSE API server. To use the APIs, the authenticated user must authenticate to localhost and have the administrator role or have *ALLOBJ special authority.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "Administration Services", + "description": "Administration Services provide APIs that give information about RSE API and the runtime environment of the RSE API server. To use the APIs, the authenticated user must authenticate to localhost and have the administrator role or have *ALLOBJ special authority." + } + ], + "paths": { + "/v1/admin/memory": { + "get": { + "tags": ["Administration Services"], + "summary": "Get information about server memory usage.", + "description": "Get information about the JVM memory usage of the server running RSE API.", + "operationId": "adminGetMemoryUsage", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "jvmFreeMemory": 17428920, + "jvmMaxMemory": 4294967296, + "jvmTotalMemory": 78249984 + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/admin/settings": { + "get": { + "tags": ["Administration Services"], + "summary": "Get the general settings being used for RSE API.", + "description": "The settings are global in nature and include settings for tuning, environment, and administrator override categories.", + "operationId": "adminGetSettings", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "adminUsers": ["USER1", "USER2"], + "includeUsers": ["USER1", "USER2"], + "excludeUsers": [], + "maxFileSize": 3072000, + "maxSessionInactivity": 7200, + "maxSessionLifetime": 21600, + "maxSessionUseCount": 1000, + "maxSessionWaitTime": 300, + "maxSessions": 100, + "maxSessionsPerUser": 20, + "sessionCleanupInterval": 300 + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + }, + "post": { + "tags": ["Administration Services"], + "summary": "Set settings for RSE API.", + "description": "The settings are global in nature and include settings for tuning, environment, and administrator override categories.", + "operationId": "adminSetSettings", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The settings for RSE API.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_Settings" + }, + "example": { + "persist": false, + "adminUsers": ["USER1", "USER2"], + "includeUsers": ["USER1", "USER2"], + "excludeUsers": [], + "maxFileSize": 3072000, + "maxSessionInactivity": 7200, + "maxSessionLifetime": 21600, + "maxSessionUseCount": 1000, + "maxSessionWaitTime": 300, + "maxSessions": 100, + "maxSessionsPerUser": 20, + "sessionCleanupInterval": 300 + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Successful request, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/admin/environment": { + "get": { + "tags": ["Administration Services"], + "summary": "Get information about server environment.", + "description": "Get information about server environment, such as host, operating system, Java version, and port.", + "operationId": "adminGetEnvironment", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "rest4iBasepath": "rest4i", + "rest4iHostname": "UT30P44", + "rest4iPort": 2012, + "rest4iVersion": "1.0.6", + "osName": "OS/400", + "osVersion": "V7R5M0", + "javaVersion": "1.8.0_351" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/admin/serverinfo": { + "get": { + "tags": ["Administration Services"], + "summary": "Get server information.", + "description": "Get basic server information including hostname, port, version, and base path.", + "operationId": "adminGetServerInfo", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "rest4iBasepath": "rest4i", + "rest4iHostname": "UT30P44", + "rest4iPort": 2012, + "rest4iVersion": "1.0.6" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/admin/sessions": { + "get": { + "tags": ["Administration Services"], + "summary": "Get information about sessions.", + "description": "Get information about sessions. The information that is returned applies to active sessions on the server. Active sessions may include sessions that are expired but have not been reclaimed by RSE API.", + "operationId": "adminGetSessions", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "totalSessions": 3, + "sessions": [ + { + "userid": "USER1", + "sessionCount": 1 + }, + { + "userid": "USER2", + "sessionCount": 2 + } + ] + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + }, + "delete": { + "tags": ["Administration Services"], + "summary": "Delete sessions.", + "description": "Delete sessions. You can delete all active sessions or only sessions tied to a user ID. Sessions that are deleted are marked as expired.", + "operationId": "adminClearSessions", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + }, + { + "name": "user", + "in": "query", + "description": "The session(s) to delete. Specify the user ID to delete all sessions created by the user, or the special value of *ALL to delete all sessions for all users.", + "schema": { + "type": "string", + "default": "*ALL" + } + } + ], + "responses": { + "204": { + "description": "Successful request, no content." + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + } + }, + "components": { + "schemas": { + "rest4i_Settings": { + "type": "object", + "properties": { + "persist": { + "type": "boolean", + "description": "Save settings to property file in persistent storage (hard disk).", + "default": false + }, + "adminUsers": { + "type": "array", + "description": "User IDs that will be designated as an RSE API administrator.", + "items": { + "type": "string" + } + }, + "includeUsers": { + "type": "array", + "description": "User IDs allowed to use RSE API.", + "items": { + "type": "string" + } + }, + "excludeUsers": { + "type": "array", + "description": "User IDs not allowed to use RSE API.", + "items": { + "type": "string" + } + }, + "maxFileSize": { + "maximum": 15360000, + "minimum": 0, + "type": "integer", + "description": "Maximum size of IFS file data that can be processed (reading or writing) by RSE API.", + "format": "int64", + "default": 3072000 + }, + "maxSessions": { + "minimum": -1, + "type": "integer", + "description": "Maximum number of total sessions. The value of -1 indicates there is no limit.", + "format": "int64", + "default": 100 + }, + "maxSessionsPerUser": { + "type": "integer", + "description": "Maximum number of sessions allowed on a per-user basis. The value of -1 indicates there is no limit.", + "format": "int64", + "default": 20 + }, + "maxSessionInactivity": { + "maximum": 7200, + "minimum": 30, + "type": "integer", + "description": "Maximum amount of inactive time, in seconds, before an available session is invalidated.", + "format": "int64", + "default": 7200 + }, + "maxSessionLifetime": { + "type": "integer", + "description": "Maximum life, in seconds, for a session. The value of -1 indicates there is no limit.", + "format": "int64", + "default": -1 + }, + "maxSessionUseCount": { + "type": "integer", + "description": "Maximum number of times a session can be used before it is invalidated. The value of -1 indicates there is no limit.", + "format": "int64", + "default": -1 + }, + "maxSessionWaitTime": { + "minimum": -1, + "type": "integer", + "description": "Maximum time, in seconds, to wait on a session to become available. The value of -1 indicates there is no limit.", + "format": "int64", + "default": 300 + }, + "sessionCleanupInterval": { + "maximum": 900, + "minimum": 30, + "type": "integer", + "description": "The time interval, in seconds, for how often the session maintenance daemon is run.", + "format": "int64", + "default": 300 + } + }, + "description": "Global settings for RSE API." + }, + "Problem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "details": { + "type": "object" + } + } + } + }, + "securitySchemes": { + "bearerHttpAuthentication": { + "type": "http", + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "Bearer [token]" + }, + "basicHttpAuthentication": { + "type": "http", + "description": "Basic authentication.", + "scheme": "basic" + } + } + } +} diff --git a/scalar/apidocs/cl.json b/scalar/apidocs/cl.json new file mode 100644 index 0000000..33d4d43 --- /dev/null +++ b/scalar/apidocs/cl.json @@ -0,0 +1,351 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - CL Command Services", + "description": "CL Command Services provide APIs for running CL commands.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "CL Command Services", + "description": "CL Command Services provide APIs for running CL commands." + } + ], + "paths": { + "/v1/cl": { + "post": { + "tags": ["CL Command Services"], + "summary": "Run one or more CL commands on the server.", + "description": "Run one or more CL commands on the server. Accepts both JSON and form-urlencoded content types. If a command fails, any messages relating to the error is returned. If the command succeeds, no data is returned.", + "operationId": "clRunCLCommand", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "List of CL commands to run.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_CLCommands" + }, + "example": { + "continueOnError": true, + "includeMessageOnSuccess": true, + "includeMessageHelpText": false, + "clCommands": [ + "qsys/crtlib lib1", + "qsys/crtsrcpf lib1/qrpglesrc" + ] + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "continueOnError": { + "type": "boolean", + "description": "Continue processing if error encountered", + "default": false + }, + "includeMessageOnSuccess": { + "type": "boolean", + "description": "Return messages on success", + "default": false + }, + "includeMessageHelpText": { + "type": "boolean", + "description": "Return message help text", + "default": false + }, + "clCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Array of CL command strings to execute" + } + }, + "required": ["clCommands"] + } + } + } + }, + "responses": { + "200": { + "description": "Command(s) issued successfully, error(s) may have occurred.", + "content": { + "application/json": { + "example": { + "totalIssued": 2, + "totalSuccesses": 1, + "totalFailures": 1, + "commandOutputList": [ + { + "success": false, + "command": "qsys/crtlib lib1", + "output": ["CPF2111: Library LIB1 already exists."] + }, + { + "success": true, + "command": "qsys/crtsrcpf lib1/qrpglesrc", + "output": ["CPC7301: File QRPGLESRC created in library LIB1."] + } + ] + } + } + } + }, + "204": { + "description": "All command(s) issued successfully, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/cl/{commandname}": { + "get": { + "tags": ["CL Command Services"], + "summary": "Retrieves the command definition for the specified CL command.", + "description": "Retrieves the command definition for the specified CL command. The command definition is returned as an XML document. The generated command information XML source is called Command Definition Markup Language or CDML. See the Document Type Definition (DTD) in /QIBM/XML/DTD/QcdCLCmd.dtd for the definition of the CDML tag language returned by this API.", + "operationId": "clGetCommandDefinition", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "schema": { + "type": "string" + } + }, + { + "name": "library", + "in": "query", + "description": "The library name. Valid values are a specific name, or one of the following special values: *CURLIB - The current library is searched. *LIBL - The library list is searched. This is the default.", + "schema": { + "type": "string", + "default": "*LIBL" + } + }, + { + "name": "ignorecase", + "in": "query", + "description": "Boolean indicating whether case should be ignored. The default value is 'true'. If set to 'false', the library and command name will not be uppercased.", + "schema": { + "type": "boolean", + "default": true + } + }, + { + "name": "commandname", + "in": "path", + "description": "The CL command name.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "definition": "..." + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + } + }, + "components": { + "schemas": { + "rest4i_CLCommands": { + "required": ["clCommands"], + "type": "object", + "properties": { + "continueOnError": { + "type": "boolean", + "description": "Continue processing CL commands if an error is encountered.", + "default": false + }, + "includeMessageOnSuccess": { + "type": "boolean", + "description": "Return CL command messages on success.", + "default": false + }, + "includeMessageHelpText": { + "type": "boolean", + "description": "Return message help text for CL command messages.", + "default": false + }, + "clCommands": { + "type": "array", + "description": "CL command to run.", + "items": { + "type": "string" + } + } + }, + "description": "List of CL commands to run." + }, + "Problem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "details": { + "type": "object" + } + } + } + }, + "securitySchemes": { + "bearerHttpAuthentication": { + "type": "http", + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "Bearer [token]" + }, + "basicHttpAuthentication": { + "type": "http", + "description": "Basic authentication.", + "scheme": "basic" + } + } + } +} diff --git a/scalar/apidocs/database.json b/scalar/apidocs/database.json new file mode 100644 index 0000000..ac59d2c --- /dev/null +++ b/scalar/apidocs/database.json @@ -0,0 +1,394 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - Database Services", + "description": "Enhanced database services with connection pooling and multiple output formats. Execute SQL queries with support for JSON, XML, CSV, HTML, and Excel output formats.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "Database Services", + "description": "Enhanced database services with connection pooling and multiple output formats." + } + ], + "paths": { + "/v1/database/query": { + "post": { + "tags": ["Database Services"], + "summary": "Execute SQL query with multiple output formats", + "description": "Execute a SQL query against the IBM i database with support for multiple output formats including JSON, XML, CSV, HTML, and Excel.", + "operationId": "executeQuery", + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ], + "requestBody": { + "description": "The SQL query and formatting options", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DatabaseQueryRequest" + }, + "example": { + "sql": "SELECT * FROM QSYS2.SYSTABLES FETCH FIRST 10 ROWS ONLY", + "format": "json", + "treatWarningsAsErrors": false, + "alwaysReturnSQLStateInformation": false + } + } + } + }, + "responses": { + "200": { + "description": "SQL query executed successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "success": true, + "data": { + "rowCount": 2, + "data": [ + { + "TABLE_SCHEMA": "QSYS2", + "TABLE_NAME": "SYSTABLES", + "TABLE_TYPE": "SYSTEM TABLE" + } + ] + }, + "message": "Query executed successfully", + "timestamp": "2024-01-15T10:30:00Z" + } + }, + "application/xml": { + "example": "trueQSYS2" + }, + "text/csv": { + "example": "TABLE_SCHEMA,TABLE_NAME,TABLE_TYPE\n\"QSYS2\",\"SYSTABLES\",\"SYSTEM TABLE\"" + } + } + }, + "400": { + "description": "Bad request - invalid SQL or parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "Validation failed", + "timestamp": "2024-01-15T10:30:00Z", + "validationErrors": [ + { + "field": "sql", + "message": "SQL query is required" + } + ] + } + } + } + } + }, + "401": { + "description": "Unauthorized request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "Forbidden - dangerous SQL detected", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + } + }, + "get": { + "tags": ["Database Services"], + "summary": "Execute SQL query via GET with multiple output formats", + "description": "Execute a SQL query via GET request with support for multiple output formats.", + "operationId": "executeQueryGet", + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ], + "parameters": [ + { + "name": "sql", + "in": "query", + "description": "The SQL query to execute", + "required": true, + "schema": { + "type": "string" + }, + "example": "SELECT * FROM QSYS2.SYSTABLES FETCH FIRST 10 ROWS ONLY" + }, + { + "name": "format", + "in": "query", + "description": "Output format: json, xml, csv, html, excel", + "schema": { + "type": "string", + "enum": ["json", "xml", "csv", "html", "excel"], + "default": "json" + } + } + ], + "responses": { + "200": { + "description": "SQL query executed successfully" + }, + "400": { + "description": "Bad request - invalid SQL or parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "Forbidden - dangerous SQL detected", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "DatabaseQueryRequest": { + "type": "object", + "properties": { + "sql": { + "type": "string", + "description": "The SQL query to execute", + "example": "SELECT * FROM QSYS2.SYSTABLES FETCH FIRST 10 ROWS ONLY" + }, + "format": { + "type": "string", + "enum": ["json", "xml", "csv", "html", "excel"], + "default": "json", + "description": "Output format for the query results" + }, + "treatWarningsAsErrors": { + "type": "boolean", + "default": false, + "description": "Whether to treat SQL warnings as errors" + }, + "alwaysReturnSQLStateInformation": { + "type": "boolean", + "default": false, + "description": "Whether to always return SQL state information" + } + }, + "required": ["sql"] + }, + "ApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "Indicates if the operation was successful" + }, + "data": { + "description": "The response data (varies by endpoint)" + }, + "error": { + "$ref": "#/components/schemas/ErrorInfo" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationInfo" + }, + "message": { + "type": "string", + "description": "Success or informational message" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "ISO-8601 formatted timestamp of the response" + }, + "metadata": { + "type": "object", + "additionalProperties": true, + "description": "Additional metadata about the response" + } + }, + "required": ["success", "timestamp"] + }, + "ErrorInfo": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description": "Error code identifying the type of error" + }, + "message": { + "type": "string", + "description": "Human-readable error message" + }, + "details": { + "type": "string", + "description": "Additional error details" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "ISO-8601 formatted timestamp when error occurred" + }, + "validationErrors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "description": "List of validation errors if applicable" + } + }, + "required": ["code", "message"] + }, + "ValidationError": { + "type": "object", + "properties": { + "field": { + "type": "string", + "description": "The field that failed validation" + }, + "message": { + "type": "string", + "description": "The validation error message" + }, + "rejectedValue": { + "description": "The value that was rejected during validation" + } + }, + "required": ["field", "message"] + }, + "PaginationInfo": { + "type": "object", + "properties": { + "page": { + "type": "integer", + "description": "Current page number (0-based)" + }, + "size": { + "type": "integer", + "description": "Number of elements per page" + }, + "totalPages": { + "type": "integer", + "description": "Total number of pages" + }, + "totalElements": { + "type": "integer", + "format": "int64", + "description": "Total number of elements across all pages" + } + } + }, + "Problem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "details": { + "type": "object" + } + } + } + }, + "securitySchemes": { + "bearerHttpAuthentication": { + "type": "http", + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "Bearer [token]" + }, + "basicHttpAuthentication": { + "type": "http", + "description": "Basic authentication.", + "scheme": "basic" + } + } + } +} diff --git a/scalar/apidocs/files.json b/scalar/apidocs/files.json new file mode 100644 index 0000000..92ddb3a --- /dev/null +++ b/scalar/apidocs/files.json @@ -0,0 +1,404 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - File Operations Services", + "description": "Enhanced file operations services supporting both IFS and SMB file operations with improved error handling and response formatting.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "File Operations Services", + "description": "Enhanced file operations services supporting both IFS and SMB file operations." + } + ], + "paths": { + "/v1/files/list": { + "get": { + "tags": ["File Operations Services"], + "summary": "List directory contents", + "description": "List files and directories at specified path using SMB/CIFS or IFS", + "operationId": "listFiles", + "parameters": [ + { + "name": "path", + "in": "query", + "required": true, + "description": "Directory path to list", + "schema": { + "type": "string" + }, + "example": "/home/user/documents" + } + ], + "responses": { + "200": { + "description": "Directory listing successful", + "content": { + "application/json": { + "example": { + "success": true, + "data": { + "path": "/home/user/documents", + "files": [ + { + "name": "report.pdf", + "size": 102400, + "isDirectory": false + }, + { + "name": "archive", + "size": 0, + "isDirectory": true + } + ] + } + } + } + } + } + } + } + }, + "/v1/files/download": { + "get": { + "tags": ["File Operations Services"], + "summary": "Download a file", + "description": "Downloads a file from the specified SMB path.", + "operationId": "downloadFile", + "parameters": [ + { + "name": "path", + "in": "query", + "required": true, + "description": "The SMB path to the file", + "schema": { + "type": "string" + } + }, + { + "name": "domain", + "in": "query", + "description": "SMB domain", + "schema": { + "type": "string" + } + }, + { + "name": "username", + "in": "query", + "description": "SMB username", + "schema": { + "type": "string" + } + }, + { + "name": "password", + "in": "query", + "description": "SMB password", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "File downloaded successfully", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "Bad request - path parameter required", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "File not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + } + } + }, + "/v1/files/upload": { + "post": { + "tags": ["File Operations Services"], + "summary": "Upload a file", + "description": "Uploads a file to the specified SMB path.", + "operationId": "uploadFile", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "File uploaded successfully", + "content": { + "application/json": { + "example": { + "success": true, + "message": "File uploaded successfully", + "folder": "/upload/path", + "filename": "document.pdf" + } + } + } + }, + "400": { + "description": "Bad request - missing required parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + } + } + }, + "/v1/files/zip": { + "get": { + "tags": ["File Operations Services"], + "summary": "Zip directory contents", + "description": "Creates a ZIP archive of all files in the specified directory.", + "operationId": "zipDirectory", + "parameters": [ + { + "name": "path", + "in": "query", + "required": true, + "description": "The SMB path to the directory", + "schema": { + "type": "string" + } + }, + { + "name": "domain", + "in": "query", + "description": "SMB domain", + "schema": { + "type": "string" + } + }, + { + "name": "username", + "in": "query", + "description": "SMB username", + "schema": { + "type": "string" + } + }, + { + "name": "password", + "in": "query", + "description": "SMB password", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Directory zipped successfully", + "content": { + "application/zip": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "Bad request - path parameter required", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "Directory not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + } + } + }, + "/v1/files/exists": { + "get": { + "tags": ["File Operations Services"], + "summary": "Check if file or directory exists", + "description": "Checks whether a file or directory exists at the specified SMB path.", + "operationId": "checkExists", + "parameters": [ + { + "name": "path", + "in": "query", + "required": true, + "description": "The SMB path to check", + "schema": { + "type": "string" + } + }, + { + "name": "domain", + "in": "query", + "description": "SMB domain", + "schema": { + "type": "string" + } + }, + { + "name": "username", + "in": "query", + "description": "SMB username", + "schema": { + "type": "string" + } + }, + { + "name": "password", + "in": "query", + "description": "SMB password", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Existence check completed", + "content": { + "application/json": { + "example": { + "success": true, + "path": "/path/to/check", + "exists": true + } + } + } + }, + "400": { + "description": "Bad request - path parameter required", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "UploadRequest": { + "type": "object", + "properties": { + "folder": { + "type": "string", + "description": "The target folder path for upload" + }, + "filename": { + "type": "string", + "description": "The name of the file to upload" + }, + "domain": { + "type": "string", + "description": "SMB domain" + }, + "username": { + "type": "string", + "description": "SMB username" + }, + "password": { + "type": "string", + "description": "SMB password" + }, + "fileContent": { + "type": "string", + "format": "byte", + "description": "Base64 encoded file content" + } + }, + "required": ["folder", "filename", "fileContent"] + }, + "Problem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "details": { + "type": "object" + } + } + } + }, + "securitySchemes": { + "bearerHttpAuthentication": { + "type": "http", + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "Bearer [token]" + }, + "basicHttpAuthentication": { + "type": "http", + "description": "Basic authentication.", + "scheme": "basic" + } + } + } +} diff --git a/scalar/apidocs/health.json b/scalar/apidocs/health.json new file mode 100644 index 0000000..958431a --- /dev/null +++ b/scalar/apidocs/health.json @@ -0,0 +1,76 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - Health Services", + "description": "Health check endpoints for service monitoring and liveness probes.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "Health", + "description": "Health check endpoints for service monitoring and liveness probes." + } + ], + "paths": { + "/v1/health/ping": { + "get": { + "tags": ["Health"], + "summary": "Ping", + "description": "Lightweight liveness check.", + "operationId": "get_api_v1_health_ping", + "responses": { + "200": { + "description": "Service is alive", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ok": { + "type": "boolean" + } + }, + "required": ["ok"] + }, + "examples": { + "ok": { + "value": { + "ok": true + } + } + } + } + } + } + }, + "security": [ + { + "basicAuth": [] + } + ] + } + } + }, + "components": { + "schemas": {}, + "securitySchemes": { + "basicAuth": { + "type": "http", + "scheme": "basic" + } + } + } +} diff --git a/scalar/apidocs/ifs.json b/scalar/apidocs/ifs.json new file mode 100644 index 0000000..f0570a5 --- /dev/null +++ b/scalar/apidocs/ifs.json @@ -0,0 +1,530 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - IFS Services", + "description": "Integrated File System (IFS) Services provide APIs for accessing objects in a way that is like personal computer and UNIX operating systems. This includes listing objects in directories, reading from files, and writing to files.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "IFS Services", + "description": "Integrated File System (IFS) Services provide APIs for accessing objects in a way that is like personal computer and UNIX operating systems." + } + ], + "paths": { + "/v1/ifs/{path}": { + "get": { + "tags": ["IFS Services"], + "summary": "Get the content of a file.", + "description": "Get the content of a file. The content is returned in a JSON object.", + "operationId": "ifsGetFileContent", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + }, + { + "name": "ETag", + "in": "header", + "description": "Whether to return checksum for the file.", + "schema": { + "type": "boolean" + } + }, + { + "name": "path", + "in": "path", + "description": "Path to file for which the data is to be read.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "ccsid": 819, + "content": "Hello world\n" + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + }, + "put": { + "tags": ["IFS Services"], + "summary": "Write a string to a file.", + "description": "Write a string to a file. The file must exist and its contents will be replaced by the string specified in the request.", + "operationId": "ifsPutFileContent", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + }, + { + "name": "If-Match", + "in": "header", + "description": "If the If-Match HTTP header is passed, RSE API will check to see if the Etag (MD5 hash of the object content) matches the provided Etag value. If this value matches, the operation will proceed. If the match fails, the system will return a 412 (Precondition Failed) error.", + "schema": { + "type": "string" + } + }, + { + "name": "path", + "in": "path", + "description": "Path to file in which the data will be written.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "File content.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_FileContent" + }, + "example": { + "content": "some data that will be written to file." + } + }, + "text/plain": { + "schema": {}, + "example": "some data that will be written to file.\n" + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Successful request, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "412": { + "description": "Precondition failed." + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/ifs/list": { + "get": { + "tags": ["IFS Services"], + "summary": "Gets a list of objects in the specified path.", + "description": "Gets a list of objects in the specified path. The information returned includes the name, whether object is a directory, the description, and the object subtype. For objects that are not in the QSYS.LIB file system, any part of the path may contain an asterisk (*), which is a wildcard that means zero or more instances of any character.", + "operationId": "ifsListDir", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + }, + { + "name": "path", + "in": "query", + "description": "The working directory. For example, /u/IBM/test", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "subtype", + "in": "query", + "description": "Subtype of objects to return. Valid values include a specific object type (*LIB, *FILE, *PGM, *OUTQ, etc.) or *ALL.", + "schema": { + "type": "string" + } + }, + { + "name": "includehidden", + "in": "query", + "description": "Whether to show hidden files.", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "objects": [ + { + "path": "/QSYS.LIB/USER1.LIB/QCLSRC.FILE", + "description": "\"test\"", + "isDir": true, + "subType": "PF-SRC" + }, + { + "path": "/QSYS.LIB/USER1.LIB/QCSRC.FILE", + "description": "", + "isDir": true, + "subType": "PF-SRC" + } + ] + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/ifs/{path}/info": { + "get": { + "tags": ["IFS Services"], + "summary": "Returns information about the object referenced by the path.", + "description": "Returns information about the object referenced by the path. The information returned includes the name, whether object is a directory, the description, the object subtype, CCSID, size, last modified timestamp, and object subtype.", + "operationId": "ifsGetFileInfo", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + }, + { + "name": "path", + "in": "path", + "description": "Path to object for which information is to be returned.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "path": "/qsys.lib/user1.lib", + "description": "user1's lib", + "isDir": true, + "subType": "PROD", + "owner": "USER1", + "ccsid": 37, + "lastModified": 1680559633, + "size": 401408, + "recordLength": -1, + "numberOfRecords": -1 + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + } + }, + "components": { + "schemas": { + "rest4i_FileContent": { + "required": ["content"], + "type": "object", + "properties": { + "content": { + "type": "string" + } + }, + "description": "The file content." + }, + "Problem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "details": { + "type": "object" + } + } + } + }, + "securitySchemes": { + "bearerHttpAuthentication": { + "type": "http", + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "Bearer [token]" + }, + "basicHttpAuthentication": { + "type": "http", + "description": "Basic authentication.", + "scheme": "basic" + } + } + } +} diff --git a/scalar/apidocs/index.json b/scalar/apidocs/index.json new file mode 100644 index 0000000..7ff335c --- /dev/null +++ b/scalar/apidocs/index.json @@ -0,0 +1,149 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i API Specifications Index", + "description": "This index lists all available OpenAPI specification files for the REST4i API. Each category has its own specification file for easier navigation and consumption.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "specifications": [ + { + "name": "Administration Services", + "file": "admin.json", + "description": "Administration Services provide APIs that give information about RSE API and the runtime environment of the RSE API server.", + "endpoints": 7, + "paths": [ + "/v1/admin/memory", + "/v1/admin/settings", + "/v1/admin/environment", + "/v1/admin/serverinfo", + "/v1/admin/sessions" + ] + }, + { + "name": "CL Command Services", + "file": "cl.json", + "description": "CL Command Services provide APIs for running CL commands.", + "endpoints": 2, + "paths": [ + "/v1/cl", + "/v1/cl/{commandname}" + ] + }, + { + "name": "SQL Services", + "file": "sql.json", + "description": "SQL Services provide APIs associated with performing SQL operations.", + "endpoints": 1, + "paths": [ + "/v1/sql" + ] + }, + { + "name": "IFS Services", + "file": "ifs.json", + "description": "Integrated File System (IFS) Services provide APIs for accessing objects in a way that is like personal computer and UNIX operating systems.", + "endpoints": 4, + "paths": [ + "/v1/ifs/{path}", + "/v1/ifs/list", + "/v1/ifs/{path}/info" + ] + }, + { + "name": "QSYS Services", + "file": "qsys.json", + "description": "QSYS Services provide APIs for accessing QSYS objects.", + "endpoints": 1, + "paths": [ + "/v1/qsys/search/{objectName}" + ] + }, + { + "name": "Session Services", + "file": "session.json", + "description": "Session Services provide APIs for authenticating a user and managing sessions.", + "endpoints": 4, + "paths": [ + "/v1/session" + ] + }, + { + "name": "Database Services", + "file": "database.json", + "description": "Enhanced database services with connection pooling and multiple output formats.", + "endpoints": 2, + "paths": [ + "/v1/database/query" + ] + }, + { + "name": "File Operations Services", + "file": "files.json", + "description": "Enhanced file operations services supporting both IFS and SMB file operations.", + "endpoints": 5, + "paths": [ + "/v1/files/list", + "/v1/files/download", + "/v1/files/upload", + "/v1/files/zip", + "/v1/files/exists" + ] + }, + { + "name": "PDF Services", + "file": "pdf.json", + "description": "PDF processing services for creating, manipulating, and extracting data from PDF documents.", + "endpoints": 4, + "paths": [ + "/v1/pdf/convert", + "/v1/pdf/merge", + "/v1/pdf/rotate", + "/v1/pdf/unprotect" + ] + }, + { + "name": "Security Services", + "file": "security.json", + "description": "Security Services provide APIs relating to digital certificate management and TLS system information.", + "endpoints": 14, + "paths": [ + "/v1/security/dcm/cert/delete", + "/v1/security/dcm/cert/export", + "/v1/security/dcm/cert/info", + "/v1/security/dcm/cert/list", + "/v1/security/dcm/cert/import", + "/v1/security/dcm/appdef/associate", + "/v1/security/dcm/appdef/disassociate", + "/v1/security/dcm/appdef/list", + "/v1/security/dcm/appdef/trust", + "/v1/security/dcm/appdef/untrust", + "/v1/security/dcm/certstore/changepassword", + "/v1/security/tls", + "/v1/security/tls/stats" + ] + }, + { + "name": "Health Services", + "file": "health.json", + "description": "Health check endpoints for service monitoring and liveness probes.", + "endpoints": 1, + "paths": [ + "/v1/health/ping" + ] + } + ], + "summary": { + "totalSpecFiles": 11, + "totalEndpoints": 45, + "version": "1.0.7-rest4i", + "masterSpec": "../scalar.json" + } +} diff --git a/scalar/apidocs/pdf.json b/scalar/apidocs/pdf.json new file mode 100644 index 0000000..b18c30e --- /dev/null +++ b/scalar/apidocs/pdf.json @@ -0,0 +1,287 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - PDF Services", + "description": "PDF processing services for creating, manipulating, and extracting data from PDF documents on IBM i systems.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "PDF Services", + "description": "PDF processing services for creating, manipulating, and extracting data from PDF documents." + } + ], + "paths": { + "/v1/pdf/convert": { + "post": { + "tags": ["PDF Services"], + "summary": "Convert HTML to PDF", + "description": "Convert HTML content to PDF document", + "operationId": "convertToPdf", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConvertRequest" + }, + "example": { + "html": "

Report

Content here

", + "size": "A4", + "orientation": "portrait" + } + } + } + }, + "responses": { + "200": { + "description": "PDF generated successfully", + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/v1/pdf/merge": { + "post": { + "tags": ["PDF Services"], + "summary": "Merge PDF files", + "description": "Merges multiple PDF files into a single PDF document.", + "operationId": "mergePdfs", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MergeRequest" + } + } + } + }, + "responses": { + "200": { + "description": "PDFs merged successfully", + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "Bad request - at least one PDF file is required", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + } + } + }, + "/v1/pdf/rotate": { + "post": { + "tags": ["PDF Services"], + "summary": "Rotate PDF pages", + "description": "Rotates all pages in a PDF document by the specified angle.", + "operationId": "rotatePdf", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RotateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "PDF rotated successfully", + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "Bad request - PDF file is required", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + } + } + }, + "/v1/pdf/unprotect": { + "post": { + "tags": ["PDF Services"], + "summary": "Remove PDF protection", + "description": "Removes protection from a PDF document by converting it to images and back to PDF.", + "operationId": "unprotectPdf", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnprotectRequest" + } + } + } + }, + "responses": { + "200": { + "description": "PDF unprotected successfully", + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "Bad request - PDF file is required", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ConvertRequest": { + "type": "object", + "properties": { + "html": { + "type": "string", + "description": "HTML content to convert to PDF" + }, + "size": { + "type": "string", + "description": "Page size for the PDF", + "default": "A4", + "enum": ["A1", "A2", "A3", "A4", "A5", "A6", "A7"] + }, + "orientation": { + "type": "string", + "description": "Page orientation", + "default": "portrait", + "enum": ["portrait", "landscape"] + } + }, + "required": ["html"] + }, + "MergeRequest": { + "type": "object", + "properties": { + "pdfFiles": { + "type": "array", + "items": { + "type": "string", + "format": "byte" + }, + "description": "Array of Base64 encoded PDF files to merge" + } + }, + "required": ["pdfFiles"] + }, + "RotateRequest": { + "type": "object", + "properties": { + "pdfFile": { + "type": "string", + "format": "byte", + "description": "Base64 encoded PDF file to rotate" + }, + "rotation": { + "type": "integer", + "description": "Rotation angle (90, 180, 270, 360/0)", + "default": 90, + "enum": [90, 180, 270, 360, 0] + } + }, + "required": ["pdfFile"] + }, + "UnprotectRequest": { + "type": "object", + "properties": { + "pdfFile": { + "type": "string", + "format": "byte", + "description": "Base64 encoded protected PDF file" + } + }, + "required": ["pdfFile"] + }, + "Problem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "details": { + "type": "object" + } + } + } + }, + "securitySchemes": { + "bearerHttpAuthentication": { + "type": "http", + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "Bearer [token]" + }, + "basicHttpAuthentication": { + "type": "http", + "description": "Basic authentication.", + "scheme": "basic" + } + } + } +} diff --git a/scalar/apidocs/qsys.json b/scalar/apidocs/qsys.json new file mode 100644 index 0000000..2f4af40 --- /dev/null +++ b/scalar/apidocs/qsys.json @@ -0,0 +1,225 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - QSYS Services", + "description": "QSYS Services provide APIs for accessing QSYS objects.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "QSYS Services", + "description": "QSYS Services provide APIs for accessing QSYS objects." + } + ], + "paths": { + "/v1/qsys/search/{objectName}": { + "get": { + "tags": ["QSYS Services"], + "summary": "Returns a list of QSYS.LIB objects that match the search criteria.", + "description": "Returns a list of QSYS.LIB objects that match the search criteria. The filter is the object name, and may be a value of *ALL, in which case all objects that match the search criteria is returned, or a generic name. A generic name is a character string that contains one or more characters followed by an asterisk (*). If a generic name is specified, all objects that have names with the same prefix as the generic name are returned.", + "operationId": "qsysGetQsysObjects", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + }, + { + "name": "objectLibrary", + "in": "query", + "description": "The library or set of libraries that are searched for objects. Valid values are a specific name, or one of the following special values: *ALL - All libraries are searched. *ALLUSR - All user libraries are searched. *CURLIB - The current library is searched. *LIBL - The library list is searched. *USRLIBL - The user portion of the library list is searched.", + "schema": { + "type": "string", + "default": "*USRLIBL" + } + }, + { + "name": "objectType", + "in": "query", + "description": "The type of object to search. Valid values include: *FILE, *PGM, *LIB, etc., or *ALL.", + "schema": { + "type": "string", + "default": "*ALL" + } + }, + { + "name": "objectSubtype", + "in": "query", + "description": "Object subtype. For example, PF-SRC, PF-DTA, SAVF, etc., or *ALL. Note that not all objects have subtypes.", + "schema": { + "type": "string", + "default": "*ALL" + } + }, + { + "name": "memberName", + "in": "query", + "description": "The member name to match for objects of type PF-SRC or PF-DTA. Valid values are a specific name, or an extended generic name where the asterisk (*) may be placed in any part of the name, or *ALL.", + "schema": { + "type": "string", + "default": "" + } + }, + { + "name": "memberType", + "in": "query", + "description": "The member type to match for members of objects of type PF-SRC or PF-DTA. Valid values are a specific name, such as C, CBLLE, RPGLE, SQLRPGLE, etc., or *ALL.", + "schema": { + "type": "string", + "default": "*ALL" + } + }, + { + "name": "objectName", + "in": "path", + "description": "The object name. Valid values are a specific name, a generic name (for example, AM*), or one of the following special values: *ALL - All object names are searched. *ALLUSR - All objects that are libraries in QSYS or the library list are searched.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "objects": [ + { + "path": "/QSYS.LIB/USER1.LIB/AXISLIBS.FILE", + "description": "", + "isDir": false, + "subType": "SAVF" + }, + { + "path": "/QSYS.LIB/USER1.LIB/DEALER.FILE", + "description": "", + "isDir": true, + "subType": "PF-DTA" + }, + { + "path": "/QSYS.LIB/USER1.LIB/QCLSRC.FILE", + "description": "\"test\"", + "isDir": true, + "subType": "PF-SRC" + } + ] + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + } + }, + "components": { + "schemas": { + "Problem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "details": { + "type": "object" + } + } + } + }, + "securitySchemes": { + "bearerHttpAuthentication": { + "type": "http", + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "Bearer [token]" + }, + "basicHttpAuthentication": { + "type": "http", + "description": "Basic authentication.", + "scheme": "basic" + } + } + } +} diff --git a/scalar/apidocs/security.json b/scalar/apidocs/security.json new file mode 100644 index 0000000..188e175 --- /dev/null +++ b/scalar/apidocs/security.json @@ -0,0 +1,1585 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - Security Services", + "description": "Security Services provide APIs relating to security, such as the management of digital certificates and the retrieval of TLS system information. All the digital certificate management APIs require the Digital Certificate Manager, option 34 of the IBM i licensed program (5761-SS1), be installed. In addition, the authenticated user must have the *ALLOBJ and *SECADM special authorities.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "Security Services", + "description": "Security Services provide APIs relating to security, such as the management of digital certificates and the retrieval of TLS system information." + } + ], + "paths": { + "/v1/security/dcm/cert/delete": { + "post": { + "tags": ["Security Services"], + "summary": "Delete a digital certificate.", + "description": "Delete a digital certificate.", + "operationId": "securityDCMDeleteCertificate", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The API properties required to delete a digital certificate.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_DCMCertRequest" + }, + "example": { + "certStoreType": "CMS", + "certStorePath": "*SYSTEM", + "certStorePassword": "passw0rd", + "certAlias": "mylabel" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Request successful, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/tls": { + "get": { + "tags": ["Security Services"], + "summary": "Retrieve system transport layer security (TLS) attributes.", + "description": "The API retrieves TLS attributes for the system. The system level settings are based on TLS System Values and System Service Tools (SST) Advanced Analysis command TLSCONFIG that allows viewing or altering of system-wide system TLS default properties.", + "operationId": "securityTLSGetAttributes", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "supportedProtocols": ["TLSv1.3", "TLSv1.2"], + "eligibleDefaultProtocols": ["TLSv1.3", "TLSv1.2"], + "defaultProtocols": ["TLSv1.3", "TLSv1.2"], + "supportedCipherSuites": ["AES_128_GCM_SHA256", "AES_256_GCM_SHA384"], + "defaultMinimumRSAKeySize": 0, + "handshakeConnectionCounts": false, + "secureSessionCaching": true, + "auditSecureTelnetHandshakes": false + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/dcm/cert/export": { + "post": { + "tags": ["Security Services"], + "summary": "Export a digital certificate.", + "description": "Export a digital certificate. Only server/client and CA certificates can be exported. Certificates can be exported in the DER, PEM, or PKCS12 formats.", + "operationId": "securityDCMExportCertificate", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The API properties required to export a digital certificate.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_DCMCertExportRequest" + }, + "example": { + "certStoreType": "CMS", + "certStorePath": "*SYSTEM", + "certStorePassword": "passw0rd", + "certFormat": "PKCS12", + "certAlias": "mylabel", + "certDataPassword": "myPassw0rd" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "certFormat": "PKCS12", + "certData": "BASE64-BLOB" + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/dcm/cert/info": { + "post": { + "tags": ["Security Services"], + "summary": "Get detailed certificate information.", + "description": "Get detailed certificate information, such as subject, issuer, subject alternative names, and serial number.", + "operationId": "securityDCMGetCertificate", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The API properties required to get detailed information about a digital certificate.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_DCMCertRequest" + }, + "example": { + "certStoreType": "CMS", + "certStorePath": "*SYSTEM", + "certStorePassword": "passw0rd", + "certAlias": "mylabel" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "alias": "UNIQUE-SAN", + "trusted": true, + "subject": "C=US,SP=Minnesota,O=IBM,CN=UniQue", + "issuer": "C=US,SP=Any,O=IBM Web Administration for i,CN=mysystem_CERTIFICATE_AUTHORITY", + "keyAlgorithm": "ECDSA", + "keySize": 256, + "hasPrivateKey": true, + "signatureAlgorithm": "RSA_SHA256", + "keyStorageLocation": "SOFTWARE", + "serialNumber": "6526CFBC0601B8", + "effectiveDate": "10/10/23 11:39:24", + "expirationDate": "10/10/24 11:39:24" + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/dcm/appdef/associate": { + "post": { + "tags": ["Security Services"], + "summary": "Associate digital certificates to an application definition.", + "description": "Associate digital certificates to an application definition. A maximum of 4 certificates can be specified.", + "operationId": "securityDCMAppDefAssociateCertificate", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The API properties required to assign digital certificates to an application definition.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_DCMCertAppDefAssociateRequest" + }, + "example": { + "appDefinitionID": "myappdef", + "certAliases": ["mylabel1", "mylabel2"] + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Request successful, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/dcm/appdef/list": { + "get": { + "tags": ["Security Services"], + "summary": "List application definitions.", + "description": "Retrieve a list of application definitions. You can filter what is returned by application definition ID and application type.", + "operationId": "securityDCMAppidList", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + }, + { + "name": "idFilter", + "in": "query", + "description": "Application definition ID filter.", + "schema": { + "type": "string" + } + }, + { + "name": "typeFilter", + "in": "query", + "description": "Application type filter. Possible values: SERVER, CLIENT, SERVER_CLIENT, OBJECT_SIGNING.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "appDefinitions": [ + { + "appDefinitionID": "QIBM_OS400_QRW_SVR_DDM_DRDA", + "appType": "SERVER", + "description": "IBM i DDM/DRDA Server - TCP/IP", + "certAliases": [] + } + ] + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/dcm/appdef/untrust": { + "post": { + "tags": ["Security Services"], + "summary": "Remove a certificate authority (CA) digital certificate from the application definition CA trust list.", + "description": "Remove a certificate authority (CA) digital certificate from the application definition CA trust list.", + "operationId": "securityDCMAppDefUntrustCertificate", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The API properties required to remove a CA digital certificate from an application definition CA trust list.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_DCMCertAppDefTrustRequest" + }, + "example": { + "appDefinitionID": "myappdef", + "certAlias": "mylabel1" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Request successful, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/dcm/appdef/disassociate": { + "post": { + "tags": ["Security Services"], + "summary": "Disassociate digital certificates from an application definition.", + "description": "Disassociate digital certificates from an application definition. All certificates associated with the application definition will be disassociated.", + "operationId": "securityDCMAppDefDisassociateCertificate", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The API properties required to disassociate digital certificates from an application definition.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_DCMCertAppDefDisassociateRequest" + }, + "example": { + "appDefinitionID": "myappdef" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Request successful, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/dcm/cert/list": { + "post": { + "tags": ["Security Services"], + "summary": "Retrieve a list of certificates in a certificate store.", + "description": "Retrieve a list of certificates in a certificate store. You can filter what is returned by alias, certificate type, days until expiration, and whether to include expired certificates.", + "operationId": "securityDCMListCertificates", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The API properties required to list certificates in a certificate store.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_DCMCertListRequest" + }, + "example": { + "certStoreType": "CMS", + "certStorePath": "*SYSTEM", + "certStorePassword": "passw0rd", + "filters": { + "certAlias": "*", + "certTypes": ["SERVER_CLIENT", "CA", "CSR"], + "daysUntilExpiration": 5000, + "excludeExpired": false + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "certificates": [ + { + "certAlias": "GLOBAL-MULTISAN", + "commonName": "myserver.ibm.com", + "type": "SERVER_CLIENT", + "daysBeforeExpiration": 1831, + "keyAlgorithm": "ECDSA", + "keySize": 256, + "keyStorageLocation": "SOFTWARE", + "signatureAlgorithm": "ECDSA_SHA256" + } + ] + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/dcm/certstore/changepassword": { + "post": { + "tags": ["Security Services"], + "summary": "Change digital certificate store password.", + "description": "Change digital certificate store password. For system certificate stores of type CMS, if the current password is omitted, the system stash file will be used.", + "operationId": "securityDCMChangeCertificateStorePassword", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The API properties required to change a digital certificate store password.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_DCMCertStoreChangePasswordRequest" + }, + "example": { + "certStoreType": "CMS", + "certStorePath": "*SYSTEM", + "certStorePassword": null, + "certStorePasswordNew": "myNewPassw0rd", + "daysToExpiration": 0 + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Request successful, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/dcm/appdef/trust": { + "post": { + "tags": ["Security Services"], + "summary": "Add certificate authority (CA) digital certificate to the application definition CA trust list.", + "description": "Add a CA certificate to the application definition CA trust list.", + "operationId": "securityDCMAppDefTrustCertificate", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The API properties required to add an CA to the application definition CA trust list.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_DCMCertAppDefTrustRequest" + }, + "example": { + "appDefinitionID": "myappdef", + "certAlias": "mylabel1" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Request successful, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/dcm/cert/import": { + "post": { + "tags": ["Security Services"], + "summary": "Import a digital certificate.", + "description": "Import a digital certificate. Only server/client or CA certificates can be imported. A certificate can be imported in the following formats: PKCS12, DER, or PEM.", + "operationId": "securityDCMImportCertificate", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The API properties required to import a digital certificate.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_DCMCertImportRequest" + }, + "example": { + "certStoreType": "CMS", + "certStorePath": "*SYSTEM", + "certStorePassword": "passw0rd", + "certType": "SERVER_CLIENT", + "certFormat": "PKCS12", + "certAlias": "mylabel", + "certData": "BASE64-BLOB", + "certDataPassword": "myPassw0rd" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Request successful, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + }, + "/v1/security/tls/stats": { + "get": { + "tags": ["Security Services"], + "summary": "Retrieve system transport layer security (TLS) statistics.", + "description": "The API retrieves TLS statistics. The information returned includes TLS handshake connection counts by protocol type and cipher suite on the system since the last reset for the system.", + "operationId": "securityTLSGetStatistics", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "protocolCounters": { + "TLSv13": 5, + "TLSv12": 10, + "TLSv11": 0, + "TLSv10": 0, + "SSLv3": 0 + }, + "cipherSuiteCounters": { + "AES_128_GCM_SHA256": 0, + "AES_256_GCM_SHA384": 0, + "RSA_AES_128_GCM_SHA256": 15 + } + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "404": { + "description": "The specified resource was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + } + }, + "components": { + "schemas": { + "rest4i_DCMCertRequest": { + "required": ["certAlias", "certStorePassword", "certStorePath", "certStoreType"], + "type": "object", + "properties": { + "certStoreType": { + "type": "string", + "description": "The type of the certificate store. Valid values: CMS." + }, + "certStorePath": { + "type": "string", + "description": "Path to certificate store or one of the following special values: *SYSTEM, *LOCALCA, *OBJECTSIGNING, or *SIGNATUREVERIFICATION." + }, + "certStorePassword": { + "type": "string", + "description": "The certificate store password." + }, + "certAlias": { + "type": "string", + "description": "The certificate label." + } + }, + "description": "Certificate request." + }, + "rest4i_DCMCertExportRequest": { + "required": ["certAlias", "certFormat", "certStorePassword", "certStorePath", "certStoreType"], + "type": "object", + "properties": { + "certStoreType": { + "type": "string", + "description": "The type of the certificate store. Valid values: CMS." + }, + "certStorePath": { + "type": "string", + "description": "Path to certificate store or one of the following special values: *SYSTEM, *LOCALCA, *OBJECTSIGNING, or SIGNATUREVERIFICATION." + }, + "certStorePassword": { + "type": "string", + "description": "The certificate store password." + }, + "certFormat": { + "type": "string", + "description": "The format of the certificate. Possible values: PKCS12, DER, or PEM." + }, + "certAlias": { + "type": "string", + "description": "The certificate label." + }, + "certDataPassword": { + "type": "string", + "description": "The password to access the certificate data that is returned." + } + }, + "description": "Export certificate request." + }, + "rest4i_DCMCertImportRequest": { + "required": ["certData", "certFormat", "certStorePassword", "certStorePath", "certStoreType", "certType"], + "type": "object", + "properties": { + "certStoreType": { + "type": "string", + "description": "The type of the certificate store. Valid values: CMS." + }, + "certStorePath": { + "type": "string", + "description": "Path to certificate store or one of the following special values: *SYSTEM, *LOCALCA, *OBJECTSIGNING, or SIGNATUREVERIFICATION." + }, + "certStorePassword": { + "type": "string", + "description": "The certificate store password." + }, + "certType": { + "type": "string", + "description": "The certificate type. Possible values: CA, or SERVER_CLIENT" + }, + "certFormat": { + "type": "string", + "description": "The format of the certificate. Possible values: PKCS12, DER, or PEM." + }, + "certAlias": { + "type": "string", + "description": "The certificate label." + }, + "certData": { + "type": "string", + "description": "Base64-encoded binary data object representing the certificate to be imported." + }, + "certDataPassword": { + "type": "string", + "description": "The password to access the certificate data." + } + }, + "description": "Import certificate request." + }, + "rest4i_DCMCertListRequest": { + "required": ["certStorePassword", "certStorePath", "certStoreType"], + "type": "object", + "properties": { + "certStoreType": { + "type": "string", + "description": "The type of the certificate store. Valid values: CMS." + }, + "certStorePath": { + "type": "string", + "description": "Path to certificate store or one of the following special values: *SYSTEM, *LOCALCA, *OBJECTSIGNING, or SIGNATUREVERIFICATION." + }, + "certStorePassword": { + "type": "string", + "description": "The certificate store password." + }, + "filters": { + "type": "object", + "properties": { + "certAlias": { + "type": "string", + "description": "Alias name filter. A simple generic name can be specified." + }, + "certTypes": { + "type": "array", + "description": "Certificate types filter. Valid values: CA, SERVER_CLIENT.", + "items": { + "type": "string" + } + }, + "daysUntilExpiration": { + "type": "integer", + "description": "Days until expiration filter." + }, + "excludeExpired": { + "type": "boolean", + "description": "Whether to exclude expired certificates." + } + }, + "description": "One or more combination of filters." + } + }, + "description": "List certificate request." + }, + "rest4i_DCMCertAppDefAssociateRequest": { + "required": ["appDefinitionID", "certAliases"], + "type": "object", + "properties": { + "appDefinitionID": { + "type": "string", + "description": "The application definition identifier." + }, + "certAliases": { + "type": "array", + "description": "The certificate label. Maximum of 4.", + "items": { + "type": "string" + } + } + }, + "description": "Associate certificate(s) to an application definition." + }, + "rest4i_DCMCertAppDefDisassociateRequest": { + "required": ["appDefinitionID"], + "type": "object", + "properties": { + "appDefinitionID": { + "type": "string", + "description": "The application definition identifier." + } + }, + "description": "Disassociate certificates from an application definition." + }, + "rest4i_DCMCertAppDefTrustRequest": { + "required": ["appDefinitionID", "certAlias"], + "type": "object", + "properties": { + "appDefinitionID": { + "type": "string", + "description": "The application definition identifier." + }, + "certAlias": { + "type": "string", + "description": "The certificate label." + } + }, + "description": "Add/remove a CA certificate to/from an application definition list of trusted CA certificates." + }, + "rest4i_DCMCertStoreChangePasswordRequest": { + "required": ["certStorePassword", "certStorePasswordNew", "certStorePath", "certStoreType"], + "type": "object", + "properties": { + "certStoreType": { + "type": "string", + "description": "The type of the certificate store. Valid values: CMS." + }, + "certStorePath": { + "type": "string", + "description": "Path to certificate store or one of the following special values: *SYSTEM, *LOCALCA, *OBJECTSIGNING, or SIGNATUREVERIFICATION." + }, + "certStorePassword": { + "type": "string", + "description": "The certificate store password. If field omitted or set to null, the system stash will be used." + }, + "certStorePasswordNew": { + "type": "string", + "description": "The new certificate store password." + }, + "daysToExpiration": { + "type": "integer", + "description": "Number of days before password expires.", + "default": 0 + } + }, + "description": "Change certificate store password request." + }, + "Problem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "details": { + "type": "object" + } + } + } + }, + "securitySchemes": { + "bearerHttpAuthentication": { + "type": "http", + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "Bearer [token]" + }, + "basicHttpAuthentication": { + "type": "http", + "description": "Basic authentication.", + "scheme": "basic" + } + } + } +} diff --git a/scalar/apidocs/session.json b/scalar/apidocs/session.json new file mode 100644 index 0000000..91cdba3 --- /dev/null +++ b/scalar/apidocs/session.json @@ -0,0 +1,477 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - Session Services", + "description": "Session Services provide APIs for authenticating a user and managing sessions that are tied to an authenticated user. The user must have a user profile on the IBM i server to be accessed. Once authenticated, a bearer token is returned and must be submitted on requests when invoking protected APIs in an HTTP authorization header.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "Session Services", + "description": "Session Services provide APIs for authenticating a user and managing sessions that are tied to an authenticated user." + } + ], + "paths": { + "/v1/session": { + "get": { + "tags": ["Session Services"], + "summary": "Get information about the session.", + "description": "Get information about the session. The information returned includes session settings in addition to information about any host server jobs tied to the session.", + "operationId": "sessionQuery", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + }, + { + "name": "envvars", + "in": "query", + "description": "Comma separated list of environment variables to return from the remote command host server job.", + "schema": { + "type": "string" + } + }, + { + "name": "maxjoblogrecords", + "in": "query", + "description": "Maximum number of message log records to return from the remote command host server job.", + "schema": { + "type": "integer", + "default": 0 + } + }, + { + "name": "joblogfilter", + "in": "query", + "description": "Comma separated list of message IDs. Only remote command host server job log messages that do not match the filter message IDs will be returned.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful request.", + "content": { + "application/json": { + "example": { + "sessionInfo": { + "userID": "user1", + "host": "localhost", + "expiration": "2023-04-04T00:56:35Z", + "creation": "2023-04-03T22:04:50Z", + "lastUsed": "2023-04-03T22:56:35Z", + "domain": "rest4i", + "expired": false + }, + "sessionSettings": { + "libraryList": [], + "clCommands": [], + "envVariables": {}, + "sqlDefaultSchema": null, + "sqlTreatWarningsAsErrors": false, + "sqlProperties": {}, + "sqlStatements": [] + } + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + } + ] + }, + "put": { + "tags": ["Session Services"], + "summary": "Refresh session settings.", + "description": "Refresh session settings. The settings affect the remote command and database host server jobs that are tied to the session. Refreshing session settings may result in the ending of existing host server jobs.", + "operationId": "sessionRefresh", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The settings for the session.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_SessionSettings" + }, + "example": { + "resetSettings": true, + "continueOnError": true, + "libraryList": ["lib1", "lib2"], + "clCommands": ["QSYS/CLRLIB LIB(BUILD)"], + "envVariables": { + "var1": "var1val", + "var2": "var2val" + }, + "sqlDefaultSchema": "lib1", + "sqlProperties": { + "auto commit": "true", + "ignore warnings": "01003,0100C,01567" + }, + "sqlStatements": ["SET PATH = LIB1, LIB2"] + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Successful request, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + } + ] + }, + "post": { + "tags": ["Session Services"], + "summary": "Authenticate with user credentials and return an embedded token.", + "description": "Authenticate with user credentials and return an embedded token to access different RSE APIs. On successful authentication, a token is returned in the Authorization HTTP header. The client must send this token in the Authorization HTTP header when making requests to protected RSE APIs.", + "operationId": "sessionLogin", + "requestBody": { + "description": "The user credentials to be authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_LoginCredentials" + }, + "example": { + "host": "localhost", + "userid": "user", + "password": "pwd" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful request, new resource created." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + } + }, + "delete": { + "tags": ["Session Services"], + "summary": "Logout, releasing resources tied to the session.", + "description": "Logout, releasing resources tied to the session. If a logout is not performed, it will be discarded after a period of idle time (default is 2 hours).", + "operationId": "sessionLogout", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Successful request, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + } + ] + } + } + }, + "components": { + "schemas": { + "rest4i_LoginCredentials": { + "required": ["password", "userid"], + "type": "object", + "properties": { + "host": { + "type": "string", + "description": "The IBM i server from which objects are to be accessed.", + "default": "localhost" + }, + "userid": { + "type": "string", + "description": "The user ID." + }, + "password": { + "type": "string", + "description": "The password." + } + }, + "description": "The login credentials." + }, + "rest4i_SessionSettings": { + "type": "object", + "properties": { + "resetSettings": { + "type": "boolean", + "description": "Replace existing sessions attributes. A value of false will merge settings in request with existing session settings.", + "default": false + }, + "continueOnError": { + "type": "boolean", + "description": "Continue with session processing if an error occurs.", + "default": false + }, + "libraryList": { + "type": "array", + "description": "Library to be added to library list of the remote command host server job tied to session.", + "items": { + "type": "string" + } + }, + "clCommands": { + "type": "array", + "description": "CL command to be run in remote command host server job tied to session.", + "items": { + "type": "string" + } + }, + "envVariables": { + "type": "object", + "description": "Environment variable to set in remote command host server job tied to session.", + "additionalProperties": { + "type": "string" + } + }, + "sqlDefaultSchema": { + "type": "string", + "description": "Default SQL schema to use when running SQL statements in database host server job." + }, + "sqlTreatWarningsAsErrors": { + "type": "boolean", + "description": "Treat SQL warnings as errors.", + "default": false + }, + "sqlProperties": { + "type": "object", + "description": "Java toolbox JDBC property.", + "additionalProperties": { + "type": "string" + } + }, + "sqlStatements": { + "type": "array", + "description": "SQL statement to be run in database host server job tied to session.", + "items": { + "type": "string" + } + } + }, + "description": "The settings for the session." + }, + "Problem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "details": { + "type": "object" + } + } + } + }, + "securitySchemes": { + "bearerHttpAuthentication": { + "type": "http", + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "Bearer [token]" + }, + "basicHttpAuthentication": { + "type": "http", + "description": "Basic authentication.", + "scheme": "basic" + } + } + } +} diff --git a/scalar/apidocs/sql.json b/scalar/apidocs/sql.json new file mode 100644 index 0000000..4f0ef73 --- /dev/null +++ b/scalar/apidocs/sql.json @@ -0,0 +1,207 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "REST4i - SQL Services", + "description": "SQL Services provide APIs associated with performing SQL operations.", + "version": "1.0.7-rest4i", + "contact": { + "name": "API Support", + "url": "https://github.com/rest4i" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "/rest4i/api" + } + ], + "tags": [ + { + "name": "SQL Services", + "description": "SQL Services provide APIs associated with performing SQL operations." + } + ], + "paths": { + "/v1/sql": { + "put": { + "tags": ["SQL Services"], + "summary": "Run a SQL statement on the server.", + "description": "Run a SQL statement on the server. If SQL statement fails, any messages relating to the error is returned. SQL state information is returned only if errors are detected. You have the option of indicating whether the state information is returned on all responses. By default, if a SQL statement is run successfully and there is no result set to return, no data is returned in the response.", + "operationId": "runSQLStatements", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "The authorization HTTP header.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The SQL statement to be run on the server.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rest4i_SQLRequest" + }, + "example": { + "alwaysReturnSQLStateInformation": false, + "treatWarningsAsErrors": false, + "sqlStatement": "select * from QIWS.QCUSTCDT" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "SQL statements(s) issued.", + "content": { + "application/json": { + "example": { + "resultSet": [ + { + "CUSNUM": 938472, + "LSTNAM": "Henning", + "INIT": "G K", + "STREET": "4859 Elm Ave", + "CITY": "Dallas", + "STATE": "TX", + "ZIPCOD": 75217, + "CDTLMT": 5000, + "CHGCOD": 3, + "BALDUE": 37, + "CDTDUE": 0 + }, + { + "CUSNUM": 839283, + "LSTNAM": "Jones", + "INIT": "B D", + "STREET": "21B NW 135 St", + "CITY": "Clay", + "STATE": "NY", + "ZIPCOD": 13041, + "CDTLMT": 400, + "CHGCOD": 1, + "BALDUE": 100, + "CDTDUE": 0 + } + ] + } + } + } + }, + "204": { + "description": "SQL statement(s) issued successfully, no content." + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "401": { + "description": "Unauthorized request was made.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "403": { + "description": "The request is forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + }, + "500": { + "description": "Unable to process the request due to an internal server error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + } + } + }, + "security": [ + { + "bearerHttpAuthentication": [] + }, + { + "basicHttpAuthentication": [] + } + ] + } + } + }, + "components": { + "schemas": { + "rest4i_SQLRequest": { + "required": ["sqlStatement"], + "type": "object", + "properties": { + "alwaysReturnSQLStateInformation": { + "type": "boolean", + "description": "Always return SQL state information. Default value is whatever has been set for the session." + }, + "treatWarningsAsErrors": { + "type": "boolean", + "description": "Treat SQL warnings as errors. Default value is whatever has been set for the session." + }, + "sqlStatement": { + "type": "string", + "description": "The SQL statement to be run." + } + }, + "description": "SQL request to run." + }, + "Problem": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "format": "int32" + }, + "details": { + "type": "object" + } + } + } + }, + "securitySchemes": { + "bearerHttpAuthentication": { + "type": "http", + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "Bearer [token]" + }, + "basicHttpAuthentication": { + "type": "http", + "description": "Basic authentication.", + "scheme": "basic" + } + } + } +} diff --git a/scalar/config.json b/scalar/config.json new file mode 100644 index 0000000..b030c26 --- /dev/null +++ b/scalar/config.json @@ -0,0 +1,41 @@ +{ + "id": "scalar", + "name": "scalar", + "urn": "scalar:_user", + "available": true, + "categories": [ + "utilities" + ], + "description": "Custom application: scalar", + "short_desc": "User-created custom app", + "author": "User", + "source": "", + "website": "", + "exposable": true, + "no_gui": false, + "supported_architectures": [ + "amd64", + "arm64" + ], + "tipi_version": 1, + "version": "1.0.0", + "dynamic_config": true, + "deprecated": false, + "force_expose": false, + "generate_vapid_keys": false, + "form_fields": [ + { + "type": "text", + "label": "API Config", + "env_variable": "FORM_API_REFERENCE_CONFIG", + }, + "type": "text", + "label": "Cors Proxy Url", + "env_variable": "FORM_PROXY_URL", + "default": "https://cors.alexzaw.dev?url=" + ], + "https": false, + "created_at": 1767952070034, + "updated_at": 1767952070034, + "force_pull": false +} diff --git a/scalar/docker-compose.json b/scalar/docker-compose.json new file mode 100644 index 0000000..a9f5010 --- /dev/null +++ b/scalar/docker-compose.json @@ -0,0 +1,36 @@ +{ + "services": [ + { + "_id": "service-9v11i7b", + "name": "api-reference", + "image": "scalarapi/api-reference:latest", + "isMain": true, + "internalPort": "8080", + "volumes": [ + { + "containerPath": "/docs", + "hostPath": "/etc/runtipi/apps/_user/scalar/apidocs", + "readOnly": false, + "shared": false, + "private": false + } + ], + "hostname": "scalar-api", + "environment": [ + { + "key": "API_REFERENCE_CONFIG", + "value": "{FORM_API_REFERENCE_CONFIG}" + } + ], + "addPorts": [ + { + "containerPort": "8090", + "hostPort": "8090", + "tcp": true, + "udp": true + } + ] + } + ], + "schemaVersion": 2 +} diff --git a/scalar/docker-compose.yml b/scalar/docker-compose.yml new file mode 100644 index 0000000..c7c2b72 --- /dev/null +++ b/scalar/docker-compose.yml @@ -0,0 +1,45 @@ +services: + api-reference: + image: scalarapi/api-reference:latest + restart: unless-stopped + networks: + scalar__user_network: + gw_priority: 0 + tipi_main_network: + gw_priority: 1 + environment: + API_REFERENCE_CONFIG: "{API_REFERENCE_CONFIG}" + ports: + - 8090:8090/tcp + - 8090:8090/udp + - ${APP_PORT}:8080 + volumes: + - /etc/runtipi/apps/_user/scalar/apidocs:/docs + labels: + generated: true + traefik.enable: true + traefik.docker.network: runtipi_tipi_main_network + traefik.http.middlewares.scalar-_user-web-redirect.redirectscheme.scheme: https + traefik.http.services.scalar-_user.loadbalancer.server.port: "8080" + traefik.http.routers.scalar-_user-insecure.rule: Host(`${APP_DOMAIN}`) + traefik.http.routers.scalar-_user-insecure.entrypoints: web + traefik.http.routers.scalar-_user-insecure.service: scalar-_user + traefik.http.routers.scalar-_user-insecure.middlewares: scalar-_user-web-redirect + traefik.http.routers.scalar-_user.rule: Host(`${APP_DOMAIN}`) + traefik.http.routers.scalar-_user.entrypoints: websecure + traefik.http.routers.scalar-_user.service: scalar-_user + traefik.http.routers.scalar-_user.tls.certresolver: myresolver + runtipi.managed: true + runtipi.appurn: scalar:_user + hostname: scalar-api +networks: + tipi_main_network: + name: runtipi_tipi_main_network + external: true + scalar__user_network: + name: scalar__user_network + external: false + ipam: + config: + - subnet: 10.128.22.0/24 + diff --git a/scalar/metadata/description.md b/scalar/metadata/description.md new file mode 100644 index 0000000..604753e --- /dev/null +++ b/scalar/metadata/description.md @@ -0,0 +1,10 @@ +--- +name: scalar +short_desc: User-created custom app +version: 1.0.0 +--- + +# scalar + +This is a user-created custom application. + diff --git a/scalar/metadata/logo.jpg b/scalar/metadata/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..123c5c381ddaa1438401a2919623b6bfc0f8937a GIT binary patch literal 3023042 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�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%0D&L^GGhCCRM{{{sRlbpAF_gLXuoe$GY-}O4m7gB zQAZsmBSy-IlarGp-zRS*K!5;&@daeW_V${x;rR6#`Ut7*`;Di){_B%%=ss|~(FBe> z@<jz8|ES*fwztWMV`ju3{_uxkZiu50AV7dXs{%6O@OxC*u+@?bcJMxD2-h}z-{xW# ztN|P-sKCB``>MkZJ4{9#F(W?m$Rk1Thl3FyK!8BI0y5&@drjG}-C7KJ^jz|QaSq;p zC0K>*0mlj}@YlcowL0|BLuJGvGoox5whkSQ009EA1Z2e7_o%XAtZEE%o)WTzYn;7r zb2AE4{f^R>z+e9Im+DP#dXtPeU`G7WkABqFt#Did1PBm_ARr^oyw{WsBNSlZQ{(;`BO}g|5g&c@(H8U_k^lh$1Y!%wh*S5dvSI8h40FDc zGKFiM+PArygsFZ!kMtof*8+KKFVa{7( zws6h)XUj0v?pr5nsuOvSFNha~1*w2y`t_Vn&n=yRN!0=Pxl^xaRybahU3Nl-L5hckeDb zBg%%cE6(`{5FpU4KuH-ek4i_D0!Vca@%HUb0) zq#{s4MwAUxskSiVC^1{O=KM2pnCf?wt^}Ta_SvE`qHNezr8zGF0tC_!=yOJt4b!Nx zkfV^5^3O^o-oubR;8@)V{NWFOC?X@uhTYVcvl1XcAT@zLW<=RAwJHlUo)WW#YtBCt zhpB!?>003TzyE!&GoozRb%i-U0RjZl66jM#lnv9Wt&pRUmGaL@CEml3J>Xc~3jFSO zzw2d2lnuMBD`zJ_fIzAOeaMKiVXD;?W?Us^3)h@~CJs~mj*^PNu3fu&l@VpbR4U6j z2oNBUra;aaQ8rAorb3QNR?0ssm3R+B_JCuhA@J*8|GF0$Q8rAYo{WM30RpKDy3#M@B<{0D)Wta>$6XVJ_7aW}GEv3)h@~CJs~mj*_at z(@#I0RYsHzQ>`T9B0zvZ9s((6MA;cD0Tj1wE|9O%bQ8rAwZj6oq0Rp)Sq>vG1!(6K;%(zR; z7Opw}OdO{A9VHimC!TmBsf;Ka=2AJvNq_)>JO#SUh_YdxH4}2wvQqw8sl;cEhOW-Fz`AH`kQ8vu0PK=fS0Rnvy zh>;Ox!@g8YnCmDpTe#-@GjW*eca&TO9)JAts2Nc<%(YUCn*ad<{Sas?Bi?`i{gpMY zA2ky4DrBYnvr>uoFk}xnR-OWnJ@#0XjA-4=vp$TP009Dh69}CV>ul%`f$0QF%oeUW z|4ckxEqkmyfjYaj+0U|Jc{SyV2@oKVvOt{?k2vCpvDUstixyR~Vaj*8`?0mTw{qZ>aefHVv{qKMOSZn2Ub-r=q##AoRI0z6RK!Cuj0=stYs+KKVHuSo; zV8Mdw+H0?^_U+sE>al!5U^;>6_C+3(>r$_Q>1x?yuTysrz>ZzwTu7BsBfBt_yFMQ#J7qYl6gX~^ly8V&I zts`HfAW)`te$@Q=@c)`mP5y z;uqD14I8TZ@1WLye{|>0oio1P69k$Bn)XVblM_>|fu@@FT-gHkXNmRNc;bmC4xAI0 zE?rvPamO8H!?}6_1PBlyK!Ct20;%r*jY~FdssYVQU=V@&XEY`zCaUGjmsj=QuldMF zKJuUc)nC_--Ezw<)gS-($3YI|H3SF{AV7csfg%K&_TZkA(Ne8}BAU}h5+Fc;009C7 z2oQ)Ykm~;5xMb6&8i?E`PDg+M0RjXF5FkLH2!Wi*xjWYeY^h}RX%Fr>87>K!5-N0t5&Uh%Aum{@=J{ z)214T+$K&(fB*pk1PBlyK%fYLraidlWVBRkpor#lkpu`3AV7cs0RjXf3#7XLH!j(< zsRkmqiPI4vK!5-N0t5&UC_U za+^3E0RjXF5FkK+0D&R|n)cwHlhIPGfg+mIMG_!DfB*pk1PBm_ERgE{-?(JcrW%Oc zCQe6y009C72oNAZpa_AcJ-Fv&v{Y-Lh~{*W1PBlyK!5-N0t6xpq`Ln%F4?rH1|qkK z(-9y*fB*pk1PBl)LZE36?l~DP)fy?*ENTHf^eb$Zg_u z1PBlyK!5-N0tAW>Xxf8&PDV?$28w7-7fFBs0RjXF5FkJxvOuc)f8&x(n`$6(n>ZZ- z0t5&UAV7csfg%K&_TZkA(Ne8}BAU}h5+Fc;009C72oQ)Ykm~;5xMb6&8i?E`PDg+M z0RjXF5FkLH2!Wi*xjWYeY^h}RX%Fr>87>K!5-N0t5&Uh%7L7?%XQX3}9UH>2x))V8MdOUE*{E2oNAZ zfB*pk1j-OtxNxC-_&Pakx*V43V@sASDWf@EB>@5i2oNAZfB=EW0*eB>rAuW*pCM*Z16N#eMMquYv;+tcAV7cs0RjZd5ZJnPYZloc z#m}>hDc+0P z;x&OY5gc&xmrpH9 zj}Rb0fB*pk1PBly(3!wL|M}1A`s=T+7A{;UC;B`wN)4QM-g(uJfBfUlYTk(n5FkK+ z009C72oPu%*u8sqH8C+!opsh(Q8Gf`J_~a_&7XlDd+f2*<(FSxZQHi3xez@|fB*pk z1PBlyK!89}0{iyutF~|7UTxgCv0AfcO?A~(S5@`zwDCU%gVsR(dve#VU0XfyzysCJ zoja4NZ-XH~fB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U VAV7cs0RjXF5FkK+0D*i3{trFQ8MXib literal 0 HcmV?d00001