166 lines
7.3 KiB
C#
166 lines
7.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data.Odbc;
|
|
using System.Linq;
|
|
using AS400API.Auth;
|
|
using AS400API.Infrastructure;
|
|
using Dapper;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace AS400API.Endpoints;
|
|
|
|
public static class As400Endpoints
|
|
{
|
|
public static RouteGroupBuilder MapAs400Endpoints(this RouteGroupBuilder group)
|
|
{
|
|
group.MapGet("/v1/as400/query", async (string sql, OdbcConnection conn, ILoggerFactory loggerFactory) =>
|
|
{
|
|
var logger = loggerFactory.CreateLogger("As400QueryEndpoint");
|
|
try
|
|
{
|
|
await conn.OpenAsync();
|
|
if (string.IsNullOrWhiteSpace(sql))
|
|
{
|
|
logger.LogWarning("Missing sql query parameter when executing AS/400 query");
|
|
return Results.BadRequest(new { error = "Query parameter 'sql' is required." });
|
|
}
|
|
|
|
logger.LogInformation("Executing AS/400 query with SQL length {Length}", sql?.Length ?? 0);
|
|
var rows = (await conn.QueryAsync(sql!)).ToList();
|
|
var formatted = rows.ToCamelCaseDictionaries().ToList();
|
|
logger.LogInformation("AS/400 query returned {RowCount} rows", formatted.Count);
|
|
return Results.Ok(formatted);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "AS/400 query failed");
|
|
return Results.Problem($"Query failed: {ex.Message}");
|
|
}
|
|
})
|
|
.RequireAuthorization(AuthPolicies.RequireAdmin)
|
|
.WithSummary("Execute a read-only SQL select against AS/400 (admin only)")
|
|
.Produces(200)
|
|
.ProducesProblem(500);
|
|
|
|
group.MapGet("/v1/as400/databases", async (OdbcConnection conn, ILoggerFactory loggerFactory) =>
|
|
{
|
|
var logger = loggerFactory.CreateLogger("As400DatabasesEndpoint");
|
|
try
|
|
{
|
|
await conn.OpenAsync();
|
|
const string sqlQuery = "SELECT SCHEMA_NAME AS LIBRARY_NAME FROM QSYS2.SYSSCHEMAS ORDER BY LIBRARY_NAME";
|
|
var schemas = (await conn.QueryAsync(sqlQuery)).ToList();
|
|
var formatted = schemas.ToCamelCaseDictionaries().ToList();
|
|
logger.LogInformation("Fetched {Count} AS/400 schemas", formatted.Count);
|
|
return Results.Ok(formatted);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Failed to list AS/400 schemas");
|
|
return Results.Problem($"Failed to list schemas: {ex.Message}");
|
|
}
|
|
})
|
|
.RequireAuthorization(AuthPolicies.RequireOperator)
|
|
.WithSummary("List available libraries/schemas for AS/400 accounts");
|
|
|
|
group.MapGet("/v1/as400/tables", async (string libraryName, OdbcConnection conn, ILoggerFactory loggerFactory) =>
|
|
{
|
|
var logger = loggerFactory.CreateLogger("As400TablesEndpoint");
|
|
try
|
|
{
|
|
await conn.OpenAsync();
|
|
if (string.IsNullOrWhiteSpace(libraryName))
|
|
{
|
|
logger.LogWarning("Missing libraryName query parameter when listing tables");
|
|
return Results.BadRequest(new { error = "Query parameter 'libraryName' is required." });
|
|
}
|
|
|
|
const string sqlQuery = """
|
|
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE
|
|
FROM QSYS2.SYSTABLES
|
|
WHERE TABLE_SCHEMA = @libraryName
|
|
ORDER BY TABLE_NAME
|
|
""";
|
|
|
|
var tables = (await conn.QueryAsync(sqlQuery, new { libraryName })).ToList();
|
|
logger.LogInformation("Fetched {Count} tables for AS/400 library {Library}", tables.Count, libraryName);
|
|
var formatted = tables.ToCamelCaseDictionaries().ToList();
|
|
return Results.Ok(formatted);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Failed to list tables for AS/400 library {Library}", libraryName);
|
|
return Results.Problem($"Failed to list tables: {ex.Message}");
|
|
}
|
|
})
|
|
.RequireAuthorization(AuthPolicies.RequireOperator)
|
|
.WithSummary("List tables for a specific AS/400 library (schema)");
|
|
|
|
group.MapGet("/v1/as400/table-structure", async (string libraryName, string tableName, OdbcConnection conn, ILoggerFactory loggerFactory) =>
|
|
{
|
|
var logger = loggerFactory.CreateLogger("As400TableStructureEndpoint");
|
|
try
|
|
{
|
|
await conn.OpenAsync();
|
|
if (string.IsNullOrWhiteSpace(libraryName) || string.IsNullOrWhiteSpace(tableName))
|
|
{
|
|
logger.LogWarning("Missing libraryName or tableName query parameter when requesting table structure");
|
|
return Results.BadRequest(new { error = "Query parameters 'libraryName' and 'tableName' are required." });
|
|
}
|
|
|
|
const string sqlQuery = """
|
|
SELECT COLUMN_NAME, SYSTEM_COLUMN_NAME, DATA_TYPE, LENGTH, NUMERIC_SCALE, IS_NULLABLE
|
|
FROM QSYS2.SYSCOLUMNS2
|
|
WHERE TABLE_SCHEMA = ?
|
|
AND TABLE_NAME = ?
|
|
ORDER BY ORDINAL_POSITION
|
|
""";
|
|
|
|
await using var command = conn.CreateCommand();
|
|
command.CommandText = sqlQuery;
|
|
|
|
var libraryParameter = command.CreateParameter();
|
|
libraryParameter.Value = libraryName.ToUpper();
|
|
command.Parameters.Add(libraryParameter);
|
|
|
|
var tableParameter = command.CreateParameter();
|
|
tableParameter.Value = tableName.ToUpper();
|
|
command.Parameters.Add(tableParameter);
|
|
|
|
var columns = new List<IDictionary<string, object>>();
|
|
await using (var reader = await command.ExecuteReaderAsync())
|
|
{
|
|
while (await reader.ReadAsync())
|
|
{
|
|
var row = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
|
for (var i = 0; i < reader.FieldCount; i++)
|
|
{
|
|
row[reader.GetName(i)] = reader.GetNormalizedValue(i)!;
|
|
}
|
|
|
|
columns.Add(row);
|
|
}
|
|
}
|
|
|
|
// var tables = (await conn.QueryAsync(sqlQuery, new { libraryName })).ToList();
|
|
// var formatted = tables.ToList();
|
|
|
|
logger.LogInformation("Fetched {Count} columns for {Library}/{Table}", columns.Count, libraryName, tableName);
|
|
var formatted = columns.Select(dict => dict.ToCamelCaseDictionary()).ToList();
|
|
return Results.Ok(formatted);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Failed to list columns for {Library}/{Table}", libraryName, tableName);
|
|
return Results.Problem($"Failed to list columns: {ex.Message}");
|
|
}
|
|
})
|
|
// .RequireAuthorization(AuthPolicies.RequireOperator)
|
|
.WithSummary("Describe the structure of a table (columns) for an AS/400 library");
|
|
|
|
return group;
|
|
}
|
|
}
|