This commit is contained in:
315
server/AyaNova/biz/UserBiz.cs
Normal file
315
server/AyaNova/biz/UserBiz.cs
Normal file
@@ -0,0 +1,315 @@
|
||||
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;
|
||||
|
||||
|
||||
namespace AyaNova.Biz
|
||||
{
|
||||
|
||||
|
||||
internal class UserBiz : BizObject, IJobObject
|
||||
{
|
||||
private readonly AyContext ct;
|
||||
public readonly long userId;
|
||||
private readonly AuthorizationRoles userRoles;
|
||||
|
||||
|
||||
internal UserBiz(AyContext dbcontext, long currentUserId, AuthorizationRoles UserRoles)
|
||||
{
|
||||
ct = dbcontext;
|
||||
userId = currentUserId;
|
||||
userRoles = UserRoles;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//CREATE
|
||||
internal async Task<User> CreateAsync(User inObj)
|
||||
{
|
||||
Validate(inObj, true);
|
||||
if (HasErrors)
|
||||
return null;
|
||||
else
|
||||
{
|
||||
//do stuff with User
|
||||
User outObj = inObj;
|
||||
outObj.OwnerId = userId;
|
||||
//SearchHelper(break down text fields, save to db)
|
||||
//TagHelper(collection of tags??)
|
||||
await ct.User.AddAsync(outObj);
|
||||
return outObj;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// GET
|
||||
|
||||
//Get one
|
||||
internal async Task<User> 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.User.SingleOrDefaultAsync(m => m.Id == fetchId);
|
||||
}
|
||||
|
||||
//get many (paged)
|
||||
internal async Task<ApiPagedResponse<User>> GetManyAsync(IUrlHelper Url, string routeName, PagingOptions pagingOptions)
|
||||
{
|
||||
|
||||
pagingOptions.Offset = pagingOptions.Offset ?? PagingOptions.DefaultOffset;
|
||||
pagingOptions.Limit = pagingOptions.Limit ?? PagingOptions.DefaultLimit;
|
||||
|
||||
var items = await ct.User
|
||||
.OrderBy(m => m.Id)
|
||||
.Skip(pagingOptions.Offset.Value)
|
||||
.Take(pagingOptions.Limit.Value)
|
||||
.ToArrayAsync();
|
||||
|
||||
var totalRecordCount = await ct.User.CountAsync();
|
||||
var pageLinks = new PaginationLinkBuilder(Url, routeName, null, pagingOptions, totalRecordCount).PagingLinksObject();
|
||||
|
||||
ApiPagedResponse<User> pr = new ApiPagedResponse<User>(items, pageLinks);
|
||||
return pr;
|
||||
}
|
||||
|
||||
|
||||
//get picklist (paged)
|
||||
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))
|
||||
{
|
||||
items = await ct.User
|
||||
.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.User.Where(m => EF.Functions.ILike(m.Name, q)).CountAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
items = await ct.User
|
||||
.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.User.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(User dbObj, User inObj)
|
||||
{
|
||||
|
||||
//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, false);
|
||||
if (HasErrors)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//patch
|
||||
internal bool Patch(User dbObj, JsonPatchDocument<User> objectPatch, uint concurrencyToken)
|
||||
{
|
||||
//Do the patching
|
||||
objectPatch.ApplyTo(dbObj);
|
||||
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = concurrencyToken;
|
||||
Validate(dbObj, false);
|
||||
if (HasErrors)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//DELETE
|
||||
//
|
||||
|
||||
internal bool Delete(User dbObj)
|
||||
{
|
||||
//Determine if the object can be deleted, do the deletion tentatively
|
||||
//Probably also in here deal with tags and associated search text etc
|
||||
|
||||
ValidateCanDelete(dbObj);
|
||||
if (HasErrors)
|
||||
return false;
|
||||
ct.User.Remove(dbObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete child objects like tags and attachments and etc
|
||||
/// </summary>
|
||||
/// <param name="dbObj"></param>
|
||||
internal void DeleteChildren(User dbObj)
|
||||
{
|
||||
//TAGS
|
||||
TagMapBiz.DeleteAllForObject(new AyaTypeId(AyaType.User, dbObj.Id), ct);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//VALIDATION
|
||||
//
|
||||
|
||||
//Can save or update?
|
||||
private void Validate(User inObj, bool isNew)
|
||||
{
|
||||
//run validation and biz rules
|
||||
if (isNew)
|
||||
{
|
||||
//NEW Users must be active
|
||||
if (inObj.Active == null || ((bool)inObj.Active) == false)
|
||||
{
|
||||
AddError(ValidationErrorType.InvalidValue, "Active", "New User must be active");
|
||||
}
|
||||
}
|
||||
|
||||
//OwnerId required
|
||||
if (!isNew)
|
||||
{
|
||||
if (inObj.OwnerId == 0)
|
||||
AddError(ValidationErrorType.RequiredPropertyEmpty, "OwnerId");
|
||||
}
|
||||
|
||||
//Name required
|
||||
if (string.IsNullOrWhiteSpace(inObj.Name))
|
||||
AddError(ValidationErrorType.RequiredPropertyEmpty, "Name");
|
||||
|
||||
//Name must be less than 255 characters
|
||||
if (inObj.Name.Length > 255)
|
||||
AddError(ValidationErrorType.LengthExceeded, "Name", "255 max");
|
||||
|
||||
//If name is otherwise OK, check that name is unique
|
||||
if (!PropertyHasErrors("Name"))
|
||||
{
|
||||
//Use Any command is efficient way to check existance, it doesn't return the record, just a true or false
|
||||
if (ct.User.Any(m => m.Name == inObj.Name && m.Id != inObj.Id))
|
||||
{
|
||||
AddError(ValidationErrorType.NotUnique, "Name");
|
||||
}
|
||||
}
|
||||
|
||||
//Start date AND end date must both be null or both contain values
|
||||
if (inObj.StartDate == null && inObj.EndDate != null)
|
||||
AddError(ValidationErrorType.RequiredPropertyEmpty, "StartDate");
|
||||
|
||||
if (inObj.StartDate != null && inObj.EndDate == null)
|
||||
AddError(ValidationErrorType.RequiredPropertyEmpty, "EndDate");
|
||||
|
||||
//Start date before end date
|
||||
if (inObj.StartDate != null && inObj.EndDate != null)
|
||||
if (inObj.StartDate > inObj.EndDate)
|
||||
AddError(ValidationErrorType.StartDateMustComeBeforeEndDate, "StartDate");
|
||||
|
||||
//Enum is valid value
|
||||
|
||||
if (!inObj.Roles.IsValid())
|
||||
{
|
||||
AddError(ValidationErrorType.InvalidValue, "Roles");
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//Can delete?
|
||||
private void ValidateCanDelete(User inObj)
|
||||
{
|
||||
//whatever needs to be check to delete this object
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//JOB / OPERATIONS
|
||||
//
|
||||
public async Task HandleJobAsync(OpsJob job)
|
||||
{
|
||||
//Hand off the particular job to the corresponding processing code
|
||||
//NOTE: If this code throws an exception the caller (JobsBiz::ProcessJobsAsync) will automatically set the job to failed and log the exeption so
|
||||
//basically any error condition during job processing should throw up an exception if it can't be handled
|
||||
switch (job.JobType)
|
||||
{
|
||||
case JobType.TestUserJob:
|
||||
await ProcessTestJobAsync(job);
|
||||
break;
|
||||
default:
|
||||
throw new System.ArgumentOutOfRangeException($"UserBiz.HandleJob-> Invalid job type{job.JobType.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// /// Handle the test job
|
||||
/// </summary>
|
||||
/// <param name="job"></param>
|
||||
private async Task ProcessTestJobAsync(OpsJob job)
|
||||
{
|
||||
var sleepTime = 30 * 1000;
|
||||
//Simulate a long running job here
|
||||
JobsBiz.UpdateJobStatus(job.GId, JobStatus.Running, ct);
|
||||
JobsBiz.LogJob(job.GId, $"UserBiz::ProcessTestJob started, sleeping for {sleepTime} seconds...", ct);
|
||||
//Uncomment this to test if the job prevents other routes from running
|
||||
//result is NO it doesn't prevent other requests, so we are a-ok for now
|
||||
await Task.Delay(sleepTime);
|
||||
JobsBiz.LogJob(job.GId, "UserBiz::ProcessTestJob done sleeping setting job to finished", ct);
|
||||
JobsBiz.UpdateJobStatus(job.GId, JobStatus.Completed, ct);
|
||||
|
||||
}
|
||||
|
||||
//Other job handlers here...
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
}//eoc
|
||||
|
||||
|
||||
}//eons
|
||||
|
||||
72
server/AyaNova/biz/UserType.cs
Normal file
72
server/AyaNova/biz/UserType.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
namespace AyaNova.Biz
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// All AyaNova user types
|
||||
/// </summary>
|
||||
public enum UserType : int
|
||||
{
|
||||
Administrator = 1,
|
||||
Schedulable = 2,
|
||||
NonSchedulable = 3,
|
||||
Client = 4,
|
||||
HeadOffice = 5,
|
||||
Utility = 6,
|
||||
Subcontractor = 7
|
||||
}
|
||||
|
||||
|
||||
}//eons
|
||||
|
||||
/*
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// UserTypes.cs
|
||||
// Implementation of Class UserTypes
|
||||
// CSLA type: enumeration
|
||||
// Created on: 07-Jun-2004 8:41:44 AM
|
||||
// Object design: Joyce
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
|
||||
namespace GZTW.AyaNova.BLL {
|
||||
/// <summary>
|
||||
/// Variations of <see cref="User"/> types
|
||||
/// </summary>
|
||||
public enum UserTypes : int {
|
||||
/// <summary>
|
||||
/// This is the special account used only for updates, for customizing layout, etc
|
||||
/// </summary>
|
||||
[Description("LT:UserTypes.Label.Administrator")] Administrator = 1,
|
||||
/// <summary>
|
||||
/// This is a user that can be assigned to a workorder or schedule and consumes an AyaNova license
|
||||
/// </summary>
|
||||
[Description("LT:UserTypes.Label.Schedulable")] Schedulable = 2,
|
||||
/// <summary>
|
||||
/// Standard user that can use all aspects of AyaNova but can not be chosen to assign to a work order or schedule
|
||||
/// Does not consume an AyaNova license
|
||||
/// </summary>
|
||||
[Description("LT:UserTypes.Label.NonSchedulable")] NonSchedulable = 3,
|
||||
/// <summary>
|
||||
/// Client's login user account for WBI
|
||||
/// </summary>
|
||||
[Description("LT:UserTypes.Label.Client")] Client = 4,
|
||||
/// <summary>
|
||||
/// HeadOffice's login user account for WBI
|
||||
/// </summary>
|
||||
[Description("LT:UserTypes.Label.HeadOffice")] HeadOffice = 5,
|
||||
/// <summary>
|
||||
/// Utility user type for internal processing with limited functionality
|
||||
/// ATTN. API users: Do not create or set a user to this type in your code, you will be dissapointed and will break things. :)
|
||||
/// </summary>
|
||||
[Description("UTILITY")] Utility = 6
|
||||
//*** IF MORE ADDED USER OBJECT USERTYPE PROPERTIES
|
||||
// BROKEN RULES MUST BE UPDATED AND SQL SERVER USER TABLE CHECK CONSTRAINT
|
||||
// FOR USERTYPE VALUE ***
|
||||
|
||||
}//end UserTypes
|
||||
|
||||
}//end namespace GZTW.AyaNova.BLL
|
||||
*/
|
||||
@@ -11,14 +11,107 @@ namespace AyaNova.Models
|
||||
public uint ConcurrencyToken { get; set; }
|
||||
[Required]
|
||||
public long OwnerId { get; set; }
|
||||
[Required]
|
||||
public bool Active { get; set; }
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
[Required]
|
||||
public string Login { get; set; }
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
[Required]
|
||||
public string Salt { get; set; }
|
||||
public AuthorizationRoles Roles { get; set; }
|
||||
[Required]
|
||||
public AuthorizationRoles Roles { get; set; }
|
||||
[Required]
|
||||
public long LocaleId { get; set; }
|
||||
public string DlKey { get; set; }
|
||||
public DateTime? DlKeyExpire { get; set; }
|
||||
public long LocaleId { get; set; }
|
||||
|
||||
[Required]
|
||||
public UserType UserType { get; set; }
|
||||
public string EmployeeNumber { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public long? ClientId { get; set; }
|
||||
public long? HeadOfficeId { get; set; }
|
||||
public long? SubVendorId { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
v7 export record sample
|
||||
{
|
||||
"DefaultLanguage": "Custom English",
|
||||
"DefaultServiceTemplateID": "ca83a7b8-4e5f-4a7b-a02b-9cf78d5f983f",
|
||||
"UserType": 2,
|
||||
"Active": true,
|
||||
"ClientID": "00000000-0000-0000-0000-000000000000",
|
||||
"HeadOfficeID": "00000000-0000-0000-0000-000000000000",
|
||||
"MemberOfGroup": "0f8a80ff-4b03-4114-ae51-2d13b812dd65",
|
||||
"Created": "03/21/2005 07:19 AM",
|
||||
"Modified": "09/15/2015 12:22 PM",
|
||||
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"Modifier": "1d859264-3f32-462a-9b0c-a67dddfdf4d3",
|
||||
"ID": "1d859264-3f32-462a-9b0c-a67dddfdf4d3",
|
||||
"FirstName": "Hank",
|
||||
"LastName": "Rearden",
|
||||
"Initials": "HR",
|
||||
"EmployeeNumber": "EMP1236",
|
||||
"PageAddress": "",
|
||||
"PageMaxText": 24,
|
||||
"Phone1": "",
|
||||
"Phone2": "",
|
||||
"EmailAddress": "",
|
||||
"UserCertifications": [
|
||||
{
|
||||
"Created": "12/22/2005 02:07 PM",
|
||||
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"Modified": "12/22/2005 02:08 PM",
|
||||
"Modifier": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"ID": "4492360c-43e4-4209-9f33-30691b0808ed",
|
||||
"UserCertificationID": "b2f26359-7c42-4218-923a-e949f3ef1f85",
|
||||
"UserID": "1d859264-3f32-462a-9b0c-a67dddfdf4d3",
|
||||
"ValidStartDate": "2005-10-11T00:00:00-07:00",
|
||||
"ValidStopDate": "2006-10-11T00:00:00-07:00"
|
||||
}
|
||||
],
|
||||
"UserSkills": [
|
||||
{
|
||||
"Created": "12/22/2005 02:06 PM",
|
||||
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"Modified": "12/22/2005 02:08 PM",
|
||||
"Modifier": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"ID": "1dc5ce96-f411-4885-856e-5bdb3ad79728",
|
||||
"UserSkillID": "2e6f8b65-594c-4f6c-9cd6-e14a562daba8",
|
||||
"UserID": "1d859264-3f32-462a-9b0c-a67dddfdf4d3"
|
||||
},
|
||||
{
|
||||
"Created": "12/22/2005 02:06 PM",
|
||||
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"Modified": "12/22/2005 02:08 PM",
|
||||
"Modifier": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"ID": "88e476d3-7526-45f5-a0dd-706c8053a63f",
|
||||
"UserSkillID": "47a4ee94-b0e9-41b5-afe5-4b4f2c981877",
|
||||
"UserID": "1d859264-3f32-462a-9b0c-a67dddfdf4d3"
|
||||
}
|
||||
],
|
||||
"Notes": "",
|
||||
"VendorID": "06e502c2-69ba-4e88-8efb-5b53c1687740",
|
||||
"RegionID": "f856423a-d468-4344-b7b8-121e466738c6",
|
||||
"DispatchZoneID": "00000000-0000-0000-0000-000000000000",
|
||||
"SubContractor": false,
|
||||
"DefaultWarehouseID": "d45eab37-b6e6-4ad2-9163-66d7ba83a98c",
|
||||
"Custom1": "",
|
||||
"Custom2": "",
|
||||
"Custom3": "",
|
||||
"Custom4": "",
|
||||
"Custom5": "",
|
||||
"Custom6": "",
|
||||
"Custom7": "",
|
||||
"Custom8": "",
|
||||
"Custom9": "",
|
||||
"Custom0": "",
|
||||
"ScheduleBackColor": -2097216,
|
||||
"TimeZoneOffset": null
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -139,8 +139,8 @@ namespace AyaNova.Util
|
||||
AyaNova.Biz.PrimeData.PrimeLocales(ct);
|
||||
|
||||
//Add user table
|
||||
exec("CREATE TABLE auser (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, name varchar(255) not null, " +
|
||||
"login text not null, password text not null, salt text not null, roles integer not null, localeid bigint REFERENCES alocale (id), " +
|
||||
exec("CREATE TABLE auser (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, active bool not null, name varchar(255) not null, " +
|
||||
"login text not null, password text not null, salt text not null, roles integer not null, localeid bigint not null REFERENCES alocale (id), " +
|
||||
"dlkey text, dlkeyexpire timestamp)");
|
||||
|
||||
//Prime the db with the default MANAGER account
|
||||
|
||||
Reference in New Issue
Block a user