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 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 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 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] [Authorize]
public class IntegrationController : ControllerBase 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 AyContext ct;
private readonly ILogger<IntegrationController> log; private readonly ILogger<IntegrationController> log;
private readonly ApiServerState serverState; 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)); return CreatedAtAction(nameof(IntegrationController.GetIntegration), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o));
} }
/// <summary> /// <summary>
/// Get Integration /// Get Integration
/// </summary> /// </summary>
@@ -79,11 +79,34 @@ namespace AyaNova.Api.Controllers
return StatusCode(403, new ApiNotAuthorizedResponse()); return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid) if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState)); 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)); if (o == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
return Ok(ApiOkResponse.Response(o)); 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> /// <summary>
/// Update Integration /// Update Integration
/// </summary> /// </summary>
@@ -99,7 +122,7 @@ namespace AyaNova.Api.Controllers
IntegrationBiz biz = IntegrationBiz.GetBiz(ct, HttpContext); IntegrationBiz biz = IntegrationBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType)) if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse()); return StatusCode(403, new ApiNotAuthorizedResponse());
var o = await biz.PutAsync(updatedObject); var o = await biz.PutAsync(updatedObject);
if (o == null) if (o == null)
{ {
if (biz.Errors.Exists(z => z.Code == ApiErrorCode.CONCURRENCY_CONFLICT)) if (biz.Errors.Exists(z => z.Code == ApiErrorCode.CONCURRENCY_CONFLICT))
@@ -130,6 +153,49 @@ namespace AyaNova.Api.Controllers
return NoContent(); 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 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.Util;
using AyaNova.Api.ControllerHelpers; using AyaNova.Api.ControllerHelpers;
using AyaNova.Models; using AyaNova.Models;
using System.Linq;
namespace AyaNova.Biz namespace AyaNova.Biz
{ {
@@ -39,6 +40,20 @@ namespace AyaNova.Biz
return await ct.Integration.AnyAsync(z => z.Id == id); 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 //CREATE
// //
@@ -49,10 +64,10 @@ namespace AyaNova.Biz
return null; return null;
else else
{ {
await ct.Integration.AddAsync(newObject); await ct.Integration.AddAsync(newObject);
await ct.SaveChangesAsync(); 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; return newObject;
} }
} }
@@ -60,12 +75,19 @@ namespace AyaNova.Biz
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
//GET //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); var ret = await ct.Integration.AsNoTracking().Include(z => z.Items).SingleOrDefaultAsync(m => m.IntegrationAppId == IntegrationAppId);
if (logTheGetEvent && ret != null) 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; return ret;
} }
@@ -89,7 +111,7 @@ namespace AyaNova.Biz
return null; return null;
} }
await ValidateAsync(putObject, dbObject); await ValidateAsync(putObject, dbObject);
if (HasErrors) return null; if (HasErrors) return null;
ct.Replace(dbObject, putObject); ct.Replace(dbObject, putObject);
@@ -106,14 +128,20 @@ namespace AyaNova.Biz
return null; return null;
} }
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct); await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct);
return putObject; return putObject;
} }
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE //DELETE
// //
internal async Task<bool> DeleteAsync(long id)
{
return await DeleteAsync(await AppIdFromDbIdAsync(id));
}
internal async Task<bool> DeleteAsync(Guid IntegrationAppId) internal async Task<bool> DeleteAsync(Guid IntegrationAppId)
{ {
using (var transaction = await ct.Database.BeginTransactionAsync()) using (var transaction = await ct.Database.BeginTransactionAsync())
@@ -127,7 +155,7 @@ namespace AyaNova.Biz
ValidateCanDelete(dbObject); ValidateCanDelete(dbObject);
if (HasErrors) if (HasErrors)
return false; return false;
ct.Integration.Remove(dbObject); ct.Integration.Remove(dbObject);
await ct.SaveChangesAsync(); await ct.SaveChangesAsync();
@@ -141,7 +169,7 @@ namespace AyaNova.Biz
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION //VALIDATION
@@ -166,11 +194,11 @@ namespace AyaNova.Biz
} }
} }
//Name required //Name required
if (proposedObj.IntegrationAppId == Guid.Empty) if (proposedObj.IntegrationAppId == Guid.Empty)
AddError(ApiErrorCode.VALIDATION_REQUIRED, "IntegrationAppId"); 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")) if (!PropertyHasErrors("IntegrationAppId"))
{ {
//Use Any command is efficient way to check existance, it doesn't return the record, just a true or false //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"); 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!!!! //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!!
private const int DESIRED_SCHEMA_LEVEL = 4; 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_INDEX_COUNT = 161;
internal const long EXPECTED_CHECK_CONSTRAINTS = 561; internal const long EXPECTED_CHECK_CONSTRAINTS = 561;
internal const long EXPECTED_FOREIGN_KEY_CONSTRAINTS = 204; 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!!!! //!!!!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 //INTEGRATIONITEM
await ExecQueryAsync("CREATE TABLE aintegrationitem (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, integrationid BIGINT NOT NULL REFERENCES aintegration ON DELETE CASCADE, " 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 //INTEGRATIONLOG