mass untag

This commit is contained in:
2018-12-06 16:28:04 +00:00
parent 8445bdeb91
commit b6d799e5c0
34 changed files with 158 additions and 2624 deletions

View File

@@ -16,6 +16,11 @@ SON OF TAGS
//DONE - Unicode tag names
- Remove old tag code, models, schema tables, cleanup and db erase code that refers to them
- THINGS TO FIX AS A RESULT OF THIS:
- Search.cs search code had a ton of tag stuff in it, rejig that shit
- Import AyaNova7 biz tons of tag stuff
- Add new tag code to all taggable objects
- Update seeder to tag objects with known set of tags for test and eval purposes
- Seed widget with predictable tags (Maybe colour names "red", "green", "blue" etc)
@@ -118,8 +123,7 @@ SERVER
- Delete user should delete private datafilters
- Did I code how to handle implications of user delete anywhere yet??
- Taggable attribute exists, is it being used to ensure that objects that are taggable only are being tagged?
- Attachable attribute does this, search for "IsAttachable" to see how it's done
- Stage 2 AFTER POSTED TEST ROUND COMPLETED
- Error handling at client (log? display?)

View File

@@ -56,20 +56,18 @@ namespace AyaNova.Api.Controllers
var values = Enum.GetValues(typeof(AyaType));
foreach (AyaType t in values)
{
string name=t.ToString();
if(t.HasAttribute(typeof(AttachableAttribute))){
name+=" [Attachable]";
string name = t.ToString();
if (t.HasAttribute(typeof(AttachableAttribute)))
{
name += " [Attachable]";
}
if(t.HasAttribute(typeof(TaggableAttribute))){
name+=" [Taggable]";
}
l.Add(new NameIdItem() { Name = name, Id = (long)t });
}
return Ok(new ApiOkResponse(l));
}

View File

@@ -15,7 +15,7 @@ namespace AyaNova.Api.Controllers
{
/// <summary>
/// Tag controller
/// JobOperations controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]

View File

@@ -1,305 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
namespace AyaNova.Api.Controllers
{
/// <summary>
/// Tag controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class TagController : Controller
{
private readonly AyContext ct;
private readonly ILogger<TagController> log;
private readonly ApiServerState serverState;
/// <summary>
/// ctor
/// </summary>
/// <param name="dbcontext"></param>
/// <param name="logger"></param>
/// <param name="apiServerState"></param>
public TagController(AyContext dbcontext, ILogger<TagController> logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;
serverState = apiServerState;
}
/// <summary>
/// Get Tag
///
/// Required roles:
/// AnyOne
/// </summary>
/// <param name="id"></param>
/// <returns>A tag</returns>
[HttpGet("{id}")]
public async Task<IActionResult> GetTag([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
//Instantiate the business object handler
TagBiz biz = TagBiz.GetBiz(ct, HttpContext);
if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, biz.BizType))
return StatusCode(401, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
var o = await biz.GetAsync(id);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
return Ok(new ApiOkResponse(o));
}
/// <summary>
/// Get Tag pick list
///
/// Required roles: AnyRole
///
/// This endpoint queries the Name property of tags for
/// items that **START WITH** the characters submitted in the
/// "q" parameter
///
/// Unlike most other picklists, wildcard characters if found in the query will be escaped and be considered part of the search string
/// Query is case insensitive as all tags are lowercase
///
/// Empty queries will return all tags
///
/// </summary>
/// <returns>Paged id/name collection of tags with paging data</returns>
[HttpGet("PickList", Name = nameof(PickList))]
public async Task<IActionResult> PickList([FromQuery] string q, [FromQuery] PagingOptions pagingOptions)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
//Instantiate the business object handler
TagBiz biz = TagBiz.GetBiz(ct, HttpContext);
if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, biz.BizType))//Note: anyone can read a tag, but that might change in future so keeping this code in
return StatusCode(401, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
ApiPagedResponse<NameIdItem> pr = await biz.GetPickListAsync(Url, nameof(PickList), pagingOptions, q);
return Ok(new ApiOkWithPagingResponse<NameIdItem>(pr));
}
/// <summary>
/// Post TAG
///
/// Required roles:
/// BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
/// </summary>
/// <param name="inObj">String name of tag</param>
/// <returns><see cref="Tag"/> object</returns>
[HttpPost]
public async Task<IActionResult> PostTag([FromBody] NameItem inObj)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
//Instantiate the business object handler
TagBiz biz = TagBiz.GetBiz(ct, HttpContext);
//If a user has change roles, or editOwnRoles then they can create, true is passed for isOwner since they are creating so by definition the owner
if (!Authorized.IsAuthorizedToCreate(HttpContext.Items, biz.BizType))
return StatusCode(401, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Create and validate
Tag o = await biz.CreateAsync(inObj.Name);
if (o == null)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return CreatedAtAction("GetTag", new { id = o.Id }, new ApiCreatedResponse(o));
}
/// <summary>
/// Put (update) Tag
///
/// Required roles:
/// BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
///
/// </summary>
/// <param name="id"></param>
/// <param name="oIn"></param>
/// <returns></returns>
[HttpPut("{id}")]
public async Task<IActionResult> PutTag([FromRoute] long id, [FromBody] Tag oIn)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagBiz biz = TagBiz.GetBiz(ct, HttpContext);
var oFromDb = await biz.GetAsync(id);
if (oFromDb == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.IsAuthorizedToModify(HttpContext.Items, biz.BizType, oFromDb.OwnerId))
return StatusCode(401, new ApiNotAuthorizedResponse());
try
{
if (!biz.Put(oFromDb, oIn))
return BadRequest(new ApiErrorResponse(biz.Errors));
}
catch (DbUpdateConcurrencyException)
{
if (!await biz.ExistsAsync(id))
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
else
return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
}
return Ok(new ApiOkResponse(new { ConcurrencyToken = oFromDb.ConcurrencyToken }));
}
/// <summary>
/// Patch (update) Tag
/// Required roles: BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
/// </summary>
/// <param name="id"></param>
/// <param name="concurrencyToken"></param>
/// <param name="objectPatch"></param>
/// <returns></returns>
[HttpPatch("{id}/{concurrencyToken}")]
public async Task<IActionResult> PatchTag([FromRoute] long id, [FromRoute] uint concurrencyToken, [FromBody]JsonPatchDocument<Tag> objectPatch)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagBiz biz = TagBiz.GetBiz(ct, HttpContext);
var oFromDb = await biz.GetAsync(id);
if (oFromDb == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.IsAuthorizedToModify(HttpContext.Items, biz.BizType, oFromDb.OwnerId))
return StatusCode(401, new ApiNotAuthorizedResponse());
try
{
//patch and validate
if (!biz.Patch(oFromDb, objectPatch, concurrencyToken))
return BadRequest(new ApiErrorResponse(biz.Errors));
}
catch (DbUpdateConcurrencyException)
{
if (!await biz.ExistsAsync(id))
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
else
return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
}
return Ok(new ApiOkResponse(new { ConcurrencyToken = oFromDb.ConcurrencyToken }));
}
/// <summary>
/// Untag all objects with this tag
/// Required roles: BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
/// </summary>
/// <param name="id">TagId</param>
/// <returns>Ok</returns>
[HttpPost("UntagAll/{id}")]
public async Task<IActionResult> UnTagEverything([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagBiz biz = TagBiz.GetBiz(ct, HttpContext);
var dbObj = await biz.GetAsync(id);
if (dbObj == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.IsAuthorizedToModify(HttpContext.Items, biz.BizType, dbObj.OwnerId))
return StatusCode(401, new ApiNotAuthorizedResponse());
//Untag from all places
if (!biz.Untag(dbObj))
return BadRequest(new ApiErrorResponse(biz.Errors));
return NoContent();
}
/// <summary>
/// Delete Tag
/// Required roles: BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
/// </summary>
/// <param name="id"></param>
/// <returns>Ok</returns>
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTag([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagBiz biz = TagBiz.GetBiz(ct, HttpContext);
var dbObj = await biz.GetAsync(id);
if (dbObj == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.IsAuthorizedToDelete(HttpContext.Items, biz.BizType, dbObj.OwnerId))
return StatusCode(401, new ApiNotAuthorizedResponse());
if (!biz.Delete(dbObj))
return BadRequest(new ApiErrorResponse(biz.Errors));
return NoContent();
}
//------------
}
}

View File

@@ -1,337 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
namespace AyaNova.Api.Controllers
{
/// <summary>
/// TagGroup controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class TagGroupController : Controller
{
private readonly AyContext ct;
private readonly ILogger<TagGroupController> log;
private readonly ApiServerState serverState;
/// <summary>
/// ctor
/// </summary>
/// <param name="dbcontext"></param>
/// <param name="logger"></param>
/// <param name="apiServerState"></param>
public TagGroupController(AyContext dbcontext, ILogger<TagGroupController> logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;
serverState = apiServerState;
}
/// <summary>
/// Get TagGroup
///
/// Required roles:
/// AnyOne
/// </summary>
/// <param name="id"></param>
/// <returns>A TagGroup</returns>
[HttpGet("{id}")]
public async Task<IActionResult> GetTagGroup([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, AyaType.TagGroup))
return StatusCode(401, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagGroupBiz biz = TagGroupBiz.GetBiz(ct, HttpContext);
var o = await biz.GetAsync(id);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
return Ok(new ApiOkResponse(o));
}
/// <summary>
/// Get TagGroup pick list, list of tag groups with name and ID
///
/// Required roles: AnyRole
///
/// This endpoint queries the Name property of TagGroups for
/// items that **START WITH** the characters submitted in the
/// "q" parameter
///
/// Unlike most other picklists, wildcard characters if found in the query will be escaped and be considered part of the search string
/// Query is case insensitive as all TagGroups are lowercase
///
/// Empty queries will return all TagGroups
///
/// </summary>
/// <returns>Paged id/name collection of TagGroups with paging data</returns>
[HttpGet("TagGroupPickList", Name = nameof(TagGroupPickList))]
public async Task<IActionResult> TagGroupPickList([FromQuery] string q, [FromQuery] PagingOptions pagingOptions)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, AyaType.TagGroup))//Note: anyone can read a TagGroup, but that might change in future so keeping this code in
return StatusCode(401, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagGroupBiz biz = TagGroupBiz.GetBiz(ct, HttpContext);
ApiPagedResponse<NameIdItem> pr = await biz.GetPickListAsync(Url, nameof(TagGroupPickList), pagingOptions, q);
return Ok(new ApiOkWithPagingResponse<NameIdItem>(pr));
}
/// <summary>
/// Get Tag pick list from group
/// Required roles: AnyRole
///
/// </summary>
/// <param name="id">TagGroupId</param>
/// <returns>name value list of all tags in group</returns>
[HttpGet("TagsInGroupPickList/{id}")]
public async Task<IActionResult> TagsInGroupPickList([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, AyaType.TagGroup))//Note: anyone can read a tag-group, but that might change in future so keeping this code in
return StatusCode(401, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagGroupBiz biz = TagGroupBiz.GetBiz(ct, HttpContext);
var l = await biz.GetTagsInGroupPickListAsync(id);
return Ok(new ApiOkResponse(l));
}
/// <summary>
/// Post TagGroup
///
/// Required roles:
/// BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
/// </summary>
/// <param name="inObj">String name of TagGroup</param>
/// <returns><see cref="TagGroup"/> object</returns>
[HttpPost]
public async Task<IActionResult> PostTagGroup([FromBody] NameItem inObj)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
//If a user has change roles, or editOwnRoles then they can create, true is passed for isOwner since they are creating so by definition the owner
if (!Authorized.IsAuthorizedToCreate(HttpContext.Items, AyaType.TagGroup))
return StatusCode(401, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagGroupBiz biz = TagGroupBiz.GetBiz(ct, HttpContext);
//Create and validate
TagGroup o = await biz.CreateAsync(inObj.Name);
if (o == null)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return CreatedAtAction("GetTagGroup", new { id = o.Id }, new ApiCreatedResponse(o));
}
/// <summary>
/// Put (update) TagGroup
///
/// Required roles:
/// BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
///
/// </summary>
/// <param name="id"></param>
/// <param name="oIn"></param>
/// <returns></returns>
[HttpPut("{id}")]
public async Task<IActionResult> PutTagGroup([FromRoute] long id, [FromBody] TagGroup oIn)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagGroupBiz biz = TagGroupBiz.GetBiz(ct, HttpContext);
var oFromDb = await biz.GetAsync(id);
if (oFromDb == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.IsAuthorizedToModify(HttpContext.Items, AyaType.TagGroup, oFromDb.OwnerId))
return StatusCode(401, new ApiNotAuthorizedResponse());
try
{
if (!biz.Put(oFromDb, oIn))
return BadRequest(new ApiErrorResponse(biz.Errors));
}
catch (DbUpdateConcurrencyException)
{
if (!await biz.ExistsAsync(id))
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
else
return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
}
return Ok(new ApiOkResponse(new { ConcurrencyToken = oFromDb.ConcurrencyToken }));
}
/// <summary>
/// Patch (update) TagGroup
/// Required roles: BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
/// </summary>
/// <param name="id"></param>
/// <param name="concurrencyToken"></param>
/// <param name="objectPatch"></param>
/// <returns></returns>
[HttpPatch("{id}/{concurrencyToken}")]
public async Task<IActionResult> PatchTagGroup([FromRoute] long id, [FromRoute] uint concurrencyToken, [FromBody]JsonPatchDocument<TagGroup> objectPatch)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagGroupBiz biz = TagGroupBiz.GetBiz(ct, HttpContext);
var oFromDb = await biz.GetAsync(id);
if (oFromDb == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.IsAuthorizedToModify(HttpContext.Items, AyaType.TagGroup, oFromDb.OwnerId))
return StatusCode(401, new ApiNotAuthorizedResponse());
try
{
if (!biz.Patch(oFromDb, objectPatch, concurrencyToken))
return BadRequest(new ApiErrorResponse(biz.Errors));
}
catch (DbUpdateConcurrencyException)
{
if (!await biz.ExistsAsync(id))
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
else
return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
}
return Ok(new ApiOkResponse(new { ConcurrencyToken = oFromDb.ConcurrencyToken }));
}
/// <summary>
/// Delete TagGroup - Does not delete Tags from objects or Tags themselves, only the group
/// Required roles: BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
/// </summary>
/// <param name="id"></param>
/// <returns>Ok</returns>
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTagGroup([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagGroupBiz biz = TagGroupBiz.GetBiz(ct, HttpContext);
var dbObj = await biz.GetAsync(id);
if (dbObj == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.IsAuthorizedToDelete(HttpContext.Items, AyaType.TagGroup, dbObj.OwnerId))
return StatusCode(401, new ApiNotAuthorizedResponse());
if (!biz.Delete(dbObj))
return BadRequest(new ApiErrorResponse(biz.Errors));
return NoContent();
}
/// <summary>
/// Post TagMap from group - Map a group of tags to an object / Id
/// Required roles: Same roles as tagged object
///
/// </summary>
/// <param name="inObj">TagMapGroupInfo</param>
/// <returns>A list of names and id's of tags that were applied to object</returns>
[HttpPost("TagObject")]
public async Task<IActionResult> TagObject([FromBody] TagMapGroupInfo inObj)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
//If a user has change roles, or editOwnRoles then they can create, true is passed for isOwner since they are creating so by definition the owner
if (!Authorized.IsAuthorizedToCreate(HttpContext.Items, AyaType.TagMap))
return StatusCode(401, new ApiNotAuthorizedResponse());
//Rights to parent taggable object?
if (!Authorized.IsAuthorizedToCreate(HttpContext.Items, inObj.TagToObjectType))
return StatusCode(401, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
TagGroupBiz biz = TagGroupBiz.GetBiz(ct, HttpContext);
//Create and return list
List<NameIdItem> l = await biz.TagObject(inObj);
if (l == null)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return Ok(new ApiOkResponse(l));
}
//------------
}//eoc
}//eons

View File

@@ -1,197 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
namespace AyaNova.Api.Controllers
{
/// <summary>
/// TagGroupMap controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class TagGroupMapController : Controller
{
private readonly AyContext ct;
private readonly ILogger<TagGroupMapController> log;
private readonly ApiServerState serverState;
/// <summary>
/// ctor
/// </summary>
/// <param name="dbcontext"></param>
/// <param name="logger"></param>
/// <param name="apiServerState"></param>
public TagGroupMapController(AyContext dbcontext, ILogger<TagGroupMapController> logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;
serverState = apiServerState;
}
/// <summary>
/// Get TagGroupMap object
/// Required roles: BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
/// </summary>
/// <param name="id"></param>
/// <returns>A TagGroupMap</returns>
[HttpGet("{id}")]
public async Task<IActionResult> GetTagGroupMap([FromRoute] long id)
{
if (!serverState.IsOpen)
{
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
}
if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, AyaType.TagGroupMap))
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResponse(ModelState));
}
//Instantiate the business object handler
TagGroupMapBiz biz = new TagGroupMapBiz(ct, UserIdFromContext.Id(HttpContext.Items), UserRolesFromContext.Roles(HttpContext.Items));
var o = await biz.GetAsync(id);
if (o == null)
{
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
}
return Ok(new ApiOkResponse(o));
}
/// <summary>
/// Post TagGroupMap - Map a tag to a group
/// Required roles: BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
/// </summary>
/// <param name="inObj">TagGroupMapInfo</param>
/// <returns><see cref="TagGroupMap"/> object</returns>
[HttpPost]
public async Task<IActionResult> PostTagGroupMap([FromBody] TagGroupMapInfo inObj)
{
if (!serverState.IsOpen)
{
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
}
if (!Authorized.IsAuthorizedToCreate(HttpContext.Items, AyaType.TagGroupMap))
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResponse(ModelState));
}
//Instantiate the business object handler
TagGroupMapBiz biz = new TagGroupMapBiz(ct, UserIdFromContext.Id(HttpContext.Items), UserRolesFromContext.Roles(HttpContext.Items));
//Create and validate
TagGroupMap o = await biz.CreateAsync(inObj);
if (o == null)
{
//error return
return BadRequest(new ApiErrorResponse(biz.Errors));
}
else
{
//save and success return
await ct.SaveChangesAsync();
//BIZLOG: Not going to log this for now, it's too common an operation and would require bringing in more info. If decide to implement should log the parent object with text of tag instead
//and don't forget about import from v7 as well
return CreatedAtAction("GetTagGroupMap", new { id = o.Id }, new ApiCreatedResponse(o));
}
}
/// <summary>
/// Delete TagGroupMap
/// Required roles: BizAdminFull, DispatchFull, InventoryFull, TechFull, AccountingFull
/// </summary>
/// <param name="id"></param>
/// <returns>Ok</returns>
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTagGroupMap([FromRoute] long id)
{
if (!serverState.IsOpen)
{
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
}
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResponse(ModelState));
}
var dbObj = await ct.TagGroupMap.SingleOrDefaultAsync(m => m.Id == id);
if (dbObj == null)
{
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
}
if (!Authorized.IsAuthorizedToDelete(HttpContext.Items, AyaType.TagGroupMap, dbObj.OwnerId))
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
//Instantiate the business object handler
TagGroupMapBiz biz = new TagGroupMapBiz(ct, UserIdFromContext.Id(HttpContext.Items), UserRolesFromContext.Roles(HttpContext.Items));
if (!biz.Delete(dbObj))
{
return BadRequest(new ApiErrorResponse(biz.Errors));
}
await ct.SaveChangesAsync();
//BIZLOG: Not going to log this for now, it's too common an operation and would require bringing in more info. If decide to implement should log the parent object with text of tag instead
return NoContent();
}
private bool TagGroupMapExists(long id)
{
return ct.TagGroupMap.Any(e => e.Id == id);
}
//------------
}//eoc
}

View File

@@ -1,264 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
namespace AyaNova.Api.Controllers
{
/// <summary>
/// TagMap controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class TagMapController : Controller
{
private readonly AyContext ct;
private readonly ILogger<TagMapController> log;
private readonly ApiServerState serverState;
/// <summary>
/// ctor
/// </summary>
/// <param name="dbcontext"></param>
/// <param name="logger"></param>
/// <param name="apiServerState"></param>
public TagMapController(AyContext dbcontext, ILogger<TagMapController> logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;
serverState = apiServerState;
}
/// <summary>
/// Get TagMap object
///
/// Required roles: Same roles as tagged object
/// </summary>
/// <param name="id"></param>
/// <returns>A TagMap</returns>
[HttpGet("{id}")]
public async Task<IActionResult> GetTagMap([FromRoute] long id)
{
if (!serverState.IsOpen)
{
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
}
if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, AyaType.TagMap))
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResponse(ModelState));
}
//Instantiate the business object handler
TagMapBiz biz = new TagMapBiz(ct, UserIdFromContext.Id(HttpContext.Items), UserRolesFromContext.Roles(HttpContext.Items));
var o = await biz.GetAsync(id);
if (o == null)
{
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
}
//Check rights to parent tagged object
if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, o.TagToObjectType))
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
return Ok(new ApiOkResponse(o));
}
/// <summary>
/// Post TagMap - Map a single Tag to an object / Id
/// Required roles: Same roles as tagged object
///
/// </summary>
/// <param name="inObj">TagMapInfo</param>
/// <returns><see cref="TagMap"/> object</returns>
[HttpPost]
public async Task<IActionResult> PostTagMap([FromBody] TagMapInfo inObj)
{
if (!serverState.IsOpen)
{
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
}
//If a user has change roles, or editOwnRoles then they can create, true is passed for isOwner since they are creating so by definition the owner
if (!Authorized.IsAuthorizedToCreate(HttpContext.Items, AyaType.TagMap))
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
//Rights to parent taggable object?
if (!Authorized.IsAuthorizedToCreate(HttpContext.Items, inObj.TagToObjectType))
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResponse(ModelState));
}
//Instantiate the business object handler
TagMapBiz biz = new TagMapBiz(ct, UserIdFromContext.Id(HttpContext.Items), UserRolesFromContext.Roles(HttpContext.Items));
//Create and validate
TagMap o = await biz.CreateAsync(inObj);
if (o == null)
{
//error return
return BadRequest(new ApiErrorResponse(biz.Errors));
}
else
{
//save and success return
await ct.SaveChangesAsync();
//BIZLOG: Not going to log this for now, it's too common an operation and would require bringing in more info. If decide to implement should log the parent object with text of tag instead
//and don't forget about import from v7 as well
return CreatedAtAction("GetTagMap", new { id = o.Id }, new ApiCreatedResponse(o));
}
}
/// <summary>
/// Delete TagMap
/// Required roles: Same roles as tagged object
/// </summary>
/// <param name="id"></param>
/// <returns>Ok</returns>
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTagMap([FromRoute] long id)
{
if (!serverState.IsOpen)
{
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
}
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResponse(ModelState));
}
var dbObj = await ct.TagMap.SingleOrDefaultAsync(m => m.Id == id);
if (dbObj == null)
{
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
}
if (!Authorized.IsAuthorizedToDelete(HttpContext.Items, AyaType.TagMap, dbObj.OwnerId))
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
//Rights to parent tagged object?
if (!Authorized.IsAuthorizedToDelete(HttpContext.Items, dbObj.TagToObjectType, dbObj.OwnerId))
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
//Instantiate the business object handler
TagMapBiz biz = new TagMapBiz(ct, UserIdFromContext.Id(HttpContext.Items), UserRolesFromContext.Roles(HttpContext.Items));
if (!biz.Delete(dbObj))
{
return BadRequest(new ApiErrorResponse(biz.Errors));
}
await ct.SaveChangesAsync();
//BIZLOG: Not going to log this for now, it's too common an operation and would require bringing in more info. If decide to implement should log the parent object with text of tag instead
return NoContent();
}
/// <summary>
/// Get Tag pick list
///
/// Required roles: Follows parent (tagged object) roles
///
/// </summary>
/// <returns>Name / Id collection of tags on object</returns>
[HttpGet("TagsOnObject")]
public async Task<IActionResult> TagsOnObjectList([FromBody] TypeAndIdInfo inObj)
{
if (!serverState.IsOpen)
{
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
}
if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, AyaType.Tag))//Note: anyone can read a tag, but that might change in future so keeping this code in
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResponse(ModelState));
}
//Check rights to parent tagged object
if (!Authorized.IsAuthorizedToReadFullRecord(HttpContext.Items, inObj.ObjectType))
{
return StatusCode(401, new ApiNotAuthorizedResponse());
}
//Instantiate the business object handler
TagMapBiz biz = new TagMapBiz(ct, UserIdFromContext.Id(HttpContext.Items), UserRolesFromContext.Roles(HttpContext.Items));
var l = await biz.GetTagsOnObjectListAsync(new AyaTypeId(inObj.ObjectType, inObj.ObjectId));
return Ok(new ApiOkResponse(l));
}
private bool TagMapExists(long id)
{
return ct.TagMap.Any(e => e.Id == id);
}
//------------
}//eoc
}

View File

@@ -21,8 +21,7 @@ namespace AyaNova.Biz
LicenseFetch=7,
LicenseTrialRequest=8,
ServerStateChange=9,
SeedDatabase=10,
TagMassUntag=11
SeedDatabase=10
//NEW ITEMS REQUIRE LOCALE KEYS AND UPDATE EventLogProcessor code that prefetches required keys

View File

@@ -12,25 +12,25 @@ namespace AyaNova.Biz
NoType = 0,
Global = 1,
[Attachable, Taggable]
[Attachable]
Widget = 2,
[Attachable, Taggable]
[Attachable]
User = 3,
ServerState = 4,
License = 5,
LogFile = 6,
Tag = 7,
TagMap = 8,
DEPRECATED_REUSELATER_7 = 7,
DEPRECATED_REUSELATER_8 = 8,
JobOperations = 9,
AyaNova7Import = 10,
TrialSeeder = 11,
Metrics = 12,
Locale = 13,
UserOptions = 14,
TagGroup = 15,
TagGroupMap = 16,
DEPRECATED_REUSELATER_15 = 15,
DEPRECATED_REUSELATER_16 = 16,
FileAttachment = 17,
DataFilter = 18

View File

@@ -38,11 +38,7 @@ namespace AyaNova.Biz
case AyaType.User:
return ct.User.Any(m => m.Id == id);
case AyaType.Widget:
return ct.Widget.Any(m => m.Id == id);
case AyaType.Tag:
return ct.Tag.Any(m => m.Id == id);
case AyaType.TagGroup:
return ct.TagGroup.Any(m => m.Id == id);
return ct.Widget.Any(m => m.Id == id);
case AyaType.FileAttachment:
return ct.FileAttachment.Any(m => m.Id == id);
case AyaType.DataFilter:

View File

@@ -26,11 +26,7 @@ namespace AyaNova.Biz
case AyaType.User:
return new UserBiz(dbcontext, userId, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, roles);
case AyaType.Widget:
return new WidgetBiz(dbcontext, userId, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, roles);
case AyaType.Tag:
return new TagBiz(dbcontext, userId, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, roles);
case AyaType.TagMap:
return new TagMapBiz(dbcontext, userId, roles);
return new WidgetBiz(dbcontext, userId, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, roles);
case AyaType.JobOperations:
return new JobOperationsBiz(dbcontext, userId, roles);
case AyaType.AyaNova7Import:

View File

@@ -44,10 +44,7 @@ namespace AyaNova.Biz
// return ct.User.AsNoTracking().Where(m => m.Id == id).Select(m => m.Name).FirstOrDefault();
// case AyaType.Widget:
// return ct.Widget.AsNoTracking().Where(m => m.Id == id).Select(m => m.Name).FirstOrDefault();
// case AyaType.Tag:
// return ct.Tag.AsNoTracking().Where(m => m.Id == id).Select(m => m.Name).FirstOrDefault();
// case AyaType.TagGroup:
// return ct.TagGroup.AsNoTracking().Where(m => m.Id == id).Select(m => m.Name).FirstOrDefault();
// case AyaType.FileAttachment:
// return ct.FileAttachment.AsNoTracking().Where(m => m.Id == id).Select(m => m.DisplayFileName).FirstOrDefault();

View File

@@ -37,12 +37,7 @@ namespace AyaNova.Biz
case AyaType.Widget:
TABLE = "awidget";
break;
case AyaType.Tag:
TABLE = "atag";
break;
case AyaType.TagGroup:
TABLE = "ataggroup";
break;
case AyaType.FileAttachment:
TABLE = "afileattachment";
COLUMN = "displayfilename";

View File

@@ -92,46 +92,8 @@ namespace AyaNova.Biz
ReadFullRecord = AuthorizationRoles.OpsAdminFull | AuthorizationRoles.OpsAdminLimited
});
////////////////////////////////////////////////////////////
//TAG
//Full roles can make new tags and can edit or delete existing tags
roles.Add(AyaType.Tag, new BizRoleSet()
{
Change = AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryFull | AuthorizationRoles.TechFull | AuthorizationRoles.AccountingFull,
EditOwn = AuthorizationRoles.NoRole,
ReadFullRecord = AuthorizationRoles.AnyRole
});
////////////////////////////////////////////////////////////
//TAGMAP
//Any roles can tag objects and remove tags as per their rights to the taggable object type in question
roles.Add(AyaType.TagMap, new BizRoleSet()
{
Change = AuthorizationRoles.AnyRole,
EditOwn = AuthorizationRoles.NoRole,
ReadFullRecord = AuthorizationRoles.AnyRole
});
////////////////////////////////////////////////////////////
//TAGGROUP - MIRROR TAGS
//Full roles can make new tags and can edit or delete existing tags
roles.Add(AyaType.TagGroup, new BizRoleSet()
{
Change = AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryFull | AuthorizationRoles.TechFull | AuthorizationRoles.AccountingFull,
EditOwn = AuthorizationRoles.NoRole,
ReadFullRecord = AuthorizationRoles.AnyRole
});
////////////////////////////////////////////////////////////
//TAGGROUPMAP - MIRROR TAGMAP
//Full roles can make new taggroupmaps and can edit or delete existing taggroupmaps
roles.Add(AyaType.TagGroupMap, new BizRoleSet()
{
Change = AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryFull | AuthorizationRoles.TechFull | AuthorizationRoles.AccountingFull,
EditOwn = AuthorizationRoles.NoRole,
ReadFullRecord = AuthorizationRoles.AnyRole
});
////////////////////////////////////////////////////////////
//OPERATIONS / JOBS

View File

@@ -97,36 +97,38 @@ namespace AyaNova.Biz
await DoImport("GZTW.AyaNova.BLL.User", "eventlog", AyaType.User, job.GId, importMap, importFileName, zipEntries);
//IMPORT UNIT MODEL CATEGORIES AS TAGS
await DoImport("GZTW.AyaNova.BLL.UnitModelCategory", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
//TODO: these need to be done differently and after the main objects are imported or in parallel with them as there is no
//separate tag type object anymore
//I guess cache the list of each v7 string and id type and then use that to tag objects as they are import4ed that use them
//IMPORT Unit service type AS TAGS
await DoImport("GZTW.AyaNova.BLL.UnitServiceType", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
// //IMPORT UNIT MODEL CATEGORIES AS TAGS
// await DoImport("GZTW.AyaNova.BLL.UnitModelCategory", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
//IMPORT Workorder Item Type AS TAGS
await DoImport("GZTW.AyaNova.BLL.WorkorderItemType", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
// //IMPORT Unit service type AS TAGS
// await DoImport("GZTW.AyaNova.BLL.UnitServiceType", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
//IMPORT Client group AS TAGS
await DoImport("GZTW.AyaNova.BLL.ClientGroup", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
// //IMPORT Workorder Item Type AS TAGS
// await DoImport("GZTW.AyaNova.BLL.WorkorderItemType", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
//IMPORT Workorder category AS TAGS
await DoImport("GZTW.AyaNova.BLL.WorkorderCategory", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
// //IMPORT Client group AS TAGS
// await DoImport("GZTW.AyaNova.BLL.ClientGroup", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
//IMPORT Part Category AS TAGS
await DoImport("GZTW.AyaNova.BLL.PartCategory", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
// //IMPORT Workorder category AS TAGS
// await DoImport("GZTW.AyaNova.BLL.WorkorderCategory", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
//IMPORT Dispatch zones AS TAGS
await DoImport("GZTW.AyaNova.BLL.DispatchZone", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
// //IMPORT Part Category AS TAGS
// await DoImport("GZTW.AyaNova.BLL.PartCategory", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
//IMPORT Scheduleable User Groups AS TAGS
await DoImport("GZTW.AyaNova.BLL.ScheduleableUserGroup", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
//Now can set users to correct tag for scheduleable user group
await DoImport("GZTW.AyaNova.BLL.ScheduleableUserGroup", "scheduleableusergrouptags", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
// //IMPORT Dispatch zones AS TAGS
// await DoImport("GZTW.AyaNova.BLL.DispatchZone", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
// //IMPORT Scheduleable User Groups AS TAGS
// await DoImport("GZTW.AyaNova.BLL.ScheduleableUserGroup", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
// //Now can set users to correct tag for scheduleable user group
// await DoImport("GZTW.AyaNova.BLL.ScheduleableUserGroup", "scheduleableusergrouptags", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
//IMPORT REGIONS AS TAGS
await DoImport("GZTW.AyaNova.BLL.Region", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
// //IMPORT REGIONS AS TAGS
// await DoImport("GZTW.AyaNova.BLL.Region", "main", AyaType.Tag, job.GId, importMap, importFileName, zipEntries);
//IMPORT LOCALES
await DoImport("GZTW.AyaNova.BLL.Locale", "main", AyaType.Locale, job.GId, importMap, importFileName, zipEntries);

View File

@@ -65,7 +65,7 @@ namespace AyaNova.Biz
public string Phrase { get; set; }
public bool NameOnly { get; set; }
public AyaType TypeOnly { get; set; }
public List<long> Tags { get; set; }
// public List<long> Tags { get; set; }
//Note: maxresults of 0 will get all results
public int MaxResults { get; set; }
@@ -73,7 +73,7 @@ namespace AyaNova.Biz
{
NameOnly = false;
TypeOnly = AyaType.NoType;
Tags = new List<long>();
// Tags = new List<long>();
MaxResults = 500;
}
@@ -85,9 +85,9 @@ namespace AyaNova.Biz
if (!string.IsNullOrWhiteSpace(this.Phrase))
return true;
//has tags?
if (this.Tags.Count > 0)
return true;
// //has tags?
// if (this.Tags.Count > 0)
// return true;
return false;
}
@@ -213,97 +213,97 @@ namespace AyaNova.Biz
//IF TAGS SPECIFIED
//BUGBUG: If no valid tags provided, i.e. a single tag of type or id 0 then can skip
if (searchParameters.Tags.Count > 0)
{
//get a count of the search tags (used by both paths below)
var SearchTagCount = searchParameters.Tags.Count;
// if (searchParameters.Tags.Count > 0)
// {
// //get a count of the search tags (used by both paths below)
// var SearchTagCount = searchParameters.Tags.Count;
if (string.IsNullOrWhiteSpace(searchParameters.Phrase))
{
// if (string.IsNullOrWhiteSpace(searchParameters.Phrase))
// {
#region TAGS ONLY SEARCH (NO PHRASE) ALL FULL MATCHES ARE INCLUSIVE
Dictionary<long, long> TagCounts = new Dictionary<long, long>();
// #region TAGS ONLY SEARCH (NO PHRASE) ALL FULL MATCHES ARE INCLUSIVE
// Dictionary<long, long> TagCounts = new Dictionary<long, long>();
//QUERY FOR ALL TAGMAPS THAT MATCH OBJECT TYPE AND ID FOR EVERY TAG SPECIFIED (UNION)
//var tagmatches= await ct.TagMap.Where(m => ).Select(m => m.Id).ToListAsync();
//ct.TagMap.Where(n => n.Tags.Count(t => tags.Contains(t.DisplayName)) == tags.Count)
// //QUERY FOR ALL TAGMAPS THAT MATCH OBJECT TYPE AND ID FOR EVERY TAG SPECIFIED (UNION)
// //var tagmatches= await ct.TagMap.Where(m => ).Select(m => m.Id).ToListAsync();
// //ct.TagMap.Where(n => n.Tags.Count(t => tags.Contains(t.DisplayName)) == tags.Count)
//algorithm:
//1) get counts for each tag specified from tagmap, if any are zero then none match and can bail early
foreach (long SearchTagId in searchParameters.Tags)
{
var MatchTagCount = await ct.TagMap.Where(m => m.TagId == SearchTagId).LongCountAsync();
//zero tags matching here at any point means no results for the entire search and we can bail
if (MatchTagCount == 0)
{
//return empty resultlist
return ReturnObject;
}
// //algorithm:
// //1) get counts for each tag specified from tagmap, if any are zero then none match and can bail early
// foreach (long SearchTagId in searchParameters.Tags)
// {
// var MatchTagCount = await ct.TagMap.Where(m => m.TagId == SearchTagId).LongCountAsync();
// //zero tags matching here at any point means no results for the entire search and we can bail
// if (MatchTagCount == 0)
// {
// //return empty resultlist
// return ReturnObject;
// }
//Save the matching count
TagCounts.Add(SearchTagId, MatchTagCount);
}
// //Save the matching count
// TagCounts.Add(SearchTagId, MatchTagCount);
// }
//2) find smallest count match so we are working with the shortest list first
var ShortestMatchingTag = TagCounts.OrderBy(x => x.Value).First().Key;
// //2) find smallest count match so we are working with the shortest list first
// var ShortestMatchingTag = TagCounts.OrderBy(x => x.Value).First().Key;
//3) Generate the shortlist of items that match the shortest tag list
var ShortList = await ct.TagMap.Where(x => x.TagId == ShortestMatchingTag).ToListAsync();
// //3) Generate the shortlist of items that match the shortest tag list
// var ShortList = await ct.TagMap.Where(x => x.TagId == ShortestMatchingTag).ToListAsync();
//4) Iterate the shortlist and see if each item matches all other tags specified if it does then put it into the matching objects list for return
// //4) Iterate the shortlist and see if each item matches all other tags specified if it does then put it into the matching objects list for return
//Iterate shortlist
foreach (TagMap t in ShortList)
{
var matchCount = 1;
//Iterate requested tags
foreach (long TagId in searchParameters.Tags)
{
//skipping already matched shortest tag
if (TagId != ShortestMatchingTag)
{
//Ok, does this object have this tag?
bool HasTag = await ct.TagMap.Where(x => x.TagToObjectId == t.TagToObjectId && x.TagToObjectType == t.TagToObjectType && x.TagId == TagId).AnyAsync();
if (HasTag)
matchCount++;
}
}
//does it match all tags?
if (matchCount == SearchTagCount)
{
//yes, add it to the results
MatchingObjects.Add(new AyaTypeId(t.TagToObjectType, t.TagToObjectId));
}
}
#endregion
// //Iterate shortlist
// foreach (TagMap t in ShortList)
// {
// var matchCount = 1;
// //Iterate requested tags
// foreach (long TagId in searchParameters.Tags)
// {
// //skipping already matched shortest tag
// if (TagId != ShortestMatchingTag)
// {
// //Ok, does this object have this tag?
// bool HasTag = await ct.TagMap.Where(x => x.TagToObjectId == t.TagToObjectId && x.TagToObjectType == t.TagToObjectType && x.TagId == TagId).AnyAsync();
// if (HasTag)
// matchCount++;
// }
// }
// //does it match all tags?
// if (matchCount == SearchTagCount)
// {
// //yes, add it to the results
// MatchingObjects.Add(new AyaTypeId(t.TagToObjectType, t.TagToObjectId));
// }
// }
// #endregion
}
else
{
#region TAGS PLUS PHRASE SEARCH WITH NON MATCHING TAGS EXCLUSIVE
//list to hold temporary matches
List<AyaTypeId> TagMatchingObjects = new List<AyaTypeId>();
// }
// else
// {
// #region TAGS PLUS PHRASE SEARCH WITH NON MATCHING TAGS EXCLUSIVE
// //list to hold temporary matches
// List<AyaTypeId> TagMatchingObjects = new List<AyaTypeId>();
//LOOP THROUGH MATCHING OBJECTS LIST
foreach (AyaTypeId i in MatchingObjects)
{
var matchCount = await ct.TagMap.Where(x => x.TagToObjectId == i.ObjectId && x.TagToObjectType == i.ObjectType && searchParameters.Tags.Contains(x.TagId)).LongCountAsync();
if (matchCount == SearchTagCount)
{
TagMatchingObjects.Add(i);
}
// //LOOP THROUGH MATCHING OBJECTS LIST
// foreach (AyaTypeId i in MatchingObjects)
// {
// var matchCount = await ct.TagMap.Where(x => x.TagToObjectId == i.ObjectId && x.TagToObjectType == i.ObjectType && searchParameters.Tags.Contains(x.TagId)).LongCountAsync();
// if (matchCount == SearchTagCount)
// {
// TagMatchingObjects.Add(i);
// }
}
// }
//Ok here we have all the MatchingObjects that had all the tags in the TagMatchingObjects list so that's actually now our defacto return list
MatchingObjects = TagMatchingObjects;
// //Ok here we have all the MatchingObjects that had all the tags in the TagMatchingObjects list so that's actually now our defacto return list
// MatchingObjects = TagMatchingObjects;
#endregion
// #endregion
}
}
// }
// }
//REMOVE ANY ITEMS THAT USER IS NOT PERMITTED TO READ
//If it's a name only search then all is allowed
@@ -403,7 +403,7 @@ namespace AyaNova.Biz
//AyContext ct = ServiceProviderProvider.DBContext;
#if (DEBUG)
if (objectType == AyaType.TagMap || objectType == AyaType.JobOperations || objectType == AyaType.Locale)
if ( objectType == AyaType.JobOperations || objectType == AyaType.Locale)
{
throw new System.NotSupportedException($"Search::ProcessKeywords - Invalid type presented {objectType}");
}

View File

@@ -1,497 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.JsonPatch;
using EnumsNET;
using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
using AyaNova.Models;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
namespace AyaNova.Biz
{
internal class TagBiz : BizObject, IImportAyaNova7Object
{
public bool SeedOrImportRelaxedRulesMode { get; set; }
internal TagBiz(AyContext dbcontext, long currentUserId, long userLocaleId, AuthorizationRoles userRoles)
{
ct = dbcontext;
UserId = currentUserId;
UserLocaleId = userLocaleId;
CurrentUserRoles = userRoles;
BizType = AyaType.Tag;
SeedOrImportRelaxedRulesMode = false;//default
}
internal static TagBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext)
{
return new TagBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
}
////////////////////////////////////////////////////////////////////////////////////////////////
//EXISTS
internal async Task<bool> ExistsAsync(long id)
{
return await ct.Tag.AnyAsync(e => e.Id == id);
}
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
internal async Task<Tag> GetAsync(long fetchId)
{
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
return await ct.Tag.SingleOrDefaultAsync(m => m.Id == fetchId);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
internal async Task<Tag> CreateAsync(string inObj)
{
inObj = CleanTagName(inObj);
Validate(inObj, true);
if (HasErrors)
return null;
else
{
//do stuff with Tag
Tag outObj = new Tag()
{
Name = inObj,
OwnerId = UserId
};
await ct.Tag.AddAsync(outObj);
await ct.SaveChangesAsync();
//Handle child and associated items:
//EVENT LOG
EventLogProcessor.LogEventToDatabase(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
//SEARCH INDEXING
Search.ProcessNewObjectKeywords(UserLocaleId, outObj.Id, BizType, outObj.Name, outObj.Name);
return outObj;
}
}
private static string CleanTagName(string inObj)
{
//Must be lowercase per rules
//This may be naive when we get international customers but for now supporting utf-8 and it appears it's safe to do this with unicode
inObj = inObj.ToLowerInvariant();
//No spaces in tags, replace with dashes
inObj = inObj.Replace(" ", "-");
//Remove multiple dash sequences
inObj = System.Text.RegularExpressions.Regex.Replace(inObj, "-+", "-");
//Ensure doesn't start or end with a dash
inObj = inObj.Trim('-');
//No longer than 255 characters
inObj = StringUtil.MaxLength(inObj, 255);
return inObj;
}
//get picklist (paged)
//Unlike most picklists, this one only checks for starts with and wildcards are not supported / treated as part of tag name
internal async Task<ApiPagedResponse<NameIdItem>> GetPickListAsync(IUrlHelper Url, string routeName, PagingOptions pagingOptions, string q)
{
pagingOptions.Offset = pagingOptions.Offset ?? PagingOptions.DefaultOffset;
pagingOptions.Limit = pagingOptions.Limit ?? PagingOptions.DefaultLimit;
NameIdItem[] items;
int totalRecordCount = 0;
if (!string.IsNullOrWhiteSpace(q))
{
//tags are allow saved this way so search this way too
q = q.ToLowerInvariant();
items = await ct.Tag
//There is some debate on this for efficiency
//I chose this method because I think it escapes potential wildcards in the string automatically
//and I don't want people using wildcards with this, only starts with is supported
//https://stackoverflow.com/questions/45708715/entity-framework-ef-functions-like-vs-string-contains
.Where(m => m.Name.StartsWith(q))
// .Where(m => EF.Functions.ILike(m.Name, q))
.OrderBy(m => m.Name)
.Skip(pagingOptions.Offset.Value)
.Take(pagingOptions.Limit.Value)
.Select(m => new NameIdItem()
{
Id = m.Id,
Name = m.Name
}).ToArrayAsync();
totalRecordCount = await ct.Tag.Where(m => m.Name.StartsWith(q)).CountAsync();
}
else
{
items = await ct.Tag
.OrderBy(m => m.Name)
.Skip(pagingOptions.Offset.Value)
.Take(pagingOptions.Limit.Value)
.Select(m => new NameIdItem()
{
Id = m.Id,
Name = m.Name
}).ToArrayAsync();
totalRecordCount = await ct.Tag.CountAsync();
}
var pageLinks = new PaginationLinkBuilder(Url, routeName, null, pagingOptions, totalRecordCount).PagingLinksObject();
ApiPagedResponse<NameIdItem> pr = new ApiPagedResponse<NameIdItem>(items, pageLinks);
return pr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//UPDATE
//
//put
internal bool Put(Tag dbObj, Tag inObj)
{
//preserve the owner ID if none was specified
if (inObj.OwnerId == 0)
inObj.OwnerId = dbObj.OwnerId;
//Ensure it follows the rules
inObj.Name = CleanTagName(inObj.Name);
//Replace the db object with the PUT object
CopyObject.Copy(inObj, dbObj, "Id");
//Set "original" value of concurrency token to input token
//this will allow EF to check it out
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken;
Validate(dbObj.Name, false);
if (HasErrors)
return false;
//Log modification
EventLogProcessor.LogEventToDatabase(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct);
//Update keywords
Search.ProcessUpdatedObjectKeywords( UserLocaleId, dbObj.Id, BizType, dbObj.Name, dbObj.Name);
return true;
}
//patch
internal bool Patch(Tag dbObj, JsonPatchDocument<Tag> objectPatch, uint concurrencyToken)
{
//Validate Patch is allowed
if (!ValidateJsonPatch<Tag>.Validate(this, objectPatch)) return false;
//Do the patching
objectPatch.ApplyTo(dbObj);
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = concurrencyToken;
//Ensure it follows the rules
dbObj.Name = CleanTagName(dbObj.Name);
Validate(dbObj.Name, false);
if (HasErrors)
return false;
//Log modification
EventLogProcessor.LogEventToDatabase(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct);
//Update keywords
Search.ProcessUpdatedObjectKeywords( UserLocaleId, dbObj.Id, BizType, dbObj.Name, dbObj.Name);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//
internal bool Delete(Tag dbObj)
{
//Determine if the object can be deleted, do the deletion and related objects
ValidateCanDelete(dbObj);
if (HasErrors)
return false;
ct.Tag.Remove(dbObj);
ct.SaveChanges();
//Delete sibling objects
//Event log process delete
EventLogProcessor.DeleteObject(UserId, BizType, dbObj.Id, dbObj.Name, ct);
ct.SaveChanges();
//Delete search index
Search.ProcessDeletedObjectKeywords(dbObj.Id, BizType);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// UNTAG - Untag this tag from everywhere it's used
//
internal bool Untag(Tag dbObj)
{
//Be careful in future, if you put ToString at the end of each object in the string interpolation
//npgsql driver will assume it's a string and put quotes around it triggering an error that a string can't be compared to an int
ct.Database.ExecuteSqlCommand($"delete from atagmap where tagid = {dbObj.Id}");
ct.Database.ExecuteSqlCommand($"delete from ataggroupmap where tagid = {dbObj.Id}");
//Log
EventLogProcessor.LogEventToDatabase(new Event(UserId, dbObj.Id, BizType, AyaEvent.TagMassUntag), ct);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION
//
//Can save or update?
private void Validate(string inObj, bool isNew)
{
//run validation and biz rules
//Name required
if (string.IsNullOrWhiteSpace(inObj))
AddError(ValidationErrorType.RequiredPropertyEmpty, "Name");
//Name must be less than 255 characters
if (inObj.Length > 255)
AddError(ValidationErrorType.LengthExceeded, "Name", "255 char max");
//Name must be unique
if (ct.Tag.Where(m => m.Name == inObj).FirstOrDefault() != null)
AddError(ValidationErrorType.NotUnique, "Name");
return;
}
//Can delete?
private void ValidateCanDelete(Tag inObj)
{
//whatever needs to be checked to delete this object
//See if any tagmaps exist with this tag in which case it's not deleteable
if (ct.TagMap.Any(e => e.TagId == inObj.Id))
{
AddError(ValidationErrorType.ReferentialIntegrity, "object", "Can't be deleted while has relations");
}
if (ct.TagGroupMap.Any(e => e.TagId == inObj.Id))
{
AddError(ValidationErrorType.ReferentialIntegrity, "TagGroup", "Can't be deleted while has TagGroup relations");
}
}
/////////////////////////////////////////////////////////////////////
/// IMPORT v7 implementation
public async Task<bool> ImportV7Async(JObject j, List<ImportAyaNova7MapItem> importMap, Guid jobId)
{
//NO TASK TYPE, IT'S ALL THE SAME, KEEPING THIS FOR POSSIBLE FUTURE PURPOSES LIKE APPENDING OBJECT TYPE OR SOMETHING
string SourceType = j["V7_TYPE"].Value<string>();
switch (SourceType)
{
case "GZTW.AyaNova.BLL.Region":
case "GZTW.AyaNova.BLL.UnitModelCategory":
case "GZTW.AyaNova.BLL.UnitServiceType":
case "GZTW.AyaNova.BLL.WorkorderItemType":
case "GZTW.AyaNova.BLL.ClientGroup":
case "GZTW.AyaNova.BLL.WorkorderCategory":
case "GZTW.AyaNova.BLL.PartCategory":
case "GZTW.AyaNova.BLL.DispatchZone":
case "GZTW.AyaNova.BLL.ScheduleableUserGroup":
{
switch (j["IMPORT_TASK"].Value<string>())
{
case "main":
{
#region main import task
var NewTagName = j["Name"].Value<string>();
var ShortTypeName = string.Empty;
switch (SourceType)
{
case "GZTW.AyaNova.BLL.Region":
ShortTypeName = "rgn";
break;
case "GZTW.AyaNova.BLL.UnitModelCategory":
ShortTypeName = "unitmdlctgry";
break;
case "GZTW.AyaNova.BLL.UnitServiceType":
ShortTypeName = "unitsvtyp";
break;
case "GZTW.AyaNova.BLL.WorkorderItemType":
ShortTypeName = "woitemtyp";
break;
case "GZTW.AyaNova.BLL.ClientGroup":
ShortTypeName = "clntgrp";
break;
case "GZTW.AyaNova.BLL.WorkorderCategory":
ShortTypeName = "woctgry";
break;
case "GZTW.AyaNova.BLL.PartCategory":
ShortTypeName = "prtctgry";
break;
case "GZTW.AyaNova.BLL.DispatchZone":
ShortTypeName = "dspchzn";
break;
case "GZTW.AyaNova.BLL.ScheduleableUserGroup":
ShortTypeName = "schdusrgrp";
break;
}
NewTagName += "." + ShortTypeName;
var OldV7Id = new Guid(j["ID"].Value<string>());
//Ensure it follows the rules
NewTagName = CleanTagName(NewTagName);
//There might already be a tag of the same name since so many different types of V7 objects are becoming tags
//Weighed the pros and cons of uniquifying by object type versus just using the same name for different object types:
//it seems to me at this point that people might desire the same exact name because if they used it that way they probably
//intended it that way, so decision is to check if it already exists and then use that ID in the importMap instead
//for matching other objects imported to tags
//Already present?
var ExistingTag = ct.Tag.Where(m => m.Name == NewTagName).FirstOrDefault();
if (ExistingTag != null)
{
//map it to the existing tag of same name
var mapItem = new ImportAyaNova7MapItem(OldV7Id, AyaType.Tag, ExistingTag.Id);
}
else
{
Tag o = await CreateAsync(NewTagName);
if (HasErrors)
{
//If there are any validation errors, log in joblog and move on
JobsBiz.LogJob(jobId, $"TagBiz::ImportV7Async -> import object \"{NewTagName}\" of type \"{SourceType}\" source id {OldV7Id.ToString()} failed validation and was not imported: {GetErrorsAsString()} ", ct);
return false;
}
else
{
await ct.SaveChangesAsync();
var mapItem = new ImportAyaNova7MapItem(OldV7Id, AyaType.Tag, o.Id);
importMap.Add(mapItem);
ImportAyaNova7Biz.LogEventCreatedModifiedEvents(j, importMap, AyaType.Tag, ct);
}
}
#endregion
}
break;
case "scheduleableusergrouptags":
{
#region attribute sched user group tags
/*
{
"ID": "871e77b2-979a-4f26-930b-46f7c05fc19f",
"Created": "08/30/2018 08:12 AM",
"Modified": "08/30/2018 08:13 AM",
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
"Modifier": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
"Name": "Yet another test group",
"Active": true,
"Description": "More testing yay!",
"ScheduleableUsers": [
{
"Created": "08/30/2018 08:13 AM",
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
"Modified": "08/30/2018 08:13 AM",
"Modifier": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
"ID": "676475be-8301-47d0-bd54-af9dbd1fe7eb",
"ScheduleableUserID": "1d859264-3f32-462a-9b0c-a67dddfdf4d3",
"ScheduleableUserGroupID": "871e77b2-979a-4f26-930b-46f7c05fc19f"
},
{
"Created": "08/30/2018 08:13 AM",
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
"Modified": "08/30/2018 08:13 AM",
"Modifier": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
"ID": "173499c3-a616-42a0-b08c-74008f8fa352",
"ScheduleableUserID": "42b282bb-100b-4b31-aa14-5c831d7cda66",
"ScheduleableUserGroupID": "871e77b2-979a-4f26-930b-46f7c05fc19f"
},
{
"Created": "08/30/2018 08:13 AM",
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
"Modified": "08/30/2018 08:13 AM",
"Modifier": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
"ID": "19c9b3d6-eeb2-44ac-be4e-6ec93d15b02a",
"ScheduleableUserID": "e6ff9bc6-a550-4242-8c41-857f740e2841",
"ScheduleableUserGroupID": "871e77b2-979a-4f26-930b-46f7c05fc19f"
}
]
}
*/
var V7Id = new Guid(j["ID"].Value<string>());
var RavenTagId = importMap.Where(m => m.V7ObjectId == V7Id).First().NewObjectAyaTypeId.ObjectId;
foreach (JToken t in j["ScheduleableUsers"])
{
var techId = new Guid(t["ScheduleableUserID"].Value<String>());
var RavenUserId = importMap.Where(m => m.V7ObjectId == techId).First().NewObjectAyaTypeId.ObjectId;
var Creator = importMap.Where(m => m.V7ObjectId == new Guid(t["Creator"].Value<string>())).First().NewObjectAyaTypeId.ObjectId;
TagMap tm = new TagMap();
tm.TagToObjectId = RavenUserId;
tm.TagToObjectType = AyaType.User;
tm.TagId = RavenTagId;
tm.OwnerId = Creator;
ct.TagMap.Add(tm);
}
ct.SaveChanges();
#endregion
}
break;
}
}
break;
}
//this is the equivalent of returning void for a Task signature with nothing to return
return true;
}
/////////////////////////////////////////////////////////////////////
}//eoc
//
}//eons

View File

@@ -1,340 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.JsonPatch;
using EnumsNET;
using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
using AyaNova.Models;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
namespace AyaNova.Biz
{
internal class TagGroupBiz : BizObject
{
// private readonly AyContext ct;
// public readonly long userId;
// private readonly AuthorizationRoles userRoles;
public bool V7ValidationImportMode { get; set; }
internal TagGroupBiz(AyContext dbcontext, long currentUserId, long userLocaleId, AuthorizationRoles userRoles)
{
ct = dbcontext;
UserId = currentUserId;
UserLocaleId = userLocaleId;
CurrentUserRoles = userRoles;
BizType = AyaType.TagGroup;
V7ValidationImportMode = false;//default
}
internal static TagGroupBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext)
{
return new TagGroupBiz(ct, UserIdFromContext.Id(httpContext.Items), UserLocaleIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
}
////////////////////////////////////////////////////////////////////////////////////////////////
//EXISTS
internal async Task<bool> ExistsAsync(long id)
{
return await ct.TagGroup.AnyAsync(e => e.Id == id);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
internal async Task<TagGroup> CreateAsync(string inObj)
{
inObj = CleanTagGroupName(inObj);
Validate(inObj, true);
if (HasErrors)
return null;
else
{
//do stuff with TagGroup
TagGroup outObj = new TagGroup()
{
Name = inObj,
OwnerId = UserId
};
await ct.TagGroup.AddAsync(outObj);
//EVENT LOG
EventLogProcessor.LogEventToDatabase(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
//SEARCH INDEXING
Search.ProcessNewObjectKeywords( UserLocaleId, outObj.Id, BizType, outObj.Name, outObj.Name);
return outObj;
}
}
private static string CleanTagGroupName(string inObj)
{
//Must be lowercase per rules
//This may be naive when we get international customers but for now supporting utf-8 and it appears it's safe to do this with unicode
inObj = inObj.ToLowerInvariant();
//No spaces in TagGroups, replace with dashes
inObj = inObj.Replace(" ", "-");
//Remove multiple dash sequences
inObj = System.Text.RegularExpressions.Regex.Replace(inObj, "-+", "-");
//Ensure doesn't start or end with a dash
inObj = inObj.Trim('-');
//No longer than 255 characters
inObj = StringUtil.MaxLength(inObj, 255);
return inObj;
}
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
//Get one
internal async Task<TagGroup> GetAsync(long fetchId)
{
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
return await ct.TagGroup.SingleOrDefaultAsync(m => m.Id == fetchId);
}
//get picklist (paged)
//Unlike most picklists, this one only checks for starts with and wildcards are not supported / treated as part of TagGroup name
internal async Task<ApiPagedResponse<NameIdItem>> GetPickListAsync(IUrlHelper Url, string routeName, PagingOptions pagingOptions, string q)
{
pagingOptions.Offset = pagingOptions.Offset ?? PagingOptions.DefaultOffset;
pagingOptions.Limit = pagingOptions.Limit ?? PagingOptions.DefaultLimit;
NameIdItem[] items;
int totalRecordCount = 0;
if (!string.IsNullOrWhiteSpace(q))
{
//TagGroups are allow saved this way so search this way too
q = q.ToLowerInvariant();
items = await ct.TagGroup
//There is some debate on this for efficiency
//I chose this method because I think it escapes potential wildcards in the string automatically
//and I don't want people using wildcards with this, only starts with is supported
//https://stackoverflow.com/questions/45708715/entity-framework-ef-functions-like-vs-string-contains
.Where(m => m.Name.StartsWith(q))
// .Where(m => EF.Functions.ILike(m.Name, q))
.OrderBy(m => m.Name)
.Skip(pagingOptions.Offset.Value)
.Take(pagingOptions.Limit.Value)
.Select(m => new NameIdItem()
{
Id = m.Id,
Name = m.Name
}).ToArrayAsync();
totalRecordCount = await ct.TagGroup.Where(m => m.Name.StartsWith(q)).CountAsync();
}
else
{
items = await ct.TagGroup
.OrderBy(m => m.Name)
.Skip(pagingOptions.Offset.Value)
.Take(pagingOptions.Limit.Value)
.Select(m => new NameIdItem()
{
Id = m.Id,
Name = m.Name
}).ToArrayAsync();
totalRecordCount = await ct.TagGroup.CountAsync();
}
var pageLinks = new PaginationLinkBuilder(Url, routeName, null, pagingOptions, totalRecordCount).PagingLinksObject();
ApiPagedResponse<NameIdItem> pr = new ApiPagedResponse<NameIdItem>(items, pageLinks);
return pr;
}
//get picklist (simple non-paged)
internal async Task<List<NameIdItem>> GetTagsInGroupPickListAsync(long tagGroupId)
{
//first, get an array of the tagId's in this group
var tagGroupTags = await ct.TagGroupMap.Where(m => m.TagGroupId == tagGroupId).Select(m => m.TagId).ToArrayAsync();
List<NameIdItem> l = new List<NameIdItem>();
l = await ct.Tag
.Where(m => tagGroupTags.Contains(m.Id))
.OrderBy(m => m.Name)
.Select(m => new NameIdItem()
{
Id = m.Id,
Name = m.Name
}).ToListAsync();
return l;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//UPDATE
//
//put
internal bool Put(TagGroup dbObj, TagGroup inObj)
{
//preserve the owner ID if none was specified
if (inObj.OwnerId == 0)
inObj.OwnerId = dbObj.OwnerId;
//Ensure it follows the rules
inObj.Name = CleanTagGroupName(inObj.Name);
//Replace the db object with the PUT object
CopyObject.Copy(inObj, dbObj, "Id");
//Set "original" value of concurrency token to input token
//this will allow EF to check it out
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken;
Validate(dbObj.Name, false);
if (HasErrors)
return false;
//Log modification
EventLogProcessor.LogEventToDatabase(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct);
//Update keywords
Search.ProcessUpdatedObjectKeywords( UserLocaleId, dbObj.Id, BizType, dbObj.Name, dbObj.Name);
return true;
}
//patch
internal bool Patch(TagGroup dbObj, JsonPatchDocument<TagGroup> objectPatch, uint concurrencyToken)
{
//Validate Patch is allowed
if (!ValidateJsonPatch<TagGroup>.Validate(this, objectPatch)) return false;
//Do the patching
objectPatch.ApplyTo(dbObj);
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = concurrencyToken;
//Ensure it follows the rules
dbObj.Name = CleanTagGroupName(dbObj.Name);
Validate(dbObj.Name, false);
if (HasErrors)
return false;
//Log modification
EventLogProcessor.LogEventToDatabase(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct);
//Update keywords
Search.ProcessUpdatedObjectKeywords( UserLocaleId, dbObj.Id, BizType, dbObj.Name, dbObj.Name);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//
internal bool Delete(TagGroup dbObj)
{
//Determine if the object can be deleted, do the deletion tentatively
ValidateCanDelete(dbObj);
if (HasErrors)
return false;
ct.Database.ExecuteSqlCommand($"delete from ataggroupmap where taggroupid = {dbObj.Id}");
ct.Database.ExecuteSqlCommand($"delete from ataggroup where id = {dbObj.Id}");
//Event log process delete
EventLogProcessor.DeleteObject(UserId, BizType, dbObj.Id, dbObj.Name, ct);
ct.SaveChanges();
//Delete search index
Search.ProcessDeletedObjectKeywords(dbObj.Id, BizType);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//TagObject
internal async Task<List<NameIdItem>> TagObject(TagMapGroupInfo inObj)
{
var ReturnObject = new List<NameIdItem>();
var TagGroupTags = await GetTagsInGroupPickListAsync(inObj.TagGroupId);
if (TagGroupTags.Count == 0) return ReturnObject;
//Tag each one separately via TagMap which handles cases where object is already tagged with that tag etc
var MapBiz = new TagMapBiz(ct, UserId, CurrentUserRoles);
foreach (NameIdItem TagInGroup in TagGroupTags)
{
var TagMapItem = await MapBiz.CreateAsync(new TagMapInfo { TagId = TagInGroup.Id, TagToObjectId = inObj.TagToObjectId, TagToObjectType = inObj.TagToObjectType });
ReturnObject.Add(TagInGroup);
}
await ct.SaveChangesAsync();
//BIZLOG: Not going to log this for now, it's too common an operation and would require bringing in more info. If decide to implement should log the parent object with text of tag instead
//and don't forget about import from v7 as well
return ReturnObject;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION
//
//Can save or update?
private void Validate(string inObj, bool isNew)
{
//run validation and biz rules
//Name required
if (string.IsNullOrWhiteSpace(inObj))
AddError(ValidationErrorType.RequiredPropertyEmpty, "Name");
//Name must be less than 255 characters
if (inObj.Length > 255)
AddError(ValidationErrorType.LengthExceeded, "Name", "255 char max");
//Name must be unique
if (ct.TagGroup.Where(m => m.Name == inObj).FirstOrDefault() != null)
AddError(ValidationErrorType.NotUnique, "Name");
return;
}
//Can delete?
private void ValidateCanDelete(TagGroup inObj)
{
//whatever needs to be check to delete this object
//See if any TagGroupmaps exist with this TagGroup in which case it's not deleteable
// if (ct.TagGroupMap.Any(e => e.TagGroupId == inObj.Id))
// {
// AddError(ValidationErrorType.ReferentialIntegrity, "TagGroup", "Can't be deleted while has tags mapped");
// }
}
/////////////////////////////////////////////////////////////////////
}//eoc
//
}//eons

View File

@@ -1,139 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.JsonPatch;
using EnumsNET;
using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
using AyaNova.Models;
using System.Collections.Generic;
namespace AyaNova.Biz
{
internal class TagGroupMapBiz : BizObject
{
internal TagGroupMapBiz(AyContext dbcontext, long currentUserId, AuthorizationRoles userRoles)
{
ct = dbcontext;
UserId = currentUserId;
CurrentUserRoles = userRoles;
BizType = AyaType.TagGroupMap;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
internal async Task<TagGroupMap> CreateAsync(TagGroupMapInfo inObj)
{
//Is this taggroup mapping already present?
var Existing = await ct.TagGroupMap.Where(m => m.TagId == inObj.TagId && m.TagGroupId == inObj.TagGroupId).FirstOrDefaultAsync();
if (Existing != null)
{
//yup so just return it, don't make a new one
return Existing;
}
Validate(inObj, true);
if (HasErrors)
return null;
else
{
//do stuff with TagGroupMap
TagGroupMap outObj = new TagGroupMap()
{
TagId = inObj.TagId,
TagGroupId = inObj.TagGroupId,
OwnerId = UserId
};
await ct.TagGroupMap.AddAsync(outObj);
return outObj;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
//Get one
internal async Task<TagGroupMap> GetAsync(long fetchId)
{
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
return await ct.TagGroupMap.SingleOrDefaultAsync(m => m.Id == fetchId);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//
internal bool Delete(TagGroupMap dbObj)
{
//Determine if the object can be deleted, do the deletion tentatively
ValidateCanDelete(dbObj);
if (HasErrors)
return false;
ct.TagGroupMap.Remove(dbObj);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION
//
//Can save or update?
private void Validate(TagGroupMapInfo inObj, bool isNew)
{
//run validation and biz rules
// //Name required
// if (string.IsNullOrWhiteSpace(inObj))
// AddError(ValidationErrorType.RequiredPropertyEmpty, "Name");
// //Name must be less than 255 characters
// if (inObj.Length > 255)
// AddError(ValidationErrorType.LengthExceeded, "Name", "255 char max");
return;
}
//Can delete?
private void ValidateCanDelete(TagGroupMap inObj)
{
//whatever needs to be check to delete this object
//See if any TagGroupMaps exist with this tag in which case it's not deleteable
// if (ct.TagGroupMap.Any(e => e.TagGroupMapId == inObj.Id))
// {
// AddError(ValidationErrorType.ReferentialIntegrity, "object", "Can't be deleted while has relations");
// }
}
/////////////////////////////////////////////////////////////////////
}//eoc
}//eons

View File

@@ -1,184 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.JsonPatch;
using EnumsNET;
using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
using AyaNova.Models;
using System.Collections.Generic;
namespace AyaNova.Biz
{
internal class TagMapBiz : BizObject
{
internal TagMapBiz(AyContext dbcontext, long currentUserId, AuthorizationRoles userRoles)
{
ct = dbcontext;
UserId = currentUserId;
CurrentUserRoles = userRoles;
BizType = AyaType.TagMap;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
internal async Task<TagMap> CreateAsync(TagMapInfo inObj)
{
//Is this tag already present?
var Existing = await ct.TagMap.Where(m => m.TagId == inObj.TagId && m.TagToObjectId == inObj.TagToObjectId && m.TagToObjectType == inObj.TagToObjectType).FirstOrDefaultAsync();
if (Existing != null)
{
//yup so just return it, don't make a new one
return Existing;
}
Validate(inObj, true);
if (HasErrors)
return null;
else
{
//do stuff with TagMap
TagMap outObj = new TagMap()
{
TagId = inObj.TagId,
TagToObjectId = inObj.TagToObjectId,
TagToObjectType = inObj.TagToObjectType,
OwnerId = UserId
};
await ct.TagMap.AddAsync(outObj);
return outObj;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
//Get one
internal async Task<TagMap> GetAsync(long fetchId)
{
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
return await ct.TagMap.SingleOrDefaultAsync(m => m.Id == fetchId);
}
internal async Task<List<NameIdItem>> GetTagsOnObjectListAsync(AyaTypeId tid)
{
/*
NOTES: This will be a bit of a "hot" path as every fetch of any main object will involve this
for now, just make it work and later can improve performance
Also is sort going to be adequate, it's supposed to be based on invariant culture
*/
List<NameIdItem> l = new List<NameIdItem>();
//Get the list of tags on the object
var tagmapsOnObject = await ct.TagMap
.Where(m => m.TagToObjectId == tid.ObjectId && m.TagToObjectType == tid.ObjectType)
.Select(m => m.TagId)
.ToListAsync();
foreach (long tagId in tagmapsOnObject)
{
var tagFromDb = await ct.Tag.SingleOrDefaultAsync(m => m.Id == tagId);
if (tagFromDb != null)
{
l.Add(new NameIdItem() { Id = tagFromDb.Id, Name = tagFromDb.Name });
}
}
//Return the list sorted alphabetically
//Note if this is commonly required then maybe make a helper / extension for it
return (l.OrderBy(o => o.Name).ToList());
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//
internal bool Delete(TagMap dbObj)
{
//Determine if the object can be deleted, do the deletion tentatively
ValidateCanDelete(dbObj);
if (HasErrors)
return false;
ct.TagMap.Remove(dbObj);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE ALL TAGMAPS FOR OBJECT
//
static internal bool DeleteAllForObject(AyaTypeId parentObj, AyContext ct)
{
//Be careful in future, if you put ToString at the end of each object in the string interpolation
//npgsql driver will assume it's a string and put quotes around it triggering an error that a string can't be compared to an int
ct.Database.ExecuteSqlCommand($"delete from atagmap where tagtoobjectid={parentObj.ObjectId} and tagtoobjecttype={parentObj.ObjectTypeAsInt}");
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION
//
//Can save or update?
private void Validate(TagMapInfo inObj, bool isNew)
{
//run validation and biz rules
// //Name required
// if (string.IsNullOrWhiteSpace(inObj))
// AddError(ValidationErrorType.RequiredPropertyEmpty, "Name");
// //Name must be less than 255 characters
// if (inObj.Length > 255)
// AddError(ValidationErrorType.LengthExceeded, "Name", "255 char max");
return;
}
//Can delete?
private void ValidateCanDelete(TagMap inObj)
{
//whatever needs to be check to delete this object
//See if any tagmaps exist with this tag in which case it's not deleteable
// if (ct.TagMap.Any(e => e.TagMapId == inObj.Id))
// {
// AddError(ValidationErrorType.ReferentialIntegrity, "object", "Can't be deleted while has relations");
// }
}
/////////////////////////////////////////////////////////////////////
}//eoc
}//eons

View File

@@ -1,15 +0,0 @@
using System;
namespace AyaNova.Biz
{
/// <summary>
/// Marker attribute indicating that an object supports tagging
/// Used in <see cref="AyaType"/>
/// </summary>
[AttributeUsage(AttributeTargets.All)]
public class TaggableAttribute : Attribute
{
//No code required, it's just a marker
//https://docs.microsoft.com/en-us/dotnet/standard/attributes/writing-custom-attributes
}
}//eons

View File

@@ -321,9 +321,9 @@ namespace AyaNova.Biz
//Delete search index
Search.ProcessDeletedObjectKeywords(dbObj.Id, BizType);
//TAGS
TagMapBiz.DeleteAllForObject(new AyaTypeId(BizType, dbObj.Id), ct);
ct.SaveChanges();
// //TAGS
// TagMapBiz.DeleteAllForObject(new AyaTypeId(BizType, dbObj.Id), ct);
// ct.SaveChanges();
return true;
}

View File

@@ -346,9 +346,9 @@ namespace AyaNova.Biz
//Delete search index
Search.ProcessDeletedObjectKeywords(dbObj.Id, BizType);
//TAGS
TagMapBiz.DeleteAllForObject(new AyaTypeId(BizType, dbObj.Id), ct);
ct.SaveChanges();
// //TAGS
// TagMapBiz.DeleteAllForObject(new AyaTypeId(BizType, dbObj.Id), ct);
// ct.SaveChanges();
return true;
}

View File

@@ -15,11 +15,7 @@ namespace AyaNova.Models
public virtual DbSet<UserOptions> UserOptions { get; set; }
public virtual DbSet<License> License { get; set; }
public virtual DbSet<Widget> Widget { get; set; }
public virtual DbSet<FileAttachment> FileAttachment { get; set; }
public virtual DbSet<Tag> Tag { get; set; }
public virtual DbSet<TagMap> TagMap { get; set; }
public virtual DbSet<TagGroup> TagGroup { get; set; }
public virtual DbSet<TagGroupMap> TagGroupMap { get; set; }
public virtual DbSet<FileAttachment> FileAttachment { get; set; }
public virtual DbSet<OpsJob> OpsJob { get; set; }
public virtual DbSet<OpsJobLog> OpsJobLog { get; set; }
public virtual DbSet<Locale> Locale { get; set; }

View File

@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using AyaNova.Biz;
using System.ComponentModel.DataAnnotations;
namespace AyaNova.Models
{
public partial class Tag
{
public long Id { get; set; }
public uint ConcurrencyToken { get; set; }
[Required]
public long OwnerId { get; set; }
[Required, MaxLength(255)]
public string Name { get; set; }//max 255 characters ascii set
}
}

View File

@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using AyaNova.Biz;
using System.ComponentModel.DataAnnotations;
namespace AyaNova.Models
{
public partial class TagGroup
{
public long Id { get; set; }
public uint ConcurrencyToken { get; set; }
[Required]
public long OwnerId { get; set; }
[Required, MaxLength(255)]
public string Name { get; set; }//max 255 characters ascii set
}
}

View File

@@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using AyaNova.Biz;
using System.ComponentModel.DataAnnotations;
namespace AyaNova.Models
{
public partial class TagGroupMap
{
public long Id { get; set; }
public uint ConcurrencyToken { get; set; }
[Required]
public long OwnerId { get; set; }
[Required]
public long TagId { get; set; }
[Required]
public long TagGroupId { get; set; }
}
}

View File

@@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using AyaNova.Biz;
using System.ComponentModel.DataAnnotations;
namespace AyaNova.Models
{
public partial class TagMap
{
public long Id { get; set; }
public uint ConcurrencyToken { get; set; }
[Required]
public long OwnerId { get; set; }
[Required]
public long TagId { get; set; }
[Required]
public long TagToObjectId { get; set; }
[Required]
public AyaType TagToObjectType { get; set; }
}
}

View File

@@ -1,13 +0,0 @@
using AyaNova.Biz;
namespace AyaNova.Models
{
public class TagGroupMapInfo
{
public long TagId { get; set; }
public long TagGroupId { get; set; }
}
}

View File

@@ -1,15 +0,0 @@
using AyaNova.Biz;
namespace AyaNova.Models
{
//Used by TagGroup controller to map a group of tags to an object
public class TagMapGroupInfo
{
public long TagGroupId { get; set; }
public long TagToObjectId { get; set; }
public AyaType TagToObjectType { get; set; }
}
}

View File

@@ -1,14 +0,0 @@
using AyaNova.Biz;
namespace AyaNova.Models
{
//Used by Tag controller to map a tag to an object
public class TagMapInfo
{
public long TagId { get; set; }
public long TagToObjectId { get; set; }
public AyaType TagToObjectType { get; set; }
}
}

View File

@@ -239,31 +239,31 @@ namespace AyaNova.Util
}
//////////////////////////////////////////////////
//TAG tables
if (currentSchema < 6)
{
LogUpdateMessage(log);
// LOOKAT: Should taggroupmap have an index that enforces no taggroup can have the same tag more than once? Same for objects being tagged?
exec("CREATE TABLE atag (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, name varchar(255) not null)");
// //////////////////////////////////////////////////
// //TAG tables
// if (currentSchema < 6)
// {
// LogUpdateMessage(log);
// // LOOKAT: Should taggroupmap have an index that enforces no taggroup can have the same tag more than once? Same for objects being tagged?
// exec("CREATE TABLE atag (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, name varchar(255) not null)");
//LOOKAT: isn't this useless without the ID? Need to see if it's being used after unit testing
exec("CREATE UNIQUE INDEX atag_name_idx ON atag (name);");
// //LOOKAT: isn't this useless without the ID? Need to see if it's being used after unit testing
// exec("CREATE UNIQUE INDEX atag_name_idx ON atag (name);");
exec("CREATE TABLE atagmap (id BIGSERIAL PRIMARY KEY, ownerid bigint not null," +
"tagid bigint not null REFERENCES atag (id), tagtoobjectid bigint not null, tagtoobjecttype integer not null)");
// exec("CREATE TABLE atagmap (id BIGSERIAL PRIMARY KEY, ownerid bigint not null," +
// "tagid bigint not null REFERENCES atag (id), tagtoobjectid bigint not null, tagtoobjecttype integer not null)");
//Taggroup
exec("CREATE TABLE ataggroup (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, name varchar(255) not null)");
exec("CREATE TABLE ataggroupmap (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, tagid bigint not null REFERENCES atag (id), taggroupid bigint not null)");
// //Taggroup
// exec("CREATE TABLE ataggroup (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, name varchar(255) not null)");
// exec("CREATE TABLE ataggroupmap (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, tagid bigint not null REFERENCES atag (id), taggroupid bigint not null)");
setSchemaLevel(++currentSchema);
}
// setSchemaLevel(++currentSchema);
// }
//////////////////////////////////////////////////
// OPS LRO tables
if (currentSchema < 7)
if (currentSchema < 6)
{
LogUpdateMessage(log);
@@ -279,7 +279,7 @@ namespace AyaNova.Util
//LICENSE table new columns
//LOOKAT: DO I need this anymore???
//answer: no because it relates to ops stuff in other tables and logging, not to the license itself (except maybe dbid?)
if (currentSchema < 8)
if (currentSchema < 7)
{
LogUpdateMessage(log);
@@ -295,7 +295,7 @@ namespace AyaNova.Util
//////////////////////////////////////////////////
//DATAFILTER table
if (currentSchema < 9)
if (currentSchema < 8)
{
LogUpdateMessage(log);

View File

@@ -275,8 +275,7 @@ namespace AyaNova.Util
//THIS METHOD IS ONLY CALLED BY SEEDER
//SO ONLY REMOVE DATA THAT IS SEEDED
//I.E. Normal user business data, not infrastructure data like license or localized text etc
EraseTable("atagmap", conn);
EraseTable("atag", conn);
EraseTable("afileattachment", conn);
EraseTable("awidget", conn);
EraseTable("aevent", conn);

View File

@@ -25,7 +25,7 @@ namespace raven_integration
//Number 2 is widget and list is zero based so confirm:
a.ObjectResponse["data"][2]["id"].Value<long>().Should().Be(2);
a.ObjectResponse["data"][2]["name"].Value<string>().Should().Be("Widget [Attachable] [Taggable]");
a.ObjectResponse["data"][2]["name"].Value<string>().Should().Be("Widget [Attachable]");
}