Init Commit
Works: -Databases --MySql/Mariadb --MongoDB --Postgres -Formatting -JSON -XML -HTML Todo: -Databases --SQLite --Firebird
This commit is contained in:
25
src/RestAPI/RestAPI.sln
Normal file
25
src/RestAPI/RestAPI.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30615.102
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestAPI", "RestAPI\RestAPI.csproj", "{3929B199-3AEB-41BA-993B-DA1AF2A73CA3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {972DCCF9-29C3-427D-82B0-1DA966964D24}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
7
src/RestAPI/RestAPI/.env.default
Normal file
7
src/RestAPI/RestAPI/.env.default
Normal file
@@ -0,0 +1,7 @@
|
||||
DATASOURCE=
|
||||
HOST=
|
||||
HOSTPORT=
|
||||
USERNAME=
|
||||
PASSWORD=
|
||||
DATABASE=
|
||||
BLACKLISTEDFIELDS=
|
||||
115
src/RestAPI/RestAPI/Clients/FirebirdClient.cs
Normal file
115
src/RestAPI/RestAPI/Clients/FirebirdClient.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using FirebirdSql.Data.FirebirdClient;
|
||||
using RestAPI.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RestAPI.Clients
|
||||
{
|
||||
public class FirebirdClient : IDatabaseClient
|
||||
{
|
||||
FbConnection connection;
|
||||
IEnumerable<string> Tables;
|
||||
|
||||
public FirebirdClient(string connectionString)
|
||||
{
|
||||
connection = new FbConnection(connectionString);
|
||||
}
|
||||
|
||||
public async Task CloseAsync()
|
||||
{
|
||||
if (connection.State != System.Data.ConnectionState.Closed)
|
||||
{
|
||||
await connection.CloseAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DoesTableExistAsync(string tableName)
|
||||
{
|
||||
if (Tables == null || !Tables.Any())
|
||||
{
|
||||
await GetTablesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return Tables.Select(x => x.ToLowerInvariant()).Contains(tableName.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public async Task<object> GetTableAsync(string tableName, string[] blacklistedFields = null)
|
||||
{
|
||||
if (blacklistedFields == null)
|
||||
{
|
||||
blacklistedFields = new string[0];
|
||||
}
|
||||
|
||||
List<List<KeyValuePair<string, string>>> content = null;
|
||||
|
||||
FbCommand cmd = new FbCommand($"SELECT * FROM `{tableName}`", connection);
|
||||
|
||||
var reader = await cmd.ExecuteReaderAsync();
|
||||
|
||||
if (reader.HasRows)
|
||||
{
|
||||
content = new List<List<KeyValuePair<string, string>>>();
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
var colSchema = reader.GetColumnSchema();
|
||||
|
||||
List<KeyValuePair<string, string>> obj = new List<KeyValuePair<string, string>>();
|
||||
|
||||
for (int col = 0; col < colSchema.Count; col++)
|
||||
{
|
||||
if (!blacklistedFields.Contains(colSchema[col].ColumnName.ToLowerInvariant()))
|
||||
{
|
||||
obj.Add(
|
||||
new KeyValuePair<string, string>(
|
||||
colSchema[col].ColumnName,
|
||||
Convert.ToString(reader[col])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
content.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
await reader.CloseAsync();
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetTablesAsync()
|
||||
{
|
||||
if (Tables == null || !Tables.Any())
|
||||
{
|
||||
List<string> TableNames = new List<string>();
|
||||
|
||||
FbCommand command = new FbCommand($"SHOW TABLES FROM `{connection.Database}`", connection);
|
||||
using (var reader = await command.ExecuteReaderAsync())
|
||||
{
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
TableNames.Add(reader.GetString(0));
|
||||
}
|
||||
|
||||
await reader.CloseAsync();
|
||||
}
|
||||
|
||||
Tables = TableNames;
|
||||
}
|
||||
|
||||
return Tables;
|
||||
}
|
||||
|
||||
public async Task OpenAsync()
|
||||
{
|
||||
if (connection.State != System.Data.ConnectionState.Open)
|
||||
{
|
||||
await connection.OpenAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
src/RestAPI/RestAPI/Clients/MongoDbClient.cs
Normal file
109
src/RestAPI/RestAPI/Clients/MongoDbClient.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Core.Clusters;
|
||||
using RestAPI.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RestAPI.Clients
|
||||
{
|
||||
public class MongoDbClient : IDatabaseClient
|
||||
{
|
||||
string database;
|
||||
string connectionString;
|
||||
MongoClient connection;
|
||||
IEnumerable<string> Tables;
|
||||
|
||||
public MongoDbClient(string connectionString, string database)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
this.database = database;
|
||||
connection = new MongoClient(connectionString);
|
||||
}
|
||||
|
||||
public Task CloseAsync()
|
||||
{
|
||||
connection.Cluster.Dispose();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<bool> DoesTableExistAsync(string tableName)
|
||||
{
|
||||
if (Tables == null || !Tables.Any())
|
||||
{
|
||||
await GetTablesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return Tables.Select(x => x.ToLowerInvariant()).Contains(tableName.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public async Task<object> GetTableAsync(string tableName, string[] blacklistedFields = null)
|
||||
{
|
||||
if (blacklistedFields == null)
|
||||
{
|
||||
blacklistedFields = new string[0];
|
||||
}
|
||||
|
||||
List<List<KeyValuePair<string, string>>> content = null;
|
||||
|
||||
var database = connection.GetDatabase(this.database);
|
||||
|
||||
var collection = database.GetCollection<dynamic>(tableName);
|
||||
|
||||
var entries = await (await collection.FindAsync(_ => true)).ToListAsync();
|
||||
|
||||
if (entries.Count > 0)
|
||||
{
|
||||
content = new List<List<KeyValuePair<string, string>>>();
|
||||
|
||||
foreach (dynamic entry in entries)
|
||||
{
|
||||
var res = new RouteValueDictionary(entry);
|
||||
|
||||
foreach (var blacklisted in blacklistedFields)
|
||||
{
|
||||
if (res.ContainsKey(blacklisted))
|
||||
{
|
||||
res.Remove(blacklisted);
|
||||
}
|
||||
}
|
||||
|
||||
List<KeyValuePair<string, string>> obj = new List<KeyValuePair<string, string>>();
|
||||
|
||||
foreach (var ent in res)
|
||||
{
|
||||
obj.Add(new KeyValuePair<string, string>(ent.Key, Convert.ToString(ent.Value)));
|
||||
}
|
||||
|
||||
content.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetTablesAsync()
|
||||
{
|
||||
if (Tables == null || !Tables.Any())
|
||||
{
|
||||
var database = connection.GetDatabase(this.database);
|
||||
Tables = await (await database.ListCollectionNamesAsync()).ToListAsync();
|
||||
}
|
||||
|
||||
return Tables;
|
||||
}
|
||||
|
||||
public Task OpenAsync()
|
||||
{
|
||||
if (connection.Cluster.Description.State != ClusterState.Connected)
|
||||
{
|
||||
connection = new MongoClient(connectionString);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
114
src/RestAPI/RestAPI/Clients/MySqlClient.cs
Normal file
114
src/RestAPI/RestAPI/Clients/MySqlClient.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using MySqlConnector;
|
||||
using RestAPI.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RestAPI.Clients
|
||||
{
|
||||
public class MySqlClient : IDatabaseClient
|
||||
{
|
||||
MySqlConnection connection;
|
||||
IEnumerable<string> Tables;
|
||||
|
||||
public MySqlClient(string connectionString)
|
||||
{
|
||||
connection = new MySqlConnection(connectionString);
|
||||
}
|
||||
|
||||
public async Task CloseAsync()
|
||||
{
|
||||
if (connection.State != System.Data.ConnectionState.Closed)
|
||||
{
|
||||
await connection.CloseAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DoesTableExistAsync(string tableName)
|
||||
{
|
||||
if (Tables == null || !Tables.Any())
|
||||
{
|
||||
await GetTablesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return Tables.Select(x => x.ToLowerInvariant()).Contains(tableName.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public async Task<object> GetTableAsync(string tableName, string[] blacklistedFields = null)
|
||||
{
|
||||
if (blacklistedFields == null)
|
||||
{
|
||||
blacklistedFields = new string[0];
|
||||
}
|
||||
|
||||
List<List<KeyValuePair<string, string>>> content = null;
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand($"SELECT * FROM `{tableName}`", connection);
|
||||
|
||||
var reader = await cmd.ExecuteReaderAsync();
|
||||
|
||||
if (reader.HasRows)
|
||||
{
|
||||
content = new List<List<KeyValuePair<string, string>>>();
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
var colSchema = await reader.GetColumnSchemaAsync();
|
||||
|
||||
List<KeyValuePair<string, string>> obj = new List<KeyValuePair<string, string>>();
|
||||
|
||||
for (int col = 0; col < colSchema.Count; col++)
|
||||
{
|
||||
if (!blacklistedFields.Contains(colSchema[col].ColumnName.ToLowerInvariant()))
|
||||
{
|
||||
obj.Add(
|
||||
new KeyValuePair<string, string>(
|
||||
colSchema[col].ColumnName,
|
||||
Convert.ToString(reader[col])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
content.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
await reader.CloseAsync();
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetTablesAsync()
|
||||
{
|
||||
if (Tables == null || !Tables.Any())
|
||||
{
|
||||
List<string> TableNames = new List<string>();
|
||||
|
||||
MySqlCommand command = new MySqlCommand($"SHOW TABLES FROM `{connection.Database}`", connection);
|
||||
using (var reader = await command.ExecuteReaderAsync())
|
||||
{
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
TableNames.Add(reader.GetString(0));
|
||||
}
|
||||
|
||||
await reader.CloseAsync();
|
||||
}
|
||||
|
||||
Tables = TableNames;
|
||||
}
|
||||
|
||||
return Tables;
|
||||
}
|
||||
|
||||
public async Task OpenAsync()
|
||||
{
|
||||
if (connection.State != System.Data.ConnectionState.Open)
|
||||
{
|
||||
await connection.OpenAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
src/RestAPI/RestAPI/Clients/NpgsqlClient.cs
Normal file
117
src/RestAPI/RestAPI/Clients/NpgsqlClient.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using Npgsql;
|
||||
using RestAPI.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RestAPI.Clients
|
||||
{
|
||||
public class NpgsqlClient : IDatabaseClient
|
||||
{
|
||||
NpgsqlConnection connection;
|
||||
IEnumerable<string> Tables;
|
||||
|
||||
public NpgsqlClient(string connectionString)
|
||||
{
|
||||
connection = new NpgsqlConnection(connectionString);
|
||||
}
|
||||
|
||||
public async Task CloseAsync()
|
||||
{
|
||||
if (connection.State != System.Data.ConnectionState.Closed)
|
||||
{
|
||||
await connection.CloseAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DoesTableExistAsync(string tableName)
|
||||
{
|
||||
if (Tables == null || !Tables.Any())
|
||||
{
|
||||
await GetTablesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return Tables.Select(x => x.ToLowerInvariant()).Contains(tableName.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public async Task<object> GetTableAsync(string tableName, string[] blacklistedFields = null)
|
||||
{
|
||||
if (blacklistedFields == null)
|
||||
{
|
||||
blacklistedFields = new string[0];
|
||||
}
|
||||
|
||||
List<List<KeyValuePair<string, string>>> content = null;
|
||||
|
||||
NpgsqlCommand cmd = new NpgsqlCommand($"SELECT * FROM {tableName}", connection);
|
||||
|
||||
var reader = await cmd.ExecuteReaderAsync();
|
||||
|
||||
if (reader.HasRows)
|
||||
{
|
||||
content = new List<List<KeyValuePair<string, string>>>();
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
var colSchema = await reader.GetColumnSchemaAsync();
|
||||
|
||||
List<KeyValuePair<string, string>> obj = new List<KeyValuePair<string, string>>();
|
||||
|
||||
for (int col = 0; col < colSchema.Count; col++)
|
||||
{
|
||||
if (!blacklistedFields.Contains(colSchema[col].ColumnName.ToLowerInvariant()))
|
||||
{
|
||||
obj.Add(
|
||||
new KeyValuePair<string, string>(
|
||||
colSchema[col].ColumnName,
|
||||
Convert.ToString(reader[col])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
content.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
await reader.CloseAsync();
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetTablesAsync()
|
||||
{
|
||||
if (Tables == null || !Tables.Any())
|
||||
{
|
||||
List<string> TableNames = new List<string>();
|
||||
|
||||
NpgsqlCommand command = new NpgsqlCommand("SELECT * FROM information_schema.tables", connection);
|
||||
using (var reader = await command.ExecuteReaderAsync())
|
||||
{
|
||||
if (reader.HasRows)
|
||||
{
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
TableNames.Add(Convert.ToString($"{reader["table_schema"]}.{reader["table_name"]}"));
|
||||
}
|
||||
}
|
||||
|
||||
await reader.CloseAsync();
|
||||
}
|
||||
|
||||
Tables = TableNames;
|
||||
}
|
||||
|
||||
return Tables;
|
||||
}
|
||||
|
||||
public async Task OpenAsync()
|
||||
{
|
||||
if (connection.State != System.Data.ConnectionState.Open)
|
||||
{
|
||||
await connection.OpenAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
115
src/RestAPI/RestAPI/Clients/SQLiteClient.cs
Normal file
115
src/RestAPI/RestAPI/Clients/SQLiteClient.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
using RestAPI.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RestAPI.Clients
|
||||
{
|
||||
public class SQLiteClient : IDatabaseClient
|
||||
{
|
||||
SqliteConnection connection;
|
||||
IEnumerable<string> Tables;
|
||||
|
||||
public SQLiteClient(string connectionString)
|
||||
{
|
||||
connection = new SqliteConnection(connectionString);
|
||||
}
|
||||
|
||||
public async Task CloseAsync()
|
||||
{
|
||||
if (connection.State != System.Data.ConnectionState.Closed)
|
||||
{
|
||||
await connection.CloseAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DoesTableExistAsync(string tableName)
|
||||
{
|
||||
if (Tables == null || !Tables.Any())
|
||||
{
|
||||
await GetTablesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return Tables.Select(x => x.ToLowerInvariant()).Contains(tableName.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public async Task<object> GetTableAsync(string tableName, string[] blacklistedFields = null)
|
||||
{
|
||||
if (blacklistedFields == null)
|
||||
{
|
||||
blacklistedFields = new string[0];
|
||||
}
|
||||
|
||||
List<List<KeyValuePair<string, string>>> content = null;
|
||||
|
||||
SqliteCommand cmd = new SqliteCommand($"SELECT * FROM `{tableName}`", connection);
|
||||
|
||||
var reader = await cmd.ExecuteReaderAsync();
|
||||
|
||||
if (reader.HasRows)
|
||||
{
|
||||
content = new List<List<KeyValuePair<string, string>>>();
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
var colSchema = reader.GetColumnSchema();
|
||||
|
||||
List<KeyValuePair<string, string>> obj = new List<KeyValuePair<string, string>>();
|
||||
|
||||
for (int col = 0; col < colSchema.Count; col++)
|
||||
{
|
||||
if (!blacklistedFields.Contains(colSchema[col].ColumnName.ToLowerInvariant()))
|
||||
{
|
||||
obj.Add(
|
||||
new KeyValuePair<string, string>(
|
||||
colSchema[col].ColumnName,
|
||||
Convert.ToString(reader[col])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
content.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
await reader.CloseAsync();
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetTablesAsync()
|
||||
{
|
||||
if (Tables == null || !Tables.Any())
|
||||
{
|
||||
List<string> TableNames = new List<string>();
|
||||
|
||||
SqliteCommand command = new SqliteCommand($"SHOW TABLES FROM `{connection.Database}`", connection);
|
||||
using (var reader = await command.ExecuteReaderAsync())
|
||||
{
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
TableNames.Add(reader.GetString(0));
|
||||
}
|
||||
|
||||
await reader.CloseAsync();
|
||||
}
|
||||
|
||||
Tables = TableNames;
|
||||
}
|
||||
|
||||
return Tables;
|
||||
}
|
||||
|
||||
public async Task OpenAsync()
|
||||
{
|
||||
if (connection.State != System.Data.ConnectionState.Open)
|
||||
{
|
||||
await connection.OpenAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
227
src/RestAPI/RestAPI/Controllers/TableController.cs
Normal file
227
src/RestAPI/RestAPI/Controllers/TableController.cs
Normal file
@@ -0,0 +1,227 @@
|
||||
using HtmlAgilityPack;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RestAPI.Clients;
|
||||
using RestAPI.Exceptions;
|
||||
using RestAPI.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Xml;
|
||||
|
||||
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
namespace RestAPI.Controllers
|
||||
{
|
||||
[Route("table")]
|
||||
[ApiController]
|
||||
public class TableController : ControllerBase
|
||||
{
|
||||
private readonly IDatabaseClient database;
|
||||
private readonly IEnumerable<string> BlacklistedFields;
|
||||
|
||||
public TableController(IDatabaseClient database)
|
||||
{
|
||||
this.database = database;
|
||||
|
||||
var fields = Environment.GetEnvironmentVariable("BLACKLISTEDFIELDS");
|
||||
|
||||
if (fields != null)
|
||||
{
|
||||
BlacklistedFields = fields.Split(",").Select(x => x.ToLowerInvariant());
|
||||
}
|
||||
else
|
||||
{
|
||||
BlacklistedFields = new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
private dynamic ConvertResponse(List<List<KeyValuePair<string, string>>> tableContent, string format, HttpResponse Response)
|
||||
{
|
||||
switch (format.ToLowerInvariant())
|
||||
{
|
||||
case "json":
|
||||
{
|
||||
Response.ContentType = "application/json";
|
||||
|
||||
List<dynamic> content = new List<dynamic>();
|
||||
|
||||
foreach (var result in tableContent)
|
||||
{
|
||||
dynamic item = new ExpandoObject();
|
||||
var dItem = item as IDictionary<string, object>;
|
||||
foreach (var attr in result)
|
||||
{
|
||||
dItem.Add(attr.Key, attr.Value);
|
||||
}
|
||||
|
||||
content.Add(item);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
case "xml":
|
||||
{
|
||||
Response.ContentType = "text/xml";
|
||||
var doc = new XmlDocument();
|
||||
|
||||
var results = doc.CreateNode(XmlNodeType.Element, null, "results", null);
|
||||
|
||||
foreach (var result in tableContent)
|
||||
{
|
||||
var res = doc.CreateNode(XmlNodeType.Element, null, "result", null);
|
||||
|
||||
foreach (var attr in result)
|
||||
{
|
||||
var resEl = doc.CreateNode(XmlNodeType.Element, null, attr.Key, null);
|
||||
|
||||
resEl.InnerText = attr.Value;
|
||||
|
||||
res.AppendChild(resEl);
|
||||
}
|
||||
|
||||
results.AppendChild(res);
|
||||
|
||||
doc.AppendChild(results);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
case "html":
|
||||
{
|
||||
Response.ContentType = "text/html";
|
||||
|
||||
var doc = new HtmlDocument();
|
||||
|
||||
var html = doc.CreateElement("html");
|
||||
var body = doc.CreateElement("body");
|
||||
var table = doc.CreateElement("table");
|
||||
var thead = doc.CreateElement("thead");
|
||||
var theadr = doc.CreateElement("tr");
|
||||
|
||||
doc.DocumentNode.AppendChild(html);
|
||||
html.AppendChild(body);
|
||||
body.AppendChild(table);
|
||||
table.AppendChild(thead);
|
||||
thead.AppendChild(theadr);
|
||||
|
||||
foreach (var attr in tableContent[0])
|
||||
{
|
||||
var th = doc.CreateElement("th");
|
||||
th.InnerHtml = attr.Key;
|
||||
theadr.AppendChild(th);
|
||||
}
|
||||
|
||||
var tbody = doc.CreateElement("tbody");
|
||||
table.AppendChild(tbody);
|
||||
|
||||
foreach (var result in tableContent)
|
||||
{
|
||||
var row = doc.CreateElement("tr");
|
||||
|
||||
foreach (var attr in result)
|
||||
{
|
||||
var resEl = doc.CreateElement("td");
|
||||
|
||||
resEl.InnerHtml = attr.Value;
|
||||
|
||||
row.AppendChild(resEl);
|
||||
}
|
||||
|
||||
tbody.AppendChild(row);
|
||||
}
|
||||
|
||||
return doc.DocumentNode.InnerHtml;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public string Get()
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.BadRequest, "Must enter a valid table");
|
||||
}
|
||||
|
||||
[HttpGet("{table}.{format}")]
|
||||
public dynamic Get(string table, string format)
|
||||
{
|
||||
database.OpenAsync().GetAwaiter().GetResult();
|
||||
|
||||
string tableLocation = table;
|
||||
|
||||
if (database is NpgsqlClient)
|
||||
{
|
||||
tableLocation = $"public.{table}";
|
||||
}
|
||||
|
||||
if (database.DoesTableExistAsync(tableLocation).GetAwaiter().GetResult())
|
||||
{
|
||||
var tableContent = database.GetTableAsync(tableLocation, BlacklistedFields.ToArray()).GetAwaiter().GetResult();
|
||||
dynamic response = null;
|
||||
|
||||
if (tableContent != null)
|
||||
{
|
||||
response = ConvertResponse(tableContent as List<List<KeyValuePair<string, string>>>, format, Response);
|
||||
}
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound, $"Data within table \"{tableLocation}\" not found");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound, $"Table \"{tableLocation}\" not found");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{area}/{table}.{format}")]
|
||||
public dynamic Get(string area, string table, string format)
|
||||
{
|
||||
database.OpenAsync().GetAwaiter().GetResult();
|
||||
|
||||
string tableLocation;
|
||||
|
||||
if (database is NpgsqlClient)
|
||||
{
|
||||
tableLocation = $"{area}.{table}";
|
||||
}
|
||||
else
|
||||
{
|
||||
tableLocation = $"{area}/{table}"; //default assumption for now
|
||||
}
|
||||
|
||||
if (database.DoesTableExistAsync(tableLocation).GetAwaiter().GetResult())
|
||||
{
|
||||
var tableContent = database.GetTableAsync(tableLocation, BlacklistedFields.ToArray()).GetAwaiter().GetResult();
|
||||
dynamic response = null;
|
||||
|
||||
if (tableContent != null)
|
||||
{
|
||||
response = ConvertResponse(tableContent as List<List<KeyValuePair<string, string>>>, format, Response);
|
||||
}
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound, $"Data within table \"{tableLocation}\" not found");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound, $"Table \"{tableLocation}\" not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using RestAPI.Exceptions;
|
||||
|
||||
namespace RestAPI.ExceptionFilters
|
||||
{
|
||||
public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
|
||||
{
|
||||
public int Order { get; } = int.MaxValue - 10;
|
||||
|
||||
public void OnActionExecuting(ActionExecutingContext context) { }
|
||||
|
||||
public void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
if (context.Exception is HttpResponseException exception)
|
||||
{
|
||||
context.Result = new ObjectResult(exception.Value)
|
||||
{
|
||||
StatusCode = exception.Status,
|
||||
};
|
||||
context.ExceptionHandled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/RestAPI/RestAPI/Exceptions/HttpResponseException.cs
Normal file
23
src/RestAPI/RestAPI/Exceptions/HttpResponseException.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace RestAPI.Exceptions
|
||||
{
|
||||
public class HttpResponseException : Exception
|
||||
{
|
||||
public int Status { get; set; } = 500;
|
||||
|
||||
public object Value { get; set; }
|
||||
|
||||
public HttpResponseException(int statusCode, object val)
|
||||
{
|
||||
Status = statusCode;
|
||||
Value = val;
|
||||
}
|
||||
|
||||
public HttpResponseException(HttpStatusCode statusCode, object val) : this((int)statusCode, val)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/RestAPI/RestAPI/Interfaces/IDatabaseClient.cs
Normal file
18
src/RestAPI/RestAPI/Interfaces/IDatabaseClient.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RestAPI.Interfaces
|
||||
{
|
||||
public interface IDatabaseClient
|
||||
{
|
||||
public Task OpenAsync();
|
||||
|
||||
public Task CloseAsync();
|
||||
|
||||
public Task<object> GetTableAsync(string tableName, string[] blacklistedFields = null);
|
||||
|
||||
public Task<bool> DoesTableExistAsync(string tableName);
|
||||
|
||||
public Task<IEnumerable<string>> GetTablesAsync();
|
||||
}
|
||||
}
|
||||
12
src/RestAPI/RestAPI/OutputFormatters/HtmlOutputFormatter.cs
Normal file
12
src/RestAPI/RestAPI/OutputFormatters/HtmlOutputFormatter.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
|
||||
namespace RestAPI.OutputFormatters
|
||||
{
|
||||
public class HtmlOutputFormatter : StringOutputFormatter
|
||||
{
|
||||
public HtmlOutputFormatter()
|
||||
{
|
||||
SupportedMediaTypes.Add("text/html");
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/RestAPI/RestAPI/Program.cs
Normal file
27
src/RestAPI/RestAPI/Program.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using dotenv.net;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace RestAPI
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
string envFile = Path.Combine(AppContext.BaseDirectory, ".env");
|
||||
|
||||
DotEnv.Config(false, filePath: envFile);
|
||||
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
}
|
||||
30
src/RestAPI/RestAPI/Properties/launchSettings.json
Normal file
30
src/RestAPI/RestAPI/Properties/launchSettings.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iis": {
|
||||
"applicationUrl": "http://localhost/RestAPI",
|
||||
"sslPort": 0
|
||||
},
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:35486",
|
||||
"sslPort": 44381
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IIS",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"RestAPI": {
|
||||
"commandName": "Project",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://0.0.0.0:5001;http://0.0.0.0:5000"
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/RestAPI/RestAPI/RestAPI.csproj
Normal file
18
src/RestAPI/RestAPI/RestAPI.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="dotenv.net" Version="2.1.1" />
|
||||
<PackageReference Include="FirebirdSql.Data.FirebirdClient" Version="7.10.1" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.29" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="5.0.1" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.11.5" />
|
||||
<PackageReference Include="MySqlConnector" Version="1.2.1" />
|
||||
<PackageReference Include="Npgsql" Version="5.0.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
205
src/RestAPI/RestAPI/Startup.cs
Normal file
205
src/RestAPI/RestAPI/Startup.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using FirebirdSql.Data.FirebirdClient;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MongoDB.Driver;
|
||||
using MySqlConnector;
|
||||
using Npgsql;
|
||||
using RestAPI.Clients;
|
||||
using RestAPI.ExceptionFilters;
|
||||
using RestAPI.Interfaces;
|
||||
using RestAPI.OutputFormatters;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace RestAPI
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
string dataSource = Environment.GetEnvironmentVariable("DATASOURCE");
|
||||
|
||||
if (dataSource != null)
|
||||
{
|
||||
string serverHost = Environment.GetEnvironmentVariable("HOST");
|
||||
string serverHostPort = Environment.GetEnvironmentVariable("HOSTPORT");
|
||||
string username = Environment.GetEnvironmentVariable("USERNAME");
|
||||
string password = Environment.GetEnvironmentVariable("PASSWORD");
|
||||
string database = Environment.GetEnvironmentVariable("DATABASE");
|
||||
|
||||
ushort hostPort = 0;
|
||||
|
||||
if (!ushort.TryParse(serverHostPort, out hostPort))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
switch (dataSource.ToLowerInvariant())
|
||||
{
|
||||
case "mysql":
|
||||
case "mariadb":
|
||||
{
|
||||
if (hostPort == 0)
|
||||
{
|
||||
hostPort = 3306;
|
||||
}
|
||||
|
||||
services.AddSingleton<IDatabaseClient>(new MySqlClient(new MySqlConnectionStringBuilder
|
||||
{
|
||||
Server = serverHost,
|
||||
Port = hostPort,
|
||||
UserID = username,
|
||||
Password = password,
|
||||
Database = database,
|
||||
AllowUserVariables = true
|
||||
}.ConnectionString));
|
||||
}
|
||||
break;
|
||||
case "mongo":
|
||||
case "mongodb":
|
||||
{
|
||||
if (hostPort == 0)
|
||||
{
|
||||
hostPort = 27017;
|
||||
}
|
||||
|
||||
MongoClientSettings builder = new MongoClientSettings();
|
||||
|
||||
string prefix = "mongodb";
|
||||
|
||||
if (!System.Net.IPAddress.TryParse(serverHost, out _))
|
||||
{
|
||||
prefix += "+srv";
|
||||
}
|
||||
|
||||
string connString;
|
||||
|
||||
if (!string.IsNullOrEmpty(username))
|
||||
{
|
||||
connString = string.Format(
|
||||
"{0}://{1}:{2}@{3}:{4}/",
|
||||
prefix,
|
||||
username,
|
||||
password,
|
||||
serverHost,
|
||||
hostPort
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
connString = string.Format(
|
||||
"{0}://{1}:{2}/",
|
||||
prefix,
|
||||
serverHost,
|
||||
hostPort
|
||||
);
|
||||
}
|
||||
|
||||
services.AddSingleton<IDatabaseClient>(new MongoDbClient(connString, database));
|
||||
}
|
||||
break;
|
||||
case "firebird":
|
||||
case "interbase":
|
||||
{
|
||||
//TODO: TEST FIREBASE
|
||||
throw new NotImplementedException();
|
||||
|
||||
if (hostPort == 0)
|
||||
{
|
||||
hostPort = 3050;
|
||||
}
|
||||
|
||||
services.AddSingleton<IDatabaseClient>(new FirebirdClient(new FbConnectionStringBuilder
|
||||
{
|
||||
DataSource = serverHost,
|
||||
Port = hostPort,
|
||||
UserID = username,
|
||||
Password = password,
|
||||
ServerType = FbServerType.Default
|
||||
}.ConnectionString));
|
||||
}
|
||||
break;
|
||||
case "pgsql":
|
||||
case "postgresql":
|
||||
{
|
||||
if (hostPort == 0)
|
||||
{
|
||||
hostPort = 5432;
|
||||
}
|
||||
|
||||
services.AddSingleton<IDatabaseClient>(new NpgsqlClient(new NpgsqlConnectionStringBuilder
|
||||
{
|
||||
Host = serverHost,
|
||||
Port = hostPort,
|
||||
Database = database,
|
||||
Username = username,
|
||||
Password = password
|
||||
}.ConnectionString));
|
||||
}
|
||||
break;
|
||||
case "sqlite":
|
||||
{
|
||||
//TODO: TEST SQLite
|
||||
throw new NotImplementedException();
|
||||
|
||||
SqliteConnectionStringBuilder builder = new SqliteConnectionStringBuilder();
|
||||
|
||||
var sqliteFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.sqlite", SearchOption.AllDirectories);
|
||||
|
||||
if (File.Exists(database))
|
||||
{
|
||||
builder.DataSource = database;
|
||||
}
|
||||
else if (!database.Contains(".sqlite"))
|
||||
{
|
||||
builder.DataSource = sqliteFiles.FirstOrDefault(x => x.Contains($"{database}.sqlite"));
|
||||
}
|
||||
|
||||
services.AddSingleton<IDatabaseClient>(new SQLiteClient(builder.ConnectionString));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
services.AddControllers(options =>
|
||||
{
|
||||
options.Filters.Add(new HttpResponseExceptionFilter());
|
||||
options.OutputFormatters.Add(new HtmlOutputFormatter());
|
||||
})
|
||||
.AddXmlSerializerFormatters();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/RestAPI/RestAPI/appsettings.Development.json
Normal file
9
src/RestAPI/RestAPI/appsettings.Development.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/RestAPI/RestAPI/appsettings.json
Normal file
10
src/RestAPI/RestAPI/appsettings.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Reference in New Issue
Block a user