This commit is contained in:
2022-06-23 00:34:31 +00:00
parent af067d9591
commit 506891723c
6 changed files with 134 additions and 30 deletions

View File

@@ -1125,4 +1125,5 @@ BUILD 8.0.0 rc2 CHANGES OF NOTE
Added integration back end feature for integration of external applications with AyaNova 8
Added front end administrative UI for viewing and controlling integrated applications and their logs
Fixed potential lockout situation in AyaNova front end with force change known password code
Expanded roles allowed to fetch license to support integration of external applications

View File

@@ -19,10 +19,10 @@ namespace AyaNova.Api.Controllers
[Authorize]
public class IntegrationController : ControllerBase
{
/*
todo: needs routes for logging and fetching log to view, also mapping collection stuff perhaps??
*/
/*
todo: needs routes for logging and fetching log to view, also mapping collection stuff perhaps??
*/
private readonly AyContext ct;
private readonly ILogger<IntegrationController> log;
private readonly ApiServerState serverState;
@@ -63,7 +63,7 @@ namespace AyaNova.Api.Controllers
return CreatedAtAction(nameof(IntegrationController.GetIntegration), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o));
}
/// <summary>
/// Get Integration
/// </summary>
@@ -79,11 +79,34 @@ namespace AyaNova.Api.Controllers
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
var o = await biz.GetAsync(integrationAppId, true, true);
var o = await biz.GetAsync(integrationAppId, true);
if (o == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
return Ok(ApiOkResponse.Response(o));
}
/// <summary>
/// Get Integration by DB Id
/// </summary>
/// <param name="id"></param>
/// <returns>Integration</returns>
[HttpGet("by-dbid/{id}")]
public async Task<IActionResult> GetIntegrationByDbId([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
IntegrationBiz biz = IntegrationBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasReadFullRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
var o = await biz.GetAsync(id, true);
if (o == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
return Ok(ApiOkResponse.Response(o));
}
/// <summary>
/// Update Integration
/// </summary>
@@ -99,7 +122,7 @@ namespace AyaNova.Api.Controllers
IntegrationBiz biz = IntegrationBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
var o = await biz.PutAsync(updatedObject);
var o = await biz.PutAsync(updatedObject);
if (o == null)
{
if (biz.Errors.Exists(z => z.Code == ApiErrorCode.CONCURRENCY_CONFLICT))
@@ -130,6 +153,49 @@ namespace AyaNova.Api.Controllers
return NoContent();
}
/// <summary>
/// Delete Integration
/// </summary>
/// <param name="id"></param>
/// <returns>NoContent</returns>
[HttpDelete("by-dbid/{id}")]
public async Task<IActionResult> DeleteIntegrationByDbId([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
IntegrationBiz biz = IntegrationBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!await biz.DeleteAsync(id))
return BadRequest(new ApiErrorResponse(biz.Errors));
return NoContent();
}
/// <summary>
/// Check Integration existance
/// </summary>
/// <param name="integrationAppId"></param>
/// <returns>Integration</returns>
[HttpGet("exists/{integrationAppId}")]
public async Task<IActionResult> GetIntegrationExistance([FromRoute] Guid integrationAppId)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
IntegrationBiz biz = IntegrationBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasReadFullRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
return Ok(ApiOkResponse.Response(await biz.ExistsByIntegrationAppIdAsync(integrationAppId)));
}

View File

@@ -191,6 +191,8 @@ namespace AyaNova.Biz
//and need TRANSLATION KEYS because any type could show in the event log at the client end
//AND QBI mirrors this too
}

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Models;
using System.Linq;
namespace AyaNova.Biz
{
@@ -39,6 +40,20 @@ namespace AyaNova.Biz
return await ct.Integration.AnyAsync(z => z.Id == id);
}
internal async Task<bool> ExistsByIntegrationAppIdAsync(Guid IntegrationAppId)
{
return await ct.Integration.AnyAsync(z => z.IntegrationAppId == IntegrationAppId);
}
///////////////////////////////////////////////////////
//APPID FROM dbID
internal async Task<Guid> AppIdFromDbIdAsync(long id)
{
return await ct.Integration.AsNoTracking().Where(z => z.Id == id).Select(z => z.IntegrationAppId).SingleOrDefaultAsync();
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
//
@@ -49,10 +64,10 @@ namespace AyaNova.Biz
return null;
else
{
await ct.Integration.AddAsync(newObject);
await ct.SaveChangesAsync();
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct);
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct);
return newObject;
}
}
@@ -60,12 +75,19 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//GET
//
internal async Task<Integration> GetAsync(Guid IntegrationAppId, bool logTheGetEvent = true, bool populatePartNames = false)
//
internal async Task<Integration> GetAsync(long id, bool logTheGetEvent = true)
{
return await GetAsync(await AppIdFromDbIdAsync(id), logTheGetEvent);
}
internal async Task<Integration> GetAsync(Guid IntegrationAppId, bool logTheGetEvent = true)
{
var ret = await ct.Integration.AsNoTracking().Include(z => z.Items).SingleOrDefaultAsync(m => m.IntegrationAppId == IntegrationAppId);
if (logTheGetEvent && ret != null)
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, ret.Id, BizType, AyaEvent.Retrieved), ct);
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, ret.Id, BizType, AyaEvent.Retrieved), ct);
return ret;
}
@@ -89,7 +111,7 @@ namespace AyaNova.Biz
return null;
}
await ValidateAsync(putObject, dbObject);
if (HasErrors) return null;
ct.Replace(dbObject, putObject);
@@ -106,14 +128,20 @@ namespace AyaNova.Biz
return null;
}
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct);
return putObject;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//
internal async Task<bool> DeleteAsync(long id)
{
return await DeleteAsync(await AppIdFromDbIdAsync(id));
}
internal async Task<bool> DeleteAsync(Guid IntegrationAppId)
{
using (var transaction = await ct.Database.BeginTransactionAsync())
@@ -127,7 +155,7 @@ namespace AyaNova.Biz
ValidateCanDelete(dbObject);
if (HasErrors)
return false;
ct.Integration.Remove(dbObject);
await ct.SaveChangesAsync();
@@ -141,7 +169,7 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION
@@ -166,11 +194,11 @@ namespace AyaNova.Biz
}
}
//Name required
//Name required
if (proposedObj.IntegrationAppId == Guid.Empty)
AddError(ApiErrorCode.VALIDATION_REQUIRED, "IntegrationAppId");
//If name is otherwise OK, check that name is unique
//If name is otherwise OK, check that name is unique
if (!PropertyHasErrors("IntegrationAppId"))
{
//Use Any command is efficient way to check existance, it doesn't return the record, just a true or false
@@ -178,7 +206,7 @@ namespace AyaNova.Biz
{
AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "IntegrationAppId");
}
}
}
}
@@ -188,7 +216,7 @@ namespace AyaNova.Biz
}
/////////////////////////////////////////////////////////////////////

View File

@@ -22,7 +22,7 @@ namespace AyaNova.Util
//!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!!
private const int DESIRED_SCHEMA_LEVEL = 4;
internal const long EXPECTED_COLUMN_COUNT = 1375;
internal const long EXPECTED_COLUMN_COUNT = 1376;
internal const long EXPECTED_INDEX_COUNT = 161;
internal const long EXPECTED_CHECK_CONSTRAINTS = 561;
internal const long EXPECTED_FOREIGN_KEY_CONSTRAINTS = 204;
@@ -31,7 +31,7 @@ namespace AyaNova.Util
//!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!!
///////////////////////////////////////// C1375:I161:CC561:FC204:V11:R2
///////////////////////////////////////// (C1376:I161:CC561:FC204:V11:R2)
/*
@@ -1339,7 +1339,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE");
//INTEGRATIONITEM
await ExecQueryAsync("CREATE TABLE aintegrationitem (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, integrationid BIGINT NOT NULL REFERENCES aintegration ON DELETE CASCADE, "
+ "atype INTEGER NOT NULL, objectid BIGINT NOT NULL, integrationitemid TEXT NOT NULL, lastsync TIMESTAMPTZ, integrationitemdata TEXT "
+ "atype INTEGER NOT NULL, objectid BIGINT NOT NULL, integrationitemid TEXT NOT NULL, integrationitemname TEXT, lastsync TIMESTAMPTZ, integrationitemdata TEXT "
+ ")");
//INTEGRATIONLOG