diff --git a/backend/.editorconfig b/backend/.editorconfig
index 3aba8dbd07..44f1362abb 100644
--- a/backend/.editorconfig
+++ b/backend/.editorconfig
@@ -116,9 +116,8 @@ dotnet_diagnostic.SA1124.severity = none
# SA1507 Code should not contain multiple blank lines in a row
dotnet_diagnostic.SA1507.severity = none
-
# Entity Framework files
-[**{entities/ef/*,PIMSContext}.cs]
+[**{entities/ef/*,PIMSContext,PimsBaseContext}.cs]
# CS8019: Using directive is unnecessary.
dotnet_diagnostic.CS8019.severity = none
diff --git a/backend/api/Areas/Leases/Models/Lease/PropertyModel.cs b/backend/api/Areas/Leases/Models/Lease/PropertyModel.cs
index 1b97404e37..51d8bc0a5f 100644
--- a/backend/api/Areas/Leases/Models/Lease/PropertyModel.cs
+++ b/backend/api/Areas/Leases/Models/Lease/PropertyModel.cs
@@ -1,5 +1,5 @@
-using Pims.Api.Models;
using System;
+using Pims.Api.Models;
namespace Pims.Api.Areas.Lease.Models.Lease
{
diff --git a/backend/api/Areas/Notes/Controllers/NoteController.cs b/backend/api/Areas/Notes/Controllers/NoteController.cs
new file mode 100644
index 0000000000..c81bfeeed1
--- /dev/null
+++ b/backend/api/Areas/Notes/Controllers/NoteController.cs
@@ -0,0 +1,102 @@
+
+using MapsterMapper;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Pims.Api.Constants;
+using Pims.Api.Models.Concepts;
+using Pims.Api.Policies;
+using Pims.Api.Services;
+using Pims.Dal.Security;
+using System.Collections.Generic;
+using Swashbuckle.AspNetCore.Annotations;
+
+namespace Pims.Api.Areas.Notes.Controllers
+{
+ ///
+ /// NoteController class, provides endpoints for interacting with notes.
+ ///
+ [Authorize]
+ [ApiController]
+ [ApiVersion("1.0")]
+ [Area("notes")]
+ [Route("v{version:apiVersion}/[area]")]
+ [Route("[area]")]
+ public class NoteController : ControllerBase
+ {
+ #region Variables
+ private readonly INoteService _noteService;
+ private readonly IMapper _mapper;
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Creates a new instance of a NoteController class, initializes it with the specified arguments.
+ ///
+ ///
+ ///
+ ///
+ public NoteController(INoteService noteService, IMapper mapper)
+ {
+ _noteService = noteService;
+ _mapper = mapper;
+ }
+ #endregion
+
+ #region Endpoints
+
+ ///
+ /// Add the specified note.
+ ///
+ /// The parent entity type.
+ /// The note to add.
+ ///
+ [HttpPost("{type}")]
+ [HasPermission(Permissions.NoteAdd)]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(EntityNoteModel), 200)]
+ [SwaggerOperation(Tags = new[] { "note" })]
+ public IActionResult AddNote(NoteType type, [FromBody] EntityNoteModel noteModel)
+ {
+ var createdNote = _noteService.Add(type, noteModel);
+ return new JsonResult(createdNote);
+ }
+
+ ///
+ /// Get the notes for the specified type and entity id.
+ ///
+ /// Used to identify note type.
+ /// Used to identify note's parent entity.
+ ///
+ [HttpGet("{type}/{entityId}")]
+ [Produces("application/json")]
+ [HasPermission(Permissions.NoteView)]
+ [ProducesResponseType(typeof(IEnumerable), 200)]
+ [SwaggerOperation(Tags = new[] { "note" })]
+ public IActionResult GetNotes(NoteType type, long entityId)
+ {
+ var notes = _noteService.GetNotes(type, entityId);
+ var mappedNotes = _mapper.Map>(notes);
+ return new JsonResult(mappedNotes);
+ }
+
+ ///
+ /// Deletes the note for the specified type.
+ ///
+ /// Used to identify note type.
+ /// Used to identify the note and delete it.
+ ///
+ [HttpDelete("{type}/{noteId}")]
+ [Produces("application/json")]
+ [HasPermission(Permissions.NoteDelete)]
+ [ProducesResponseType(typeof(bool), 200)]
+ [SwaggerOperation(Tags = new[] { "note" })]
+ public IActionResult DeleteNote(NoteType type, int noteId)
+ {
+ _noteService.DeleteNote(type, noteId);
+ return new JsonResult(true);
+ }
+ #endregion
+ }
+}
diff --git a/backend/api/Constants/NoteType.cs b/backend/api/Constants/NoteType.cs
new file mode 100644
index 0000000000..8047cc6225
--- /dev/null
+++ b/backend/api/Constants/NoteType.cs
@@ -0,0 +1,14 @@
+using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
+
+namespace Pims.Api.Constants
+{
+ [JsonConverter(typeof(JsonStringEnumMemberConverter))]
+ public enum NoteType
+ {
+ [EnumMember(Value = "activity")]
+ Activity,
+ [EnumMember(Value = "file")]
+ File
+ }
+}
diff --git a/backend/api/Controllers/DocumentController.cs b/backend/api/Controllers/DocumentController.cs
index 36c378a6cb..b2c30d52ae 100644
--- a/backend/api/Controllers/DocumentController.cs
+++ b/backend/api/Controllers/DocumentController.cs
@@ -1,8 +1,14 @@
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Pims.Api.Models;
+using Pims.Api.Models.Mayan;
+using Pims.Api.Models.Mayan.Document;
+using Pims.Api.Models.Mayan.Sync;
using Pims.Api.Policies;
using Pims.Api.Services;
+using Pims.Dal.Entities;
using Pims.Dal.Security;
using Swashbuckle.AspNetCore.Annotations;
@@ -20,6 +26,7 @@ public class DocumentController : ControllerBase
{
#region Variables
private readonly IDocumentService _documentService;
+ private readonly IDocumentSyncService _documentSyncService;
#endregion
#region Constructors
@@ -27,9 +34,11 @@ public class DocumentController : ControllerBase
/// Creates a new instance of a ErrorController class.
///
///
- public DocumentController(IDocumentService documentService)
+ ///
+ public DocumentController(IDocumentService documentService, IDocumentSyncService documentSyncService)
{
_documentService = documentService;
+ _documentSyncService = documentSyncService;
}
#endregion
@@ -40,12 +49,12 @@ public DocumentController(IDocumentService documentService)
///
[HttpGet("types")]
[HasPermission(Permissions.PropertyAdd)]
- [ProducesResponseType(typeof(string), 200)]
+ [ProducesResponseType(typeof(ExternalResult>), 200)]
[SwaggerOperation(Tags = new[] { "documents" })]
public IActionResult GetDocumentTypes()
{
- var ast = _documentService.GetDocumentTypes();
- return new JsonResult(ast);
+ var result = _documentService.GetDocumentTypes();
+ return new JsonResult(result);
}
///
@@ -54,12 +63,12 @@ public IActionResult GetDocumentTypes()
[HttpGet]
[HasPermission(Permissions.PropertyAdd)]
[Produces("application/json")]
- [ProducesResponseType(typeof(string), 200)]
+ [ProducesResponseType(typeof(ExternalResult>), 200)]
[SwaggerOperation(Tags = new[] { "documents" })]
public IActionResult GetDocumentList()
{
- var ast = _documentService.GetDocumentList();
- return new JsonResult(ast);
+ var result = _documentService.GetDocumentList();
+ return new JsonResult(result);
}
///
@@ -67,12 +76,12 @@ public IActionResult GetDocumentList()
///
[HttpGet("{documentId}/files/{fileId}/download")]
[HasPermission(Permissions.PropertyAdd)]
- [ProducesResponseType(typeof(string), 200)]
+ [ProducesResponseType(typeof(ExternalResult), 200)]
[SwaggerOperation(Tags = new[] { "documents" })]
public IActionResult DownloadFile(int documentId, int fileId)
{
- var ast = _documentService.DownloadFile(documentId, fileId);
- return new JsonResult(ast);
+ var result = _documentService.DownloadFile(documentId, fileId);
+ return new JsonResult(result);
}
///
@@ -80,12 +89,51 @@ public IActionResult DownloadFile(int documentId, int fileId)
///
[HttpPost]
[HasPermission(Permissions.PropertyAdd)]
- [ProducesResponseType(typeof(string), 200)]
+ [ProducesResponseType(typeof(ExternalResult), 200)]
[SwaggerOperation(Tags = new[] { "documents" })]
public IActionResult UploadDocument([FromForm] int documentType, [FromForm] IFormFile file)
{
- var ast = _documentService.UploadDocument(documentType, file);
- return new JsonResult(ast);
+ var result = _documentService.UploadDocument(documentType, file);
+ return new JsonResult(result);
+ }
+
+ ///
+ /// Uploads the passed document.
+ ///
+ [HttpPatch("sync/mayan/documenttype")]
+ //[HasPermission(Permissions.PropertyAdd)] // TODO: put the correct permission
+ [ProducesResponseType(typeof(ExternalBatchResult), 200)]
+ [SwaggerOperation(Tags = new[] { "documents" })]
+ public IActionResult SyncMayanDocumentTypes([FromBody] SyncModel model)
+ {
+ var result = _documentSyncService.SyncMayanDocumentTypes(model);
+ return new JsonResult(result);
+ }
+
+ ///
+ /// Uploads the passed document.
+ ///
+ [HttpPatch("sync/mayan/metadatatype")]
+ //[HasPermission(Permissions.PropertyAdd)] // TODO: put the correct permission
+ [ProducesResponseType(typeof(ExternalBatchResult), 200)]
+ [SwaggerOperation(Tags = new[] { "documents" })]
+ public IActionResult SyncMayanMetadataTypes([FromBody] SyncModel model)
+ {
+ var result = _documentSyncService.SyncMayanMetadataTypes(model);
+ return new JsonResult(result);
+ }
+
+ ///
+ /// Uploads the passed document.
+ ///
+ [HttpPatch("sync/backend/documenttype")]
+ //[HasPermission(Permissions.PropertyAdd)] // TODO: put the correct permission
+ [ProducesResponseType(typeof(PimsDocumentTyp), 200)]
+ [SwaggerOperation(Tags = new[] { "documents" })]
+ public async Task SyncDocumentTypes()
+ {
+ var result = await _documentSyncService.SyncBackendDocumentTypes();
+ return new JsonResult(result);
}
#endregion
diff --git a/backend/api/Helpers/Extensions/ExceptionExtensions.cs b/backend/api/Helpers/Extensions/ExceptionExtensions.cs
index bf182bab03..d7ee836d12 100644
--- a/backend/api/Helpers/Extensions/ExceptionExtensions.cs
+++ b/backend/api/Helpers/Extensions/ExceptionExtensions.cs
@@ -1,5 +1,5 @@
-using Pims.Api.Helpers.Exceptions;
using System;
+using Pims.Api.Helpers.Exceptions;
namespace Pims.Api.Helpers.Extensions
{
diff --git a/backend/api/Helpers/Middleware/LogResponseMiddleware.cs b/backend/api/Helpers/Middleware/LogResponseMiddleware.cs
index ce84a9982e..36210a6bd3 100644
--- a/backend/api/Helpers/Middleware/LogResponseMiddleware.cs
+++ b/backend/api/Helpers/Middleware/LogResponseMiddleware.cs
@@ -1,4 +1,3 @@
-using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading.Tasks;
diff --git a/backend/api/Models/Concepts/Note/ActivityNoteMap.cs b/backend/api/Models/Concepts/Note/ActivityNoteMap.cs
new file mode 100644
index 0000000000..cdce225499
--- /dev/null
+++ b/backend/api/Models/Concepts/Note/ActivityNoteMap.cs
@@ -0,0 +1,27 @@
+using Mapster;
+using Entity = Pims.Dal.Entities;
+
+namespace Pims.Api.Models.Concepts
+{
+ public class ActivityNoteMap : IRegister
+ {
+ public void Register(TypeAdapterConfig config)
+ {
+ config.NewConfig()
+ .PreserveReference(true)
+ .Map(dest => dest.Id, src => src.PimsActivityInstanceNoteId)
+ .Map(dest => dest.Note, src => src.Note)
+ .Map(dest => dest.Parent, src => src)
+ .Inherits();
+
+ config.NewConfig()
+ .Map(dest => dest.PimsActivityInstanceNoteId, src => src.Id)
+ .Map(dest => dest.Note, src => src.Note)
+ .Map(dest => dest.ActivityInstanceId, src => src.Parent.Id)
+ .Inherits();
+
+ config.NewConfig()
+ .ConstructUsing(src => new NoteParentModel { Id = src.ActivityInstanceId });
+ }
+ }
+}
diff --git a/backend/api/Models/Concepts/Note/EntityNoteModel.cs b/backend/api/Models/Concepts/Note/EntityNoteModel.cs
new file mode 100644
index 0000000000..0d37bf8444
--- /dev/null
+++ b/backend/api/Models/Concepts/Note/EntityNoteModel.cs
@@ -0,0 +1,27 @@
+namespace Pims.Api.Models.Concepts
+{
+ ///
+ /// EntityNoteModel class, provides a model to represent notes associated to entities.
+ ///
+ public class EntityNoteModel : BaseAppModel
+ {
+ #region Properties
+
+ ///
+ /// get/set - The id for this entity-note association.
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// get/set - The parent entity that owns this note. Notes are associated to parent entities.
+ ///
+ public NoteParentModel Parent { get; set; }
+
+ ///
+ /// get/set - The note model.
+ ///
+ public NoteModel Note { get; set; }
+
+ #endregion
+ }
+}
diff --git a/backend/api/Models/Concepts/Note/NoteMap.cs b/backend/api/Models/Concepts/Note/NoteMap.cs
new file mode 100644
index 0000000000..d0a4e79325
--- /dev/null
+++ b/backend/api/Models/Concepts/Note/NoteMap.cs
@@ -0,0 +1,23 @@
+using Mapster;
+using Entity = Pims.Dal.Entities;
+
+namespace Pims.Api.Models.Concepts
+{
+ public class NoteMap : IRegister
+ {
+ public void Register(TypeAdapterConfig config)
+ {
+ config.NewConfig()
+ .PreserveReference(true)
+ .Map(dest => dest.Id, src => src.Id)
+ .Map(dest => dest.Note, src => src.NoteTxt)
+ .Map(dest => dest.RowVersion, src => src.ConcurrencyControlNumber)
+ .Inherits();
+
+ config.NewConfig()
+ .Map(dest => dest.Id, src => src.Id)
+ .Map(dest => dest.NoteTxt, src => src.Note)
+ .Inherits();
+ }
+ }
+}
diff --git a/backend/api/Models/Concepts/Note/NoteModel.cs b/backend/api/Models/Concepts/Note/NoteModel.cs
new file mode 100644
index 0000000000..59fdb63b1f
--- /dev/null
+++ b/backend/api/Models/Concepts/Note/NoteModel.cs
@@ -0,0 +1,23 @@
+
+namespace Pims.Api.Models.Concepts
+{
+ ///
+ /// NoteModel class, provides a model to represent notes associated to entities.
+ ///
+ public class NoteModel : BaseAppModel
+ {
+ #region Properties
+
+ ///
+ /// get/set - The id for this note.
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// get/set - The note text contents.
+ ///
+ public string Note { get; set; }
+
+ #endregion
+ }
+}
diff --git a/backend/api/Models/Concepts/Note/NoteParentModel.cs b/backend/api/Models/Concepts/Note/NoteParentModel.cs
new file mode 100644
index 0000000000..7d22f962bf
--- /dev/null
+++ b/backend/api/Models/Concepts/Note/NoteParentModel.cs
@@ -0,0 +1,18 @@
+
+namespace Pims.Api.Models.Concepts
+{
+ ///
+ /// NoteParentModel class, provides a model to represent an entity that owns notes.
+ ///
+ public class NoteParentModel
+ {
+ #region Properties
+
+ ///
+ /// get/set - The id for this model.
+ ///
+ public long Id { get; set; }
+
+ #endregion
+ }
+}
diff --git a/backend/api/Models/ExternalResult.cs b/backend/api/Models/ExternalResult.cs
index df5de99cce..01636e9408 100644
--- a/backend/api/Models/ExternalResult.cs
+++ b/backend/api/Models/ExternalResult.cs
@@ -1,3 +1,5 @@
+using System.Net;
+
namespace Pims.Api.Models
{
///
@@ -19,5 +21,10 @@ public class ExternalResult
/// get/set - A description of the type.
///
public T Payload { get; set; }
+
+ ///
+ /// get/set - The http status code returned.
+ ///
+ public HttpStatusCode HttpStatusCode { get; set; }
}
}
\ No newline at end of file
diff --git a/backend/api/Models/Mayan/Document/DocumentTypeMetadataType.cs b/backend/api/Models/Mayan/Document/DocumentTypeMetadataType.cs
new file mode 100644
index 0000000000..2021bb615c
--- /dev/null
+++ b/backend/api/Models/Mayan/Document/DocumentTypeMetadataType.cs
@@ -0,0 +1,41 @@
+using System.Text.Json.Serialization;
+using Pims.Api.Models.Mayan.Metadata;
+
+namespace Pims.Api.Models.Mayan.Document
+{
+ ///
+ /// Represents a document type information.
+ ///
+ public class DocumentTypeMetadataType
+ {
+ ///
+ /// get/set - The document type metadata type relationship id.
+ ///
+ [JsonPropertyName("id")]
+ public long Id { get; set; }
+
+ ///
+ /// get/set - The document type definition.
+ ///
+ [JsonPropertyName("document_type")]
+ public DocumentType DocumentType { get; set; }
+
+ ///
+ /// get/set - The metadata type definition.
+ ///
+ [JsonPropertyName("metadata_type")]
+ public MetadataType MetadataType { get; set; }
+
+ ///
+ /// get/set - The metadata required flag.
+ ///
+ [JsonPropertyName("required")]
+ public bool Required { get; set; }
+
+ ///
+ /// get/set - Url request path for this information.
+ ///
+ [JsonPropertyName("url")]
+ public string Url { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/backend/api/Models/Mayan/Metadata/MetadataType.cs b/backend/api/Models/Mayan/Metadata/MetadataType.cs
new file mode 100644
index 0000000000..5c3d816375
--- /dev/null
+++ b/backend/api/Models/Mayan/Metadata/MetadataType.cs
@@ -0,0 +1,70 @@
+using System.Text.Json.Serialization;
+
+namespace Pims.Api.Models.Mayan.Metadata
+{
+ ///
+ /// Represents a mayan metadata type.
+ ///
+ public class MetadataType
+ {
+ ///
+ /// get/set - The default value.
+ ///
+ [JsonPropertyName("default")]
+ public string Default { get; set; }
+
+ ///
+ /// get/set - The metadata id.
+ ///
+ [JsonPropertyName("id")]
+ public long Id { get; set; }
+
+ ///
+ /// get/set - The metadata type label.
+ ///
+ [JsonPropertyName("label")]
+ public string Label { get; set; }
+
+ ///
+ /// get/set - Mayan lookup information.
+ ///
+ [JsonPropertyName("lookup")]
+ public string Lookup { get; set; }
+
+ ///
+ /// get/set - Metadata name.
+ ///
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+
+ ///
+ /// get/set - The metadata parser.
+ ///
+ [JsonPropertyName("parser")]
+ public string Parser { get; set; }
+
+ ///
+ /// get/set - The parser arguments.
+ ///
+ [JsonPropertyName("parser_arguments")]
+ public string Parser_arguments { get; set; }
+
+ ///
+ /// get/set - The url path for the metadata.
+ ///
+ [JsonPropertyName("url")]
+ public string Url { get; set; }
+
+ ///
+ /// get/set - The metadata validation.
+ ///
+ [JsonPropertyName("validation")]
+ public string Validation { get; set; }
+
+ ///
+ /// get/set - The metadata validation arguments.
+ ///
+ [JsonPropertyName("validation_arguments")]
+ public string Validation_arguments { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/backend/api/Models/Mayan/PimsSync/DocumentMetadataTypeModel.cs b/backend/api/Models/Mayan/PimsSync/DocumentMetadataTypeModel.cs
new file mode 100644
index 0000000000..d8f71fec4c
--- /dev/null
+++ b/backend/api/Models/Mayan/PimsSync/DocumentMetadataTypeModel.cs
@@ -0,0 +1,13 @@
+using System.Text.Json.Serialization;
+
+namespace Pims.Api.Models.Mayan.Sync
+{
+ public class DocumentMetadataTypeModel
+ {
+ [JsonPropertyName("label")]
+ public string Label { get; set; }
+
+ [JsonPropertyName("required")]
+ public bool Required { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/backend/api/Models/Mayan/PimsSync/DocumentTypeModel.cs b/backend/api/Models/Mayan/PimsSync/DocumentTypeModel.cs
new file mode 100644
index 0000000000..eeb37656fe
--- /dev/null
+++ b/backend/api/Models/Mayan/PimsSync/DocumentTypeModel.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Pims.Api.Models.Mayan.Sync
+{
+ public class DocumentTypeModel
+ {
+ [JsonPropertyName("label")]
+ public string Label { get; set; }
+
+ [JsonPropertyName("metadata_types")]
+ public IList MetadataTypes { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/backend/api/Models/Mayan/PimsSync/ExternalBatchResult.cs b/backend/api/Models/Mayan/PimsSync/ExternalBatchResult.cs
new file mode 100644
index 0000000000..60bebea0ed
--- /dev/null
+++ b/backend/api/Models/Mayan/PimsSync/ExternalBatchResult.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using Pims.Api.Models.Mayan.Document;
+using Pims.Api.Models.Mayan.Metadata;
+
+namespace Pims.Api.Models.Mayan.Sync
+{
+ public class ExternalBatchResult
+ {
+ public ExternalBatchResult()
+ {
+ DeletedMetadata = new List>();
+ CreatedMetadata = new List>();
+ DeletedDocumentType = new List>();
+ CreatedDocumentType = new List>();
+ DeletedDocumentTypeMetadataType = new List>();
+ LinkedDocumentMetadataTypes = new List>();
+ }
+
+ public List> DeletedMetadata { get; set; }
+ public List> CreatedMetadata { get; set; }
+
+ public List> DeletedDocumentType { get; set; }
+ public List> CreatedDocumentType { get; set; }
+
+ public List> DeletedDocumentTypeMetadataType { get; set; }
+ public List> LinkedDocumentMetadataTypes { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/backend/api/Models/Mayan/PimsSync/SyncModel.cs b/backend/api/Models/Mayan/PimsSync/SyncModel.cs
new file mode 100644
index 0000000000..c4dadf263c
--- /dev/null
+++ b/backend/api/Models/Mayan/PimsSync/SyncModel.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Pims.Api.Models.Mayan.Sync
+{
+ public class SyncModel
+ {
+ [JsonPropertyName("document_types")]
+ public IList DocumentTypes { get; set; }
+
+ [JsonPropertyName("metadata_types")]
+ public IList MetadataTypes { get; set; }
+
+ [JsonPropertyName("remove_lingering_metadata_types")]
+ public bool RemoveLingeringMetadataTypes { get; set; }
+
+ [JsonPropertyName("remove_lingering_document_types")]
+ public bool RemoveLingeringDocumentTypes { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/backend/api/Pims.Api.csproj b/backend/api/Pims.Api.csproj
index 8c4e74b791..900e76978e 100644
--- a/backend/api/Pims.Api.csproj
+++ b/backend/api/Pims.Api.csproj
@@ -2,8 +2,8 @@
0ef6255f-9ea0-49ec-8c65-c172304b4926
- 1.1.0-30.40
- 1.1.0.30
+ 1.1.0-31.23
+ 1.1.0.31
true
16BC0468-78F6-4C91-87DA-7403C919E646
@@ -27,10 +27,6 @@
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
runtime; build; native; contentfiles; analyzers; buildtransitive
all
@@ -41,7 +37,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/backend/api/Repositories/IDocumentRepository.cs b/backend/api/Repositories/IDocumentRepository.cs
deleted file mode 100644
index 2f92e18cc4..0000000000
--- a/backend/api/Repositories/IDocumentRepository.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
-using Pims.Api.Models;
-using Pims.Api.Models.Mayan;
-using Pims.Api.Models.Mayan.Document;
-
-namespace Pims.Api.Repositories.EDMS
-{
- ///
- /// IDocumentRepository interface, defines the functionality for a document repository.
- ///
- public interface IDocumentRepository
- {
- Task>> GetDocumentTypesAsync(string ordering = "", int? page = null, int? pageSize = null);
-
- Task>> GetDocumentsListAsync(string ordering = "", int? page = null, int? pageSize = null);
-
- Task> DownloadFileAsync(int documentId, int fileId);
-
- Task> UploadDocumentAsync(int documentType, IFormFile file);
-
- }
-}
diff --git a/backend/api/Repositories/Mayan/IEdmsAuthRepository.cs b/backend/api/Repositories/Mayan/IEdmsAuthRepository.cs
new file mode 100644
index 0000000000..5657104033
--- /dev/null
+++ b/backend/api/Repositories/Mayan/IEdmsAuthRepository.cs
@@ -0,0 +1,12 @@
+using System.Threading.Tasks;
+
+namespace Pims.Api.Repositories.Mayan
+{
+ ///
+ /// IEdmsAuthRepository interface, defines the functionality for a EDMS (Electronic Document Management System) authentication.
+ ///
+ public interface IEdmsAuthRepository
+ {
+ Task GetTokenAsync();
+ }
+}
diff --git a/backend/api/Repositories/Mayan/IEdmsDocumentRepository.cs b/backend/api/Repositories/Mayan/IEdmsDocumentRepository.cs
new file mode 100644
index 0000000000..63f2204a80
--- /dev/null
+++ b/backend/api/Repositories/Mayan/IEdmsDocumentRepository.cs
@@ -0,0 +1,35 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Pims.Api.Models;
+using Pims.Api.Models.Mayan;
+using Pims.Api.Models.Mayan.Document;
+
+namespace Pims.Api.Repositories.Mayan
+{
+ ///
+ /// IEdmsDocumentRepository interface, defines the functionality for a document repository.
+ ///
+ public interface IEdmsDocumentRepository
+ {
+ Task> CreateDocumentTypeAsync(DocumentType documentType);
+
+ Task> DeleteDocumentTypeAsync(long documentTypeId);
+
+ Task>> GetDocumentTypesAsync(string ordering = "", int? page = null, int? pageSize = null);
+
+ Task>> GetDocumentTypeMetadataTypesAsync(long documentId, string ordering = "", int? page = null, int? pageSize = null);
+
+ Task>> GetDocumentsListAsync(string ordering = "", int? page = null, int? pageSize = null);
+
+ Task> DownloadFileAsync(int documentId, int fileId);
+
+ Task> UploadDocumentAsync(int documentType, IFormFile file);
+
+ Task> CreateDocumentTypeMetadataTypeAsync(long documentTypeId, long metadataTypeId, bool isRequired);
+
+ Task> UpdateDocumentTypeMetadataTypeAsync(long documentTypeId, long documentTypeMetadataTypeId, bool isRequired);
+
+ Task> DeleteDocumentTypeMetadataTypeAsync(long documentTypeId, long documentTypeMetadataTypeId);
+
+ }
+}
diff --git a/backend/api/Repositories/Mayan/IEdmsMetadataRepository.cs b/backend/api/Repositories/Mayan/IEdmsMetadataRepository.cs
new file mode 100644
index 0000000000..be72e03ef4
--- /dev/null
+++ b/backend/api/Repositories/Mayan/IEdmsMetadataRepository.cs
@@ -0,0 +1,17 @@
+using System.Threading.Tasks;
+using Pims.Api.Models;
+using Pims.Api.Models.Mayan;
+using Pims.Api.Models.Mayan.Metadata;
+
+namespace Pims.Api.Repositories.Mayan
+{
+ ///
+ /// IEdmsMetadataRepository interface, defines the functionality for a metadata repository.
+ ///
+ public interface IEdmsMetadataRepository
+ {
+ Task> CreateMetadataTypeAsync(MetadataType metadataType);
+ Task> DeleteMetadataTypeAsync(long metadataTypeId);
+ Task>> GetMetadataTypesAsync(string ordering = "", int? page = null, int? pageSize = null);
+ }
+}
diff --git a/backend/api/Repositories/Mayan/MayanAuthRepository.cs b/backend/api/Repositories/Mayan/MayanAuthRepository.cs
new file mode 100644
index 0000000000..ae2fa6bd44
--- /dev/null
+++ b/backend/api/Repositories/Mayan/MayanAuthRepository.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Net.Http;
+using System.Net.Mime;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Pims.Api.Helpers.Exceptions;
+using Pims.Api.Models;
+using Pims.Api.Models.Mayan;
+
+namespace Pims.Api.Repositories.Mayan
+{
+ ///
+ /// MayanDocumentRepository provides document access from a Mayan EDMS api.
+ ///
+ public class MayanAuthRepository : MayanBaseRepository, IEdmsAuthRepository
+ {
+ private string _currentToken;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Injected Logger Provider.
+ /// Injected Httpclient factory.
+ /// The injected configuration provider.
+ public MayanAuthRepository(
+ ILogger logger,
+ IHttpClientFactory httpClientFactory,
+ IConfiguration configuration)
+ : base(logger, httpClientFactory, configuration)
+ {
+ _currentToken = string.Empty;
+ }
+
+ public async Task GetTokenAsync()
+ {
+ if (string.IsNullOrEmpty(_currentToken))
+ {
+ ExternalResult tokenResult = await RequestToken();
+
+ if (tokenResult.Status == ExternalResultStatus.Error)
+ {
+ throw new AuthenticationException(tokenResult.Message);
+ }
+ _currentToken = tokenResult.Payload.Token;
+ }
+
+ return _currentToken;
+ }
+
+ private async Task> RequestToken()
+ {
+ _logger.LogDebug("Getting authentication token...");
+ Uri endpoint = new Uri($"{_config.BaseUri}/auth/token/obtain/");
+ using StringContent credentials = new(JsonSerializer.Serialize(new TokenRequest
+ {
+ Username = _config.ConnectionUser,
+ Password = _config.ConnectionPassword,
+ }), Encoding.UTF8, MediaTypeNames.Application.Json);
+
+ ExternalResult result = await PostAsync(endpoint, credentials);
+ _logger.LogDebug("Finished getting authentication token");
+
+ return result;
+ }
+ }
+}
diff --git a/backend/api/Repositories/Mayan/MayanBaseRepository.cs b/backend/api/Repositories/Mayan/MayanBaseRepository.cs
new file mode 100644
index 0000000000..8d28b18248
--- /dev/null
+++ b/backend/api/Repositories/Mayan/MayanBaseRepository.cs
@@ -0,0 +1,241 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Net.Mime;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Pims.Api.Models;
+using Pims.Api.Models.Config;
+
+namespace Pims.Api.Repositories.Mayan
+{
+ ///
+ /// MayanBaseRepository provides common methods to interact with the Mayan EDMS api.
+ ///
+ public abstract class MayanBaseRepository
+ {
+ private const string MayanConfigSectionKey = "Mayan";
+ protected readonly MayanConfig _config;
+ protected readonly IHttpClientFactory _httpClientFactory;
+ protected readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Injected Logger Provider.
+ /// Injected Httpclient factory.
+ /// The injected configuration provider.
+ protected MayanBaseRepository(
+ ILogger logger,
+ IHttpClientFactory httpClientFactory,
+ IConfiguration configuration)
+ {
+ _logger = logger;
+ _httpClientFactory = httpClientFactory;
+ _config = new MayanConfig();
+ configuration.Bind(MayanConfigSectionKey, _config);
+ }
+
+ protected async Task> GetAsync(Uri endpoint, string authenticationToken)
+ {
+ using HttpClient client = _httpClientFactory.CreateClient("Pims.Api.Logging");
+ client.DefaultRequestHeaders.Accept.Clear();
+ if (!string.IsNullOrEmpty(authenticationToken))
+ {
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", authenticationToken);
+ }
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
+ try
+ {
+ HttpResponseMessage response = await client.GetAsync(endpoint).ConfigureAwait(true);
+ var result = await ProcessResponse(response);
+ return result;
+ }
+ catch (Exception e)
+ {
+ _logger.LogError("Unexpected exception during Get {e}", e);
+ return new ExternalResult()
+ {
+ Status = ExternalResultStatus.Error,
+ Message = "Exception during Get",
+ };
+ }
+ }
+
+ protected async Task> PostAsync(Uri endpoint, HttpContent content, string authenticationToken = null)
+ {
+ using HttpClient client = _httpClientFactory.CreateClient("Pims.Api.Logging");
+ client.DefaultRequestHeaders.Accept.Clear();
+ if (!string.IsNullOrEmpty(authenticationToken))
+ {
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", authenticationToken);
+ }
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
+
+ try
+ {
+ HttpResponseMessage response = await client.PostAsync(endpoint, content).ConfigureAwait(true);
+ var result = await ProcessResponse(response);
+ return result;
+ }
+ catch (Exception e)
+ {
+ _logger.LogError("Unexpected exception during post {e}", e);
+ return new ExternalResult()
+ {
+ Status = ExternalResultStatus.Error,
+ Message = "Exception during Post",
+ };
+ }
+ }
+
+ protected async Task> PutAsync(Uri endpoint, HttpContent content, string authenticationToken = null)
+ {
+ using HttpClient client = _httpClientFactory.CreateClient("Pims.Api.Logging");
+ client.DefaultRequestHeaders.Accept.Clear();
+ if (!string.IsNullOrEmpty(authenticationToken))
+ {
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", authenticationToken);
+ }
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
+
+ try
+ {
+ HttpResponseMessage response = await client.PutAsync(endpoint, content).ConfigureAwait(true);
+ var result = await ProcessResponse(response);
+ return result;
+ }
+ catch (Exception e)
+ {
+ _logger.LogError("Unexpected exception during put {e}", e);
+ return new ExternalResult()
+ {
+ Status = ExternalResultStatus.Error,
+ Message = "Exception during Put",
+ };
+
+ }
+ }
+
+ protected async Task> DeleteAsync(Uri endpoint, string authenticationToken = null)
+ {
+ using HttpClient client = _httpClientFactory.CreateClient("Pims.Api.Logging");
+ client.DefaultRequestHeaders.Accept.Clear();
+ if (!string.IsNullOrEmpty(authenticationToken))
+ {
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", authenticationToken);
+ }
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
+
+ ExternalResult result = new ExternalResult()
+ {
+ Status = ExternalResultStatus.Error,
+ Payload = endpoint.AbsolutePath,
+ };
+
+ try
+ {
+ HttpResponseMessage response = await client.DeleteAsync(endpoint).ConfigureAwait(true);
+
+ _logger.LogTrace("Response: {response}", response);
+
+ string payload = await response.Content.ReadAsStringAsync().ConfigureAwait(true);
+ result.HttpStatusCode = response.StatusCode;
+ switch (response.StatusCode)
+ {
+ case HttpStatusCode.OK:
+ _logger.LogTrace("Response payload: {payload}", payload);
+ result.Status = ExternalResultStatus.Success;
+ break;
+ case HttpStatusCode.NoContent:
+ result.Status = ExternalResultStatus.Success;
+ result.Message = "No content was returned from the call";
+ break;
+ case HttpStatusCode.Forbidden:
+ result.Status = ExternalResultStatus.Error;
+ result.Message = "Request was forbidden";
+ break;
+ case HttpStatusCode.BadRequest:
+ result.Status = ExternalResultStatus.Error;
+ result.Message = payload;
+ break;
+ default:
+ result.Status = ExternalResultStatus.Error;
+ result.Message = $"Unable to contact endpoint {response.RequestMessage.RequestUri}. Http status {response.StatusCode}";
+ break;
+ }
+ return result;
+ }
+ catch (Exception e)
+ {
+ result.Status = ExternalResultStatus.Error;
+ result.Message = "Exception during Delete";
+ _logger.LogError("Unexpected exception during delete {e}", e);
+ }
+ return result;
+ }
+
+ protected Dictionary GenerateQueryParams(string ordering = "", int? page = null, int? pageSize = null)
+ {
+ Dictionary queryParams = new();
+
+ if (!string.IsNullOrEmpty(ordering))
+ {
+ queryParams["ordering"] = ordering;
+ }
+ if (page.HasValue)
+ {
+ queryParams["page"] = page.ToString();
+ }
+ if (pageSize.HasValue)
+ {
+ queryParams["page_size"] = pageSize.ToString();
+ }
+
+ return queryParams;
+ }
+
+ private async Task> ProcessResponse(HttpResponseMessage response)
+ {
+ ExternalResult result = new ExternalResult()
+ {
+ Status = ExternalResultStatus.Error,
+ };
+
+ _logger.LogTrace("Response: {response}", response);
+ string payload = await response.Content.ReadAsStringAsync().ConfigureAwait(true);
+ result.HttpStatusCode = response.StatusCode;
+ switch (response.StatusCode)
+ {
+ case HttpStatusCode.OK:
+ case HttpStatusCode.Created:
+ _logger.LogTrace("Response payload: {payload}", payload);
+ T requestTokenResult = JsonSerializer.Deserialize(payload);
+ result.Status = ExternalResultStatus.Success;
+ result.Payload = requestTokenResult;
+ break;
+ case HttpStatusCode.NoContent:
+ result.Status = ExternalResultStatus.Error;
+ result.Message = "No content was returned from the call";
+ break;
+ case HttpStatusCode.Forbidden:
+ result.Status = ExternalResultStatus.Error;
+ result.Message = "Request was forbidden";
+ break;
+ case HttpStatusCode.BadRequest:
+ result.Status = ExternalResultStatus.Error;
+ result.Message = payload;
+ break;
+ default:
+ result.Status = ExternalResultStatus.Error;
+ result.Message = $"Unable to contact endpoint {response.RequestMessage.RequestUri}. Http status {response.StatusCode}";
+ break;
+ }
+ return result;
+ }
+ }
+}
diff --git a/backend/api/Repositories/Mayan/MayanDocumentRepository.cs b/backend/api/Repositories/Mayan/MayanDocumentRepository.cs
new file mode 100644
index 0000000000..5186e67f7b
--- /dev/null
+++ b/backend/api/Repositories/Mayan/MayanDocumentRepository.cs
@@ -0,0 +1,287 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.WebUtilities;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Pims.Api.Models;
+using Pims.Api.Models.Mayan;
+using Pims.Api.Models.Mayan.Document;
+using Pims.Api.Models.Mayan.Metadata;
+
+namespace Pims.Api.Repositories.Mayan
+{
+ ///
+ /// MayanDocumentRepository provides document access from a Mayan EDMS api.
+ ///
+ public class MayanDocumentRepository : MayanBaseRepository, IEdmsDocumentRepository
+ {
+ private readonly IEdmsAuthRepository _authRepository;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Injected Logger Provider.
+ /// Injected Httpclient factory.
+ /// Injected repository that handles authentication.
+ /// The injected configuration provider.
+ public MayanDocumentRepository(
+ ILogger logger,
+ IHttpClientFactory httpClientFactory,
+ IEdmsAuthRepository authRepository,
+ IConfiguration configuration)
+ : base(logger, httpClientFactory, configuration)
+ {
+ _authRepository = authRepository;
+ }
+
+ public async Task> CreateDocumentTypeAsync(DocumentType documentType)
+ {
+ _logger.LogDebug("Creating document type...");
+
+ string authenticationToken = await _authRepository.GetTokenAsync();
+ JsonSerializerOptions serializerOptions = new()
+ {
+ IgnoreNullValues = true
+ };
+ string serializedDocumentType = JsonSerializer.Serialize(documentType, serializerOptions);
+ using HttpContent content = new StringContent(serializedDocumentType);
+ content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
+
+ Uri endpoint = new($"{_config.BaseUri}/document_types/");
+
+ var response = await PostAsync(endpoint, content, authenticationToken);
+
+ _logger.LogDebug($"Finished creating a document type");
+
+ return response;
+ }
+
+ public async Task> DeleteDocumentTypeAsync(long documentTypeId)
+ {
+ _logger.LogDebug("Deleting document type...");
+
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ Uri endpoint = new($"{this._config.BaseUri}/document_types/{documentTypeId}/");
+
+ var response = await DeleteAsync(endpoint, authenticationToken);
+
+ _logger.LogDebug($"Finished deleting document type");
+ return response;
+ }
+
+ public async Task>> GetDocumentTypesAsync(string ordering = "", int? page = null, int? pageSize = null)
+ {
+ _logger.LogDebug("Retrieving document types...");
+
+ string endpointString = $"{_config.BaseUri}/document_types/";
+ var queryParams = GenerateQueryParams(ordering, page, pageSize);
+ Uri endpoint = new(QueryHelpers.AddQueryString(endpointString, queryParams));
+
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ ExternalResult> response = await GetAsync>(endpoint, authenticationToken).ConfigureAwait(true);
+
+ _logger.LogDebug("Finished retrieving document types");
+ return response;
+ }
+
+ public async Task>> GetDocumentTypeMetadataTypesAsync(long documentId, string ordering = "", int? page = null, int? pageSize = null)
+ {
+ _logger.LogDebug("Retrieving document type metadata types...");
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ var queryParams = GenerateQueryParams(ordering, page, pageSize);
+
+ string endpointString = $"{this._config.BaseUri}/document_types/{documentId}/metadata_types/";
+ Uri endpoint = new(QueryHelpers.AddQueryString(endpointString, queryParams));
+ var response = await GetAsync>(endpoint, authenticationToken).ConfigureAwait(true);
+
+ _logger.LogDebug("Finished retrieving document type's metadata types");
+ return response;
+ }
+
+ public async Task>> GetDocumentsListAsync(string ordering = "", int? page = null, int? pageSize = null)
+ {
+ _logger.LogDebug("Retrieving document list...");
+
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ var queryParams = GenerateQueryParams(ordering, page, pageSize);
+
+ string endpointString = $"{_config.BaseUri}/documents/";
+ Uri endpoint = new(QueryHelpers.AddQueryString(endpointString, queryParams));
+ var response = await GetAsync>(endpoint, authenticationToken).ConfigureAwait(true);
+
+ _logger.LogDebug("Finished retrieving document list");
+ return response;
+ }
+
+
+ public async Task> DownloadFileAsync(int documentId, int fileId)
+ {
+ _logger.LogDebug("Downloading file...");
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ ExternalResult retVal = new()
+ {
+ Status = ExternalResultStatus.Error,
+ };
+
+ using HttpClient client = _httpClientFactory.CreateClient("Pims.Api.Logging");
+ client.DefaultRequestHeaders.Accept.Clear();
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", authenticationToken);
+ try
+ {
+ Uri endpoint = new($"{this._config.BaseUri}/documents/{documentId}/files/{fileId}/download/");
+ HttpResponseMessage response = await client.GetAsync(endpoint).ConfigureAwait(true);
+ byte[] payload = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(true);
+ _logger.LogTrace("Response: {response}", response);
+ switch (response.StatusCode)
+ {
+ case HttpStatusCode.OK:
+ string contentDisposition = response.Content.Headers.GetValues("Content-Disposition").FirstOrDefault();
+ string fileName = GetFileNameFromContentDisposition(contentDisposition);
+
+ retVal.Status = ExternalResultStatus.Success;
+ retVal.Payload = new FileDownload()
+ {
+ FilePayload = payload,
+ Size = int.Parse(response.Content.Headers.GetValues("Content-Length").FirstOrDefault()),
+ Mimetype = response.Content.Headers.GetValues("Content-Type").FirstOrDefault(),
+ FileName = fileName,
+ };
+
+ break;
+ case HttpStatusCode.NoContent:
+ retVal.Status = ExternalResultStatus.Success;
+ retVal.Message = "No content found";
+ break;
+ case HttpStatusCode.Forbidden:
+ retVal.Status = ExternalResultStatus.Error;
+ retVal.Message = "Forbidden";
+ break;
+ default:
+ retVal.Status = ExternalResultStatus.Error;
+ retVal.Message = $"Unable to contact endpoint {endpoint}. Http status {response.StatusCode}";
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ retVal.Status = ExternalResultStatus.Error;
+ retVal.Message = "Exception downloading file";
+ _logger.LogError("Unexpected exception downloading file {e}", e);
+ }
+
+ _logger.LogDebug($"Finished downloading file");
+ return retVal;
+ }
+
+ public async Task> UploadDocumentAsync(int documentType, IFormFile file)
+ {
+ _logger.LogDebug("Uploading document...");
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ byte[] fileData;
+ using var byteReader = new BinaryReader(file.OpenReadStream());
+ fileData = byteReader.ReadBytes((int)file.OpenReadStream().Length);
+
+ // Add the file data to the content
+ using ByteArrayContent fileBytes = new ByteArrayContent(fileData);
+ using MultipartFormDataContent multiContent = new MultipartFormDataContent();
+ multiContent.Add(fileBytes, "file", file.FileName);
+
+ // Add the document id to the content
+ using HttpContent content = new StringContent(documentType.ToString());
+ multiContent.Add(content, "document_type_id");
+
+ Uri endpoint = new($"{this._config.BaseUri}/documents/upload/");
+
+ ExternalResult result = await PostAsync(endpoint, multiContent, authenticationToken);
+
+ _logger.LogDebug($"Finished uploading file");
+ return result;
+ }
+
+ public async Task>> GetMetadataTypesAsync(string ordering = "", int? page = null, int? pageSize = null)
+ {
+ _logger.LogDebug("Retrieving metadata types...");
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ var queryParams = GenerateQueryParams(ordering, page, pageSize);
+ string endpointString = $"{_config.BaseUri}/metadata_types/";
+ Uri endpoint = new(QueryHelpers.AddQueryString(endpointString, queryParams));
+
+ var response = await GetAsync>(endpoint, authenticationToken);
+
+ _logger.LogDebug("Finished retrieving metadata types");
+ return response;
+ }
+
+ public async Task> CreateDocumentTypeMetadataTypeAsync(long documentTypeId, long metadataTypeId, bool isRequired)
+ {
+ _logger.LogDebug("Creating document type's metadata type...");
+
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ var linkModel = new { metadata_type_id = metadataTypeId, required = isRequired };
+ using HttpContent content = new StringContent(JsonSerializer.Serialize(linkModel));
+ content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
+
+ Uri endpoint = new($"{this._config.BaseUri}/document_types/{documentTypeId}/metadata_types/");
+
+ var response = await PostAsync(endpoint, content, authenticationToken);
+
+ _logger.LogDebug($"Finished creating document type's metadata type");
+ return response;
+ }
+
+ public async Task> UpdateDocumentTypeMetadataTypeAsync(long documentTypeId, long documentTypeMetadataTypeId, bool isRequired)
+ {
+ _logger.LogDebug("Updating document type and metadata type...");
+
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ using MultipartFormDataContent form = new MultipartFormDataContent();
+ using StringContent isRequiredContent = new StringContent(isRequired.ToString());
+ form.Add(isRequiredContent, "required");
+
+ Uri endpoint = new($"{this._config.BaseUri}/document_types/{documentTypeId}/metadata_types/{documentTypeMetadataTypeId}/");
+
+ var response = await PutAsync(endpoint, form, authenticationToken);
+
+ _logger.LogDebug($"Finished update document type with a metadata type");
+ return response;
+ }
+
+ public async Task> DeleteDocumentTypeMetadataTypeAsync(long documentTypeId, long documentTypeMetadataTypeId)
+ {
+ _logger.LogDebug("Deleting document type's metadata type...");
+
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ Uri endpoint = new($"{this._config.BaseUri}/document_types/{documentTypeId}/metadata_types/{documentTypeMetadataTypeId}/");
+
+ var response = await DeleteAsync(endpoint, authenticationToken);
+
+ _logger.LogDebug($"Finished deleting document type's metadata type");
+ return response;
+ }
+
+ private static string GetFileNameFromContentDisposition(string contentDisposition)
+ {
+ const string fileNameFlag = "filename";
+ string[] parts = contentDisposition.Split(" ");
+ string fileNamePart = parts.FirstOrDefault(x => x.Contains(fileNameFlag));
+ return fileNamePart[(fileNameFlag.Length + 1)..].Replace("\"", string.Empty);
+ }
+ }
+}
\ No newline at end of file
diff --git a/backend/api/Repositories/Mayan/MayanMetadataRepository.cs b/backend/api/Repositories/Mayan/MayanMetadataRepository.cs
new file mode 100644
index 0000000000..452b72d68d
--- /dev/null
+++ b/backend/api/Repositories/Mayan/MayanMetadataRepository.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.WebUtilities;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Pims.Api.Models;
+using Pims.Api.Models.Mayan;
+using Pims.Api.Models.Mayan.Metadata;
+
+namespace Pims.Api.Repositories.Mayan
+{
+ ///
+ /// MayanMetadataRepository provides document access from a Mayan EDMS api.
+ ///
+ public class MayanMetadataRepository : MayanBaseRepository, IEdmsMetadataRepository
+ {
+ private readonly IEdmsAuthRepository _authRepository;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Injected Logger Provider.
+ /// Injected Httpclient factory.
+ /// Injected repository that handles authentication.
+ /// The injected configuration provider.
+ public MayanMetadataRepository(
+ ILogger logger,
+ IHttpClientFactory httpClientFactory,
+ IEdmsAuthRepository authRepository,
+ IConfiguration configuration)
+ : base(logger, httpClientFactory, configuration)
+ {
+ _authRepository = authRepository;
+ }
+
+ public async Task>> GetMetadataTypesAsync(string ordering = "", int? page = null, int? pageSize = null)
+ {
+ _logger.LogDebug("Retrieving metadata types...");
+
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ var queryParams = GenerateQueryParams(ordering, page, pageSize);
+ string endpointString = $"{_config.BaseUri}/metadata_types/";
+ Uri endpoint = new(QueryHelpers.AddQueryString(endpointString, queryParams));
+
+ var response = await GetAsync>(endpoint, authenticationToken).ConfigureAwait(true);
+
+ _logger.LogDebug("Finished retrieving metadata types");
+ return response;
+ }
+
+ public async Task> CreateMetadataTypeAsync(MetadataType metadataType)
+ {
+ _logger.LogDebug("Creating metadata type...");
+
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ JsonSerializerOptions serializerOptions = new()
+ {
+ IgnoreNullValues = true,
+ };
+ string serializedMetadataType = JsonSerializer.Serialize(metadataType, serializerOptions);
+ using HttpContent content = new StringContent(serializedMetadataType);
+ content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
+
+ Uri endpoint = new($"{this._config.BaseUri}/metadata_types/");
+
+ var response = await PostAsync(endpoint, content, authenticationToken).ConfigureAwait(true);
+
+ this._logger.LogDebug($"Finished creating a metadata type");
+ return response;
+ }
+
+ public async Task> DeleteMetadataTypeAsync(long metadataTypeId)
+ {
+ _logger.LogDebug("Deleting metadata type...");
+
+ string authenticationToken = await _authRepository.GetTokenAsync();
+
+ Uri endpoint = new($"{this._config.BaseUri}/metadata_types/{metadataTypeId}/");
+
+ var response = await DeleteAsync(endpoint, authenticationToken);
+
+ _logger.LogDebug($"Finished deleting metadata type");
+ return response;
+ }
+ }
+}
diff --git a/backend/api/Repositories/MayanDocumentRepository.cs b/backend/api/Repositories/MayanDocumentRepository.cs
deleted file mode 100644
index f7f1a98adb..0000000000
--- a/backend/api/Repositories/MayanDocumentRepository.cs
+++ /dev/null
@@ -1,436 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Net.Http.Headers;
-using System.Net.Mime;
-using System.Text;
-using System.Text.Json;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.WebUtilities;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-using Pims.Api.Helpers.Exceptions;
-using Pims.Api.Models;
-using Pims.Api.Models.Config;
-using Pims.Api.Models.Mayan;
-using Pims.Api.Models.Mayan.Document;
-
-namespace Pims.Api.Repositories.EDMS
-{
- ///
- /// MayanDocumentRepository provides document access from a Mayan EDMS api.
- ///
- public class MayanDocumentRepository : IDocumentRepository
- {
- private readonly ILogger _logger;
- private readonly IHttpClientFactory _httpClientFactory;
- private const string MayanConfigSectionKey = "Mayan";
- private readonly MayanConfig _config;
- private string _CurrentToken;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Injected Logger Provider.
- /// Injected Httpclient factory.
- /// The injected configuration provider.
- public MayanDocumentRepository(
- ILogger logger,
- IHttpClientFactory httpClientFactory,
- IConfiguration configuration)
- {
- _logger = logger;
- _httpClientFactory = httpClientFactory;
- _config = new MayanConfig();
- configuration.Bind(MayanConfigSectionKey, this._config);
- _CurrentToken = "";
- }
-
- public async Task>> GetDocumentTypesAsync(string ordering = "", int? page = null, int? pageSize = null)
- {
- string authenticationToken = await GetToken();
-
- ExternalResult> retVal = new()
- {
- Status = ExternalResultStatus.Error,
- };
-
- _logger.LogDebug("Retrieving document types...");
- using HttpClient client = _httpClientFactory.CreateClient("Pims.Api.Logging");
- client.DefaultRequestHeaders.Accept.Clear();
- client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", authenticationToken);
- client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
-
- Dictionary queryParams = new();
-
- if (!string.IsNullOrEmpty(ordering))
- {
- queryParams["ordering"] = ordering;
- }
-
- if (page.HasValue)
- {
- queryParams["page"] = page.ToString();
- }
-
- if (pageSize.HasValue)
- {
- queryParams["page_size"] = pageSize.ToString();
- }
-
- try
- {
- string endpointString = $"{this._config.BaseUri}/document_types/";
- Uri endpoint = new(QueryHelpers.AddQueryString(endpointString, queryParams));
- HttpResponseMessage response = await client.GetAsync(endpoint).ConfigureAwait(true);
- string payload = await response.Content.ReadAsStringAsync().ConfigureAwait(true);
- this._logger.LogTrace("Response: {response}", response);
- switch (response.StatusCode)
- {
- case HttpStatusCode.OK:
- this._logger.LogTrace("Response payload: {payload}", payload);
- QueryResult documentTypesResult = JsonSerializer.Deserialize>(payload);
- if (documentTypesResult != null)
- {
- retVal.Status = ExternalResultStatus.Success;
- retVal.Payload = documentTypesResult;
- }
- else
- {
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = "The response is empty";
- }
-
- break;
- case HttpStatusCode.NoContent:
- retVal.Status = ExternalResultStatus.Success;
- retVal.Message = "No content found";
- break;
- case HttpStatusCode.Forbidden:
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = "Forbidden";
- break;
- default:
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = $"Unable to contact endpoint {endpoint}. Http status {response.StatusCode}";
- break;
- }
- }
- catch (Exception e)
- {
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = "Exception retrieving documents types";
- this._logger.LogError("Unexpected exception retrieving document types {e}", e);
- }
-
- this._logger.LogDebug("Finished retrieving document types");
- return retVal;
- }
-
- public async Task>> GetDocumentsListAsync(string ordering = "", int? page = null, int? pageSize = null)
- {
- string authenticationToken = await GetToken();
-
- ExternalResult> retVal = new ()
- {
- Status = ExternalResultStatus.Error,
- };
-
- _logger.LogDebug("Retrieving document list...");
- using HttpClient client = _httpClientFactory.CreateClient("Pims.Api.Logging");
- client.DefaultRequestHeaders.Accept.Clear();
- client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", authenticationToken);
- client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
-
- Dictionary queryParams = new ();
-
- if (!string.IsNullOrEmpty(ordering))
- {
- queryParams["ordering"] = ordering;
- }
-
- if (page.HasValue)
- {
- queryParams["page"] = page.ToString();
- }
-
- if (pageSize.HasValue)
- {
- queryParams["page_size"] = pageSize.ToString();
- }
-
- try
- {
- string endpointString = $"{this._config.BaseUri}/documents/";
- Uri endpoint = new (QueryHelpers.AddQueryString(endpointString, queryParams));
- HttpResponseMessage response = await client.GetAsync(endpoint).ConfigureAwait(true);
- string payload = await response.Content.ReadAsStringAsync().ConfigureAwait(true);
- this._logger.LogTrace("Response: {response}", response);
- switch (response.StatusCode)
- {
- case HttpStatusCode.OK:
- this._logger.LogTrace("Response payload: {payload}", payload);
- QueryResult documentDetailsResult = JsonSerializer.Deserialize>(payload);
- if (documentDetailsResult != null)
- {
- retVal.Status = ExternalResultStatus.Success;
- retVal.Payload = documentDetailsResult;
- }
- else
- {
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = "The response is empty";
- }
-
- break;
- case HttpStatusCode.NoContent:
- retVal.Status = ExternalResultStatus.Success;
- retVal.Message = "No content found";
- break;
- case HttpStatusCode.Forbidden:
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = "Forbidden";
- break;
- default:
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = $"Unable to contact endpoint {endpoint}. Http status {response.StatusCode}";
- break;
- }
- }
- catch (Exception e)
- {
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = "Exception retrieving documents";
- this._logger.LogError("Unexpected exception retrieving document list {e}", e);
- }
-
- this._logger.LogDebug("Finished retrieving document list");
- return retVal;
- }
-
-
- public async Task> DownloadFileAsync(int documentId, int fileId)
- {
- string authenticationToken = await GetToken();
-
- ExternalResult retVal = new ()
- {
- Status = ExternalResultStatus.Error,
- };
-
- _logger.LogDebug("Downloading file...");
- using HttpClient client = _httpClientFactory.CreateClient("Pims.Api.Logging");
- client.DefaultRequestHeaders.Accept.Clear();
- client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", authenticationToken);
-
- try
- {
-
- Uri endpoint = new ($"{this._config.BaseUri}/documents/{documentId}/files/{fileId}/download/");
- HttpResponseMessage response = await client.GetAsync(endpoint).ConfigureAwait(true);
- byte[] payload = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(true);
- this._logger.LogTrace("Response: {response}", response);
- switch (response.StatusCode)
- {
- case HttpStatusCode.OK:
- string contentDisposition = response.Content.Headers.GetValues("Content-Disposition").FirstOrDefault();
- string fileName = GetFileNameFromContentDisposition(contentDisposition);
-
- retVal.Status = ExternalResultStatus.Success;
- retVal.Payload = new FileDownload()
- {
- FilePayload = payload,
- Size = int.Parse(response.Content.Headers.GetValues("Content-Length").FirstOrDefault()),
- Mimetype = response.Content.Headers.GetValues("Content-Type").FirstOrDefault(),
- FileName = fileName
- };
-
- break;
- case HttpStatusCode.NoContent:
- retVal.Status = ExternalResultStatus.Success;
- retVal.Message = "No content found";
- break;
- case HttpStatusCode.Forbidden:
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = "Forbidden";
- break;
- default:
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = $"Unable to contact endpoint {endpoint}. Http status {response.StatusCode}";
- break;
- }
- }
- catch (Exception e)
- {
- retVal.Status = ExternalResultStatus.Error;
- retVal.Message = "Exception downloading file";
- this._logger.LogError("Unexpected exception downloading file {e}", e);
- }
-
- this._logger.LogDebug($"Finished downloading file");
- return retVal;
- }
-
- public async Task> UploadDocumentAsync(int documentType, IFormFile file)
- {
- string authenticationToken = await GetToken();
-
- ExternalResult uploadDocumentResult = new ()
- {
- Status = ExternalResultStatus.Error,
- };
-
- _logger.LogDebug("Uploading document...");
- using HttpClient client = _httpClientFactory.CreateClient("Pims.Api.Logging");
- client.DefaultRequestHeaders.Accept.Clear();
- client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", authenticationToken);
-
- try
- {
- byte[] fileData;
- using var byteReader = new BinaryReader(file.OpenReadStream());
- fileData = byteReader.ReadBytes((int)file.OpenReadStream().Length);
-
- using ByteArrayContent fileBytes = new ByteArrayContent(fileData);
- using MultipartFormDataContent multiContent = new MultipartFormDataContent();
- multiContent.Add(fileBytes, "file", file.FileName);
-
- using HttpContent content = new StringContent(documentType.ToString());
- multiContent.Add(content, "document_type_id");
-
- Uri endpoint = new ($"{this._config.BaseUri}/documents/upload/");
- using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, endpoint);
- request.Content = multiContent;
-
- HttpResponseMessage response = await client.SendAsync(request).ConfigureAwait(true);
- string payload = await response.Content.ReadAsStringAsync().ConfigureAwait(true);
- this._logger.LogTrace("Response: {response}", response);
- switch (response.StatusCode)
- {
- case HttpStatusCode.Created:
- this._logger.LogTrace("Response payload: {payload}", payload);
- uploadDocumentResult.Status = ExternalResultStatus.Success;
- uploadDocumentResult.Payload = JsonSerializer.Deserialize(payload);
- break;
- case HttpStatusCode.NoContent:
- uploadDocumentResult.Status = ExternalResultStatus.Success;
- uploadDocumentResult.Message = "No content found";
- break;
- case HttpStatusCode.Forbidden:
- uploadDocumentResult.Status = ExternalResultStatus.Error;
- uploadDocumentResult.Message = "Forbidden";
- break;
- case HttpStatusCode.BadRequest:
- uploadDocumentResult.Status = ExternalResultStatus.Error;
- uploadDocumentResult.Message = payload;
- break;
- default:
- uploadDocumentResult.Status = ExternalResultStatus.Error;
- uploadDocumentResult.Message = $"Unable to contact endpoint {endpoint}. Http status {response.StatusCode}";
- break;
- }
- }
- catch (Exception e)
- {
- uploadDocumentResult.Status = ExternalResultStatus.Error;
- uploadDocumentResult.Message = "Exception uploading file";
- this._logger.LogError("Unexpected exception uploading file {e}", e);
- }
-
- this._logger.LogDebug($"Finished uploading file");
- return uploadDocumentResult;
- }
-
- private async Task GetToken()
- {
- if (string.IsNullOrEmpty(_CurrentToken))
- {
- ExternalResult tokenResult = await RequestToken();
-
- if (tokenResult.Status == ExternalResultStatus.Error)
- {
- throw new AuthenticationException(tokenResult.Message);
- }
- _CurrentToken = tokenResult.Payload;
- }
-
- return _CurrentToken;
- }
-
- private async Task> RequestToken()
- {
- ExternalResult tokenResult = new ExternalResult()
- {
- Status = ExternalResultStatus.Error
- };
-
- _logger.LogDebug("Getting authentication token...");
- using HttpClient client = _httpClientFactory.CreateClient("Pims.Api.Logging");
- client.DefaultRequestHeaders.Accept.Clear();
- client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
- try
- {
- using StringContent credentials = new (JsonSerializer.Serialize(new TokenRequest
- {
- Username = _config.ConnectionUser,
- Password = _config.ConnectionPassword
- }), Encoding.UTF8, MediaTypeNames.Application.Json);
-
- Uri endpoint = new Uri($"{this._config.BaseUri}/auth/token/obtain/");
- HttpResponseMessage response = await client.PostAsync(endpoint, credentials).ConfigureAwait(true);
- string payload = await response.Content.ReadAsStringAsync().ConfigureAwait(true);
- this._logger.LogTrace("Response: {response}", response);
- switch (response.StatusCode)
- {
- case HttpStatusCode.OK:
- this._logger.LogTrace("Response payload: {payload}", payload);
- TokenResult requestTokenResult = JsonSerializer.Deserialize(payload);
- tokenResult.Status = ExternalResultStatus.Success;
- tokenResult.Payload = requestTokenResult.Token;
- break;
- case HttpStatusCode.NoContent:
- tokenResult.Status = ExternalResultStatus.Error;
- tokenResult.Message = "No token was returned from the call";
- break;
- case HttpStatusCode.Forbidden:
- tokenResult.Status = ExternalResultStatus.Error;
- tokenResult.Message = "Token request was forbidden";
- break;
- case HttpStatusCode.BadRequest:
- tokenResult.Status = ExternalResultStatus.Error;
- tokenResult.Message = payload;
- break;
- default:
- tokenResult.Status = ExternalResultStatus.Error;
- tokenResult.Message = $"Unable to contact endpoint {endpoint}. Http status {response.StatusCode}";
- break;
- }
- }
- catch (Exception e)
- {
- tokenResult.Status = ExternalResultStatus.Error;
- tokenResult.Message = "Exception obtaining a token";
- this._logger.LogError("Unexpected exception obtaining a token {e}", e);
- }
-
- this._logger.LogDebug("Finished getting authentication token");
- return tokenResult;
- }
-
-
-
- private static string GetFileNameFromContentDisposition(string contentDisposition)
- {
- const string fileNameFlag = "filename";
- string[] parts = contentDisposition.Split(" ");
- string fileNamePart = parts.FirstOrDefault(x => x.Contains(fileNameFlag));
- return fileNamePart[(fileNameFlag.Length + 1)..].Replace("\"", string.Empty);
- }
-
-
- }
-}
diff --git a/backend/api/Services/BaseService.cs b/backend/api/Services/BaseService.cs
new file mode 100644
index 0000000000..4666755e6d
--- /dev/null
+++ b/backend/api/Services/BaseService.cs
@@ -0,0 +1,49 @@
+using Microsoft.Extensions.Logging;
+using System.Security.Claims;
+
+namespace Pims.Api.Services
+{
+ ///
+ /// BaseService abstract class, provides a generic service layer to perform business logic.
+ /// It can access the datastore via available repositories.
+ ///
+ public abstract class BaseService
+ {
+ #region Properties
+ ///
+ /// get - The current user accessing the service.
+ ///
+ protected ClaimsPrincipal User { get; }
+
+ ///
+ /// get - The logger.
+ ///
+ protected ILogger Logger { get; }
+
+ #endregion
+
+ #region Constructors
+ ///
+ /// Creates a new instance of a BaseService class, and initializes it with the specified arguments.
+ ///
+ ///
+ ///
+ protected BaseService(ClaimsPrincipal user, ILogger logger)
+ {
+ this.User = user;
+ this.Logger = logger;
+ }
+ #endregion
+
+ #region Methods
+ ///
+ /// Provides a way to fetch the user within the assembly.
+ ///
+ ///
+ internal ClaimsPrincipal GetUser()
+ {
+ return this.User;
+ }
+ #endregion
+ }
+}
diff --git a/backend/api/Services/DocumentService.cs b/backend/api/Services/DocumentService.cs
index 68aaddff31..143b66f55d 100644
--- a/backend/api/Services/DocumentService.cs
+++ b/backend/api/Services/DocumentService.cs
@@ -3,7 +3,7 @@
using Pims.Api.Models;
using Pims.Api.Models.Mayan;
using Pims.Api.Models.Mayan.Document;
-using Pims.Api.Repositories.EDMS;
+using Pims.Api.Repositories.Mayan;
namespace Pims.Api.Services
{
@@ -12,9 +12,9 @@ namespace Pims.Api.Services
///
public class DocumentService : IDocumentService
{
- private readonly IDocumentRepository documentRepository;
+ private readonly IEdmsDocumentRepository documentRepository;
- public DocumentService(IDocumentRepository documentRepository)
+ public DocumentService(IEdmsDocumentRepository documentRepository)
{
this.documentRepository = documentRepository;
}
diff --git a/backend/api/Services/DocumentSyncService.cs b/backend/api/Services/DocumentSyncService.cs
new file mode 100644
index 0000000000..b794370a6e
--- /dev/null
+++ b/backend/api/Services/DocumentSyncService.cs
@@ -0,0 +1,243 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Pims.Api.Models;
+using Pims.Api.Models.Mayan;
+using Pims.Api.Models.Mayan.Document;
+using Pims.Api.Models.Mayan.Metadata;
+using Pims.Api.Models.Mayan.Sync;
+using Pims.Api.Repositories.Mayan;
+using Pims.Dal.Entities;
+using Pims.Dal.Repositories;
+
+namespace Pims.Api.Services
+{
+ ///
+ /// DocumentSyncService implementation provides document syncronization between different data sources.
+ ///
+ public class DocumentSyncService : IDocumentSyncService
+ {
+ private readonly IEdmsDocumentRepository mayanDocumentRepository;
+ private readonly IEdmsMetadataRepository mayanMetadataRepository;
+ private readonly IDocumentTypeRepository documentTypeRepository;
+
+ public DocumentSyncService(IEdmsDocumentRepository edmsDocumentRepository, IEdmsMetadataRepository emdMetadataRepository, IDocumentTypeRepository documentTypeRepository)
+ {
+ this.mayanDocumentRepository = edmsDocumentRepository;
+ this.mayanMetadataRepository = emdMetadataRepository;
+ this.documentTypeRepository = documentTypeRepository;
+ }
+
+ public ExternalBatchResult SyncMayanMetadataTypes(SyncModel model)
+ {
+ ExternalBatchResult batchResult = new ExternalBatchResult();
+
+ Task>> retrieveTask = mayanMetadataRepository.GetMetadataTypesAsync(pageSize: 5000);
+ retrieveTask.Wait();
+ var metadata = retrieveTask.Result;
+
+ // Add the metadata types not in mayan
+ IList>> createTasks = new List>>();
+ foreach (var metadataTypeLabel in model.MetadataTypes)
+ {
+ if (metadata.Payload.Results.FirstOrDefault(x => x.Label == metadataTypeLabel) == null)
+ {
+ var newMetadataType = new MetadataType() { Label = metadataTypeLabel, Name = metadataTypeLabel.Replace(' ', '_') };
+ createTasks.Add(mayanMetadataRepository.CreateMetadataTypeAsync(newMetadataType));
+ }
+ }
+
+ Task.WaitAll(createTasks.ToArray());
+ foreach (var task in createTasks)
+ {
+ batchResult.CreatedMetadata.Add(task.Result);
+ }
+
+ if (model.RemoveLingeringMetadataTypes)
+ {
+ // Delete the metadata types that are not on the sync model
+ IList>> deleteTasks = new List>>();
+ foreach (var metadataType in metadata.Payload.Results)
+ {
+ if (model.MetadataTypes.IndexOf(metadataType.Label) < 0)
+ {
+ deleteTasks.Add(mayanMetadataRepository.DeleteMetadataTypeAsync(metadataType.Id));
+ }
+ }
+
+ Task.WaitAll(deleteTasks.ToArray());
+ foreach (var task in deleteTasks)
+ {
+ batchResult.DeletedMetadata.Add(task.Result);
+ }
+ }
+
+ return batchResult;
+ }
+
+ public ExternalBatchResult SyncMayanDocumentTypes(SyncModel model)
+ {
+ ExternalBatchResult batchResult = new ExternalBatchResult();
+
+ Task>> documentTypeTask = mayanDocumentRepository.GetDocumentTypesAsync(pageSize: 5000);
+ documentTypeTask.Wait();
+
+ ExternalResult> documentTypesRetrieved = documentTypeTask.Result;
+
+ // Add the document types not in mayan
+ IList>> createTasks = new List>>();
+ foreach (var documentTypeModel in model.DocumentTypes)
+ {
+ if (documentTypesRetrieved.Payload.Results.FirstOrDefault(x => x.Label == documentTypeModel.Label) == null)
+ {
+ createTasks.Add(mayanDocumentRepository.CreateDocumentTypeAsync(new DocumentType() { Label = documentTypeModel.Label }));
+ }
+ }
+ Task.WaitAll(createTasks.ToArray());
+ foreach (var task in createTasks)
+ {
+ batchResult.CreatedDocumentType.Add(task.Result);
+ }
+
+ if (model.RemoveLingeringDocumentTypes)
+ {
+ // Delete the document types that are not on the sync model
+ IList>> deleteTasks = new List>>();
+ foreach (var documentType in documentTypesRetrieved.Payload.Results)
+ {
+ if (model.DocumentTypes.FirstOrDefault(x => x.Label == documentType.Label) == null)
+ {
+ deleteTasks.Add(mayanDocumentRepository.DeleteDocumentTypeAsync(documentType.Id));
+ }
+ }
+
+ Task.WaitAll(deleteTasks.ToArray());
+ foreach (var task in deleteTasks)
+ {
+ batchResult.DeletedDocumentType.Add(task.Result);
+ }
+ }
+
+ SyncDocumentTypeMetadataTypes(model, ref batchResult);
+
+ return batchResult;
+ }
+
+ public async Task> SyncBackendDocumentTypes()
+ {
+ ExternalResult> mayanResult = await mayanDocumentRepository.GetDocumentTypesAsync(pageSize: 5000);
+
+ if (mayanResult.Status != ExternalResultStatus.Success && mayanResult.Payload?.Results?.Count == 0)
+ {
+ return new List();
+ }
+
+ IList pimsDocumentTypes = documentTypeRepository.GetAll();
+
+ // Add the document types not in the backend
+ IList createdDocumentTypes = new List();
+ foreach (var mayanDocumentType in mayanResult.Payload.Results)
+ {
+ if (pimsDocumentTypes.FirstOrDefault(x => x.MayanId == mayanDocumentType.Id) == null)
+ {
+ var newPimsDocType = new PimsDocumentTyp() { MayanId = mayanDocumentType.Id, DocumentType = mayanDocumentType.Label };
+ createdDocumentTypes.Add(documentTypeRepository.Add(newPimsDocType));
+ }
+ }
+
+ // If there are new doctypes, commit the transaction
+ if (createdDocumentTypes.Count > 0)
+ {
+ documentTypeRepository.CommitTransaction();
+ }
+
+ return createdDocumentTypes;
+ }
+
+ private void SyncDocumentTypeMetadataTypes(SyncModel model, ref ExternalBatchResult batchResult)
+ {
+ Task>> documentTypeTask = mayanDocumentRepository.GetDocumentTypesAsync(pageSize: 5000);
+ Task>> metadataTypesTask = mayanMetadataRepository.GetMetadataTypesAsync(pageSize: 5000);
+ Task.WaitAll(documentTypeTask, metadataTypesTask);
+
+ var retrievedMetadataTypes = metadataTypesTask.Result;
+
+ // Check that the metadata types on the documents are the same as the ones in the specified in the model
+ IList> linkTasks = new List>();
+ foreach (var documentTypeModel in model.DocumentTypes)
+ {
+ linkTasks.Add(AsyncLinkDocumentMetadata(documentTypeModel, documentTypeTask.Result.Payload.Results, retrievedMetadataTypes.Payload.Results));
+ }
+
+ Task.WaitAll(linkTasks.ToArray());
+ foreach (var task in linkTasks)
+ {
+ batchResult.DeletedDocumentTypeMetadataType.AddRange(task.Result.DeletedDocumentTypeMetadataType);
+ batchResult.LinkedDocumentMetadataTypes.AddRange(task.Result.LinkedDocumentMetadataTypes);
+ }
+ }
+
+ private async Task AsyncLinkDocumentMetadata(DocumentTypeModel documentTypeModel, IList retrievedDocumentTypes, IList retrievedMetadataTypes)
+ {
+ ExternalBatchResult batchResult = new ExternalBatchResult();
+
+ var documentType = retrievedDocumentTypes.FirstOrDefault(x => x.Label == documentTypeModel.Label);
+ ExternalResult> documentTypeMetadataTypes = await mayanDocumentRepository.GetDocumentTypeMetadataTypesAsync(documentType.Id, pageSize: 5000);
+
+ // Update the document type's metadata types
+ IList>> documentTypeMetadataTypeTasks = new List>>();
+ var retrievedLinks = documentTypeMetadataTypes.Payload.Results;
+ foreach (var metadataTypeModel in documentTypeModel.MetadataTypes)
+ {
+ DocumentTypeMetadataType existingDocumentTypeMetadaType = retrievedLinks.FirstOrDefault(x => x.MetadataType.Label == metadataTypeModel.Label);
+ if (existingDocumentTypeMetadaType == null)
+ {
+ MetadataType metadataType = retrievedMetadataTypes.FirstOrDefault(x => x.Label == metadataTypeModel.Label);
+ if (metadataType != null)
+ {
+ documentTypeMetadataTypeTasks.Add(mayanDocumentRepository.CreateDocumentTypeMetadataTypeAsync(documentType.Id, metadataType.Id, metadataTypeModel.Required));
+ }
+ else
+ {
+ batchResult.LinkedDocumentMetadataTypes.Add(
+ new ExternalResult()
+ {
+ Message = $"Metadata with label [{metadataTypeModel.Label}] does not exist in Mayan",
+ Status = ExternalResultStatus.Error,
+ });
+ }
+ }
+ else
+ {
+ if (existingDocumentTypeMetadaType.Required != metadataTypeModel.Required)
+ {
+ documentTypeMetadataTypeTasks.Add(mayanDocumentRepository.UpdateDocumentTypeMetadataTypeAsync(documentType.Id, existingDocumentTypeMetadaType.Id, metadataTypeModel.Required));
+ }
+ }
+ }
+ Task.WaitAll(documentTypeMetadataTypeTasks.ToArray());
+ foreach (var task in documentTypeMetadataTypeTasks)
+ {
+ batchResult.LinkedDocumentMetadataTypes.Add(task.Result);
+ }
+
+ // Get metadata types not on sync model
+ IList>> deleteTasks = new List>>();
+ foreach (var documentTypeMetadataType in documentTypeMetadataTypes.Payload.Results)
+ {
+ if (documentTypeModel.MetadataTypes.FirstOrDefault(x => x.Label == documentTypeMetadataType.MetadataType.Label) == null)
+ {
+ deleteTasks.Add(mayanDocumentRepository.DeleteDocumentTypeMetadataTypeAsync(documentType.Id, documentTypeMetadataType.Id));
+ }
+ }
+
+ Task.WaitAll(deleteTasks.ToArray());
+ foreach (var task in deleteTasks)
+ {
+ batchResult.DeletedDocumentTypeMetadataType.Add(task.Result);
+ }
+
+ return batchResult;
+ }
+ }
+}
diff --git a/backend/api/Services/IDocumentSyncService.cs b/backend/api/Services/IDocumentSyncService.cs
new file mode 100644
index 0000000000..1914a1dffb
--- /dev/null
+++ b/backend/api/Services/IDocumentSyncService.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Pims.Api.Models.Mayan.Sync;
+using Pims.Dal.Entities;
+
+namespace Pims.Api.Services
+{
+ ///
+ /// IDocumentSyncService interface, defines the functionality for document syncronization services.
+ ///
+ public interface IDocumentSyncService
+ {
+ ExternalBatchResult SyncMayanDocumentTypes(SyncModel model);
+
+ ExternalBatchResult SyncMayanMetadataTypes(SyncModel model);
+
+ Task> SyncBackendDocumentTypes();
+ }
+}
diff --git a/backend/api/Services/INoteService.cs b/backend/api/Services/INoteService.cs
new file mode 100644
index 0000000000..24b6aa39df
--- /dev/null
+++ b/backend/api/Services/INoteService.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+using Pims.Api.Constants;
+using Pims.Api.Models.Concepts;
+using Pims.Dal.Entities;
+
+namespace Pims.Api.Services
+{
+ public interface INoteService
+ {
+ EntityNoteModel Add(NoteType type, EntityNoteModel model);
+
+ void DeleteNote(NoteType type, int noteId);
+ IEnumerable GetNotes(NoteType type, long entityId);
+ }
+}
diff --git a/backend/api/Services/NoteService.cs b/backend/api/Services/NoteService.cs
new file mode 100644
index 0000000000..46fd3f3b55
--- /dev/null
+++ b/backend/api/Services/NoteService.cs
@@ -0,0 +1,99 @@
+using System.Collections.Generic;
+using System.Security.Claims;
+using MapsterMapper;
+using Microsoft.Extensions.Logging;
+using Pims.Api.Constants;
+using Pims.Api.Models.Concepts;
+using Pims.Core.Extensions;
+using Pims.Dal.Entities;
+using Pims.Dal.Helpers.Extensions;
+using Pims.Dal.Repositories;
+using Pims.Dal.Security;
+
+namespace Pims.Api.Services
+{
+ public class NoteService : BaseService, INoteService
+ {
+ private readonly INoteRepository _noteRepository;
+ private readonly IEntityNoteRepository _entityNoteRepository;
+ private readonly IMapper _mapper;
+
+ ///
+ /// Creates a new instance of a NoteService, and initializes it with the specified arguments.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public NoteService(ClaimsPrincipal user, ILogger logger, IMapper mapper, INoteRepository noteRepository, IEntityNoteRepository entityNoteRepository) : base(user, logger)
+ {
+ _mapper = mapper;
+ _noteRepository = noteRepository;
+ _entityNoteRepository = entityNoteRepository;
+ }
+
+ public EntityNoteModel Add(NoteType type, EntityNoteModel model)
+ {
+ model.ThrowIfNull(nameof(model));
+ this.User.ThrowIfNotAuthorized(Permissions.NoteAdd);
+
+ EntityNoteModel result;
+
+ switch (type)
+ {
+ case NoteType.Activity:
+ default:
+ var pimsEntity = _mapper.Map(model);
+
+ var createdEntity = _entityNoteRepository.Add(pimsEntity);
+ _entityNoteRepository.CommitTransaction();
+
+ result = _mapper.Map(createdEntity);
+ break;
+ }
+
+ return result;
+ }
+
+
+ ///
+ /// Find and delete the note
+ ///
+ /// Note type to determine the type of note to delete.
+ /// Note id to identify the note to delete
+ public void DeleteNote(NoteType type, int noteId)
+ {
+ switch (type)
+ {
+ case NoteType.Activity:
+ _noteRepository.DeleteActivityNotes(noteId);
+ _entityNoteRepository.CommitTransaction();
+ break;
+ case NoteType.File:
+ // Write code to delete the note from FileNotes table
+ break;
+ default:
+ break;
+ }
+ }
+
+ ///
+ /// Get notes by note type.
+ ///
+ /// Note type to determine the type of notes to return.
+ /// Entity Id to determine the entity to which notes belongs to.
+ ///
+ public IEnumerable GetNotes(NoteType type, long entityId)
+ {
+ switch (type)
+ {
+ case NoteType.Activity:
+ return _noteRepository.GetActivityNotes(entityId);
+ case NoteType.File:
+ default:
+ return new List();
+ }
+ }
+ }
+}
diff --git a/backend/api/Startup.cs b/backend/api/Startup.cs
index 3847d7e8e1..ba979fd038 100644
--- a/backend/api/Startup.cs
+++ b/backend/api/Startup.cs
@@ -37,7 +37,7 @@
using Pims.Api.Helpers.Mapping;
using Pims.Api.Helpers.Middleware;
using Pims.Api.Helpers.Routes.Constraints;
-using Pims.Api.Repositories.EDMS;
+using Pims.Api.Repositories.Mayan;
using Pims.Api.Services;
using Pims.Core.Converters;
using Pims.Core.Http;
@@ -295,7 +295,9 @@ public void ConfigureServices(IServiceCollection services)
private static void AddPimsApiRepositories(IServiceCollection services)
{
- services.AddSingleton();
+ services.AddSingleton();
+ services.AddScoped();
+ services.AddScoped();
}
///
@@ -306,6 +308,8 @@ private static void AddPimsApiRepositories(IServiceCollection services)
private static void AddPimsApiServices(IServiceCollection services)
{
services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
}
///
diff --git a/backend/entities/Comparers/AccessRequestOrganizationOrganizationIdComparer.cs b/backend/dal/Comparers/AccessRequestOrganizationOrganizationIdComparer.cs
similarity index 100%
rename from backend/entities/Comparers/AccessRequestOrganizationOrganizationIdComparer.cs
rename to backend/dal/Comparers/AccessRequestOrganizationOrganizationIdComparer.cs
diff --git a/backend/entities/Comparers/OrganizationIdComparer.cs b/backend/dal/Comparers/OrganizationIdComparer.cs
similarity index 100%
rename from backend/entities/Comparers/OrganizationIdComparer.cs
rename to backend/dal/Comparers/OrganizationIdComparer.cs
diff --git a/backend/entities/Comparers/OrganizationOrganizationIdComparer.cs b/backend/dal/Comparers/OrganizationOrganizationIdComparer.cs
similarity index 100%
rename from backend/entities/Comparers/OrganizationOrganizationIdComparer.cs
rename to backend/dal/Comparers/OrganizationOrganizationIdComparer.cs
diff --git a/backend/entities/Comparers/RoleIdComparer.cs b/backend/dal/Comparers/RoleIdComparer.cs
similarity index 100%
rename from backend/entities/Comparers/RoleIdComparer.cs
rename to backend/dal/Comparers/RoleIdComparer.cs
diff --git a/backend/entities/Comparers/RoleRoleIdComparer.cs b/backend/dal/Comparers/RoleRoleIdComparer.cs
similarity index 100%
rename from backend/entities/Comparers/RoleRoleIdComparer.cs
rename to backend/dal/Comparers/RoleRoleIdComparer.cs
diff --git a/backend/entities/Comparers/UserOrganizationOrganizationIdComparer.cs b/backend/dal/Comparers/UserOrganizationOrganizationIdComparer.cs
similarity index 100%
rename from backend/entities/Comparers/UserOrganizationOrganizationIdComparer.cs
rename to backend/dal/Comparers/UserOrganizationOrganizationIdComparer.cs
diff --git a/backend/entities/Comparers/UserRoleRoleIdComparer.cs b/backend/dal/Comparers/UserRoleRoleIdComparer.cs
similarity index 100%
rename from backend/entities/Comparers/UserRoleRoleIdComparer.cs
rename to backend/dal/Comparers/UserRoleRoleIdComparer.cs
diff --git a/backend/dal/Helpers/Extensions/EntityTypeBuilderExtensions.cs b/backend/dal/Helpers/Extensions/EntityTypeBuilderExtensions.cs
index 9c90d7c13e..db2db8d95d 100644
--- a/backend/dal/Helpers/Extensions/EntityTypeBuilderExtensions.cs
+++ b/backend/dal/Helpers/Extensions/EntityTypeBuilderExtensions.cs
@@ -13,37 +13,6 @@ namespace Pims.Dal.Extensions
///
public static class EntityTypeBuilderExtensions
{
- ///
- /// Adds the configured table name
- ///
- ///
- ///
- ///
- public static EntityTypeBuilder ToMotiTable(this EntityTypeBuilder builder)
- where T : class
- {
- var type = typeof(T);
- var table = type.GetCustomAttribute() ?? throw new InvalidOperationException($"Entity '{type.Name}' model requires MotiAttribute defined.");
- builder.ToTable(table.Name);
- return builder;
- }
-
- ///
- /// Add the primary key to the table and name it with the abbreviation configured.
- ///
- ///
- ///
- ///
- ///
- public static KeyBuilder HasMotiKey(this EntityTypeBuilder builder, Expression> keyExpression)
- where T : class
- {
- var type = typeof(T);
- var table = type.GetCustomAttribute() ?? throw new InvalidOperationException($"Entity '{type.Name}' model requires MotiAttribute defined.");
- var primaryKeyName = $"{table.Abbreviation}_PK";
- return builder.HasKey(keyExpression).HasName(primaryKeyName);
- }
-
///
/// Add a sequence property to the table with the appropriate naming convention.
///
diff --git a/backend/dal/Helpers/Extensions/ServiceCollectionExtensions.cs b/backend/dal/Helpers/Extensions/ServiceCollectionExtensions.cs
index e388aab218..6408010b92 100644
--- a/backend/dal/Helpers/Extensions/ServiceCollectionExtensions.cs
+++ b/backend/dal/Helpers/Extensions/ServiceCollectionExtensions.cs
@@ -44,6 +44,9 @@ public static IServiceCollection AddPimsDalRepositories(this IServiceCollection
repositories.AddScoped();
repositories.AddScoped();
repositories.AddScoped();
+ repositories.AddScoped();
+ repositories.AddScoped();
+ repositories.AddScoped();
return repositories; // TODO: Use reflection to find all Repositories.
}
diff --git a/backend/entities/Helpers/MotiTableAttribute.cs b/backend/dal/Helpers/MotiTableAttribute.cs
similarity index 100%
rename from backend/entities/Helpers/MotiTableAttribute.cs
rename to backend/dal/Helpers/MotiTableAttribute.cs
diff --git a/backend/entities/Helpers/OrganizationExtensions.cs b/backend/dal/Helpers/OrganizationExtensions.cs
similarity index 100%
rename from backend/entities/Helpers/OrganizationExtensions.cs
rename to backend/dal/Helpers/OrganizationExtensions.cs
diff --git a/backend/entities/Helpers/PersonExtensions.cs b/backend/dal/Helpers/PersonExtensions.cs
similarity index 100%
rename from backend/entities/Helpers/PersonExtensions.cs
rename to backend/dal/Helpers/PersonExtensions.cs
diff --git a/backend/entities/Helpers/PropertyExtensions.cs b/backend/dal/Helpers/PropertyExtensions.cs
similarity index 100%
rename from backend/entities/Helpers/PropertyExtensions.cs
rename to backend/dal/Helpers/PropertyExtensions.cs
diff --git a/backend/dal/IPimsRepository.cs b/backend/dal/IPimsRepository.cs
index d46567c770..8a2d023808 100644
--- a/backend/dal/IPimsRepository.cs
+++ b/backend/dal/IPimsRepository.cs
@@ -54,6 +54,12 @@ public interface IPimsRepository : IRepository
#region ResearchFiles
IResearchFileRepository ResearchFile { get; }
#endregion
+
+ #region Notes
+ INoteRepository Note { get; }
+
+ IEntityNoteRepository EntityNote { get; }
+ #endregion
#endregion
}
}
diff --git a/backend/entities/Models/AccessRequestFilter.cs b/backend/dal/Models/AccessRequestFilter.cs
similarity index 100%
rename from backend/entities/Models/AccessRequestFilter.cs
rename to backend/dal/Models/AccessRequestFilter.cs
diff --git a/backend/entities/Models/AutocompletionRequestModel.cs b/backend/dal/Models/AutocompletionRequestModel.cs
similarity index 100%
rename from backend/entities/Models/AutocompletionRequestModel.cs
rename to backend/dal/Models/AutocompletionRequestModel.cs
diff --git a/backend/entities/Models/ContactFilter.cs b/backend/dal/Models/ContactFilter.cs
similarity index 100%
rename from backend/entities/Models/ContactFilter.cs
rename to backend/dal/Models/ContactFilter.cs
diff --git a/backend/entities/Models/EnvironmentModel.cs b/backend/dal/Models/EnvironmentModel.cs
similarity index 100%
rename from backend/entities/Models/EnvironmentModel.cs
rename to backend/dal/Models/EnvironmentModel.cs
diff --git a/backend/entities/Models/LeaseFilter.cs b/backend/dal/Models/LeaseFilter.cs
similarity index 100%
rename from backend/entities/Models/LeaseFilter.cs
rename to backend/dal/Models/LeaseFilter.cs
diff --git a/backend/entities/Models/OrganizationFilter.cs b/backend/dal/Models/OrganizationFilter.cs
similarity index 100%
rename from backend/entities/Models/OrganizationFilter.cs
rename to backend/dal/Models/OrganizationFilter.cs
diff --git a/backend/entities/Models/PageFilter.cs b/backend/dal/Models/PageFilter.cs
similarity index 100%
rename from backend/entities/Models/PageFilter.cs
rename to backend/dal/Models/PageFilter.cs
diff --git a/backend/entities/Models/Paged`.cs b/backend/dal/Models/Paged`.cs
similarity index 100%
rename from backend/entities/Models/Paged`.cs
rename to backend/dal/Models/Paged`.cs
diff --git a/backend/entities/Models/PropertyFilter.cs b/backend/dal/Models/PropertyFilter.cs
similarity index 100%
rename from backend/entities/Models/PropertyFilter.cs
rename to backend/dal/Models/PropertyFilter.cs
diff --git a/backend/entities/Models/ResearchFilter.cs b/backend/dal/Models/ResearchFilter.cs
similarity index 100%
rename from backend/entities/Models/ResearchFilter.cs
rename to backend/dal/Models/ResearchFilter.cs
diff --git a/backend/entities/Models/UserFilter.cs b/backend/dal/Models/UserFilter.cs
similarity index 100%
rename from backend/entities/Models/UserFilter.cs
rename to backend/dal/Models/UserFilter.cs
diff --git a/backend/dal/PIMSBaseContext.cs b/backend/dal/PIMSBaseContext.cs
deleted file mode 100644
index 60c45f4b4f..0000000000
--- a/backend/dal/PIMSBaseContext.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using Microsoft.AspNetCore.Http;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Options;
-using Pims.Core.Extensions;
-using Pims.Dal.Extensions;
-
-using System.Linq;
-using System.Text.Json;
-
-namespace Pims.Dal
-{
- ///
- /// PimsContext class, provides a data context to manage the datasource for the PIMS application.
- ///
- public partial class PimsContext : DbContext
- {
- #region Variables
- private readonly IHttpContextAccessor _httpContextAccessor;
- private readonly JsonSerializerOptions _serializerOptions;
- #endregion
-
- #region Constructors
-
- ///
- /// Creates a new instance of a PimsContext class.
- ///
- ///
- ///
- ///
- ///
- public PimsContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor = null, IOptions serializerOptions = null) : base(options)
- {
- _httpContextAccessor = httpContextAccessor;
- _serializerOptions = serializerOptions.Value;
- }
- #endregion
-
- #region Methods
- ///
- /// Configures the DbContext with the specified options.
- ///
- ///
- protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
- {
- if (!optionsBuilder.IsConfigured)
- {
- optionsBuilder.EnableSensitiveDataLogging();
- }
-
- base.OnConfiguring(optionsBuilder);
- }
-
- ///
- /// Save the entities with who created them or updated them.
- ///
- ///
- public override int SaveChanges()
- {
- // get entries that are being Added or Updated
- var modifiedEntries = ChangeTracker.Entries()
- .Where(x => x.State.IsIn(EntityState.Added, EntityState.Modified, EntityState.Deleted));
-
- // Default values are provided because depending on the claim source it may or may not have these values.
- var username = _httpContextAccessor.HttpContext.User.GetUsername() ?? "service";
- var key = _httpContextAccessor.HttpContext.User.GetUserKey();
- var directory = _httpContextAccessor.HttpContext.User.GetUserDirectory() ?? "";
- foreach (var entry in modifiedEntries)
- {
- entry.UpdateAppAuditProperties(username, key, directory);
- }
-
- return base.SaveChanges();
- }
-
- ///
- /// Wrap the save changes in a transaction for rollback.
- ///
- ///
- public int CommitTransaction()
- {
- var result = 0;
- using (var transaction = this.Database.BeginTransaction())
- {
- try
- {
- result = this.SaveChanges();
- transaction.Commit();
- }
- catch (DbUpdateException)
- {
- transaction.Rollback();
- throw;
- }
- }
- return result;
- }
-
- ///
- /// Deserialize the specified 'json' to the specified type of 'T'.
- ///
- ///
- ///
- ///
- public T Deserialize(string json)
- {
- return JsonSerializer.Deserialize(json, _serializerOptions);
- }
-
- ///
- /// Serialize the specified 'item'.
- ///
- ///
- ///
- ///
- public string Serialize(T item)
- {
- return JsonSerializer.Serialize(item, _serializerOptions);
- }
- #endregion
- }
-}
diff --git a/backend/dal/PIMSContext.cs b/backend/dal/PIMSContext.cs
index 5fe9c1cbd6..6d8b347a0e 100644
--- a/backend/dal/PIMSContext.cs
+++ b/backend/dal/PIMSContext.cs
@@ -1,6372 +1,128 @@
-using System;
+using System.Linq;
+using System.Text.Json;
+using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Pims.Dal.Entities;
-
-#nullable disable
+using Microsoft.Extensions.Options;
+using Pims.Core.Extensions;
+using Pims.Dal.Extensions;
namespace Pims.Dal
{
- public partial class PimsContext : DbContext
+ ///
+ /// PimsContext class, provides a data context to manage the datasource for the PIMS application.
+ ///
+ public class PimsContext : PimsBaseContext
{
+ #region Variables
+ private readonly IHttpContextAccessor _httpContextAccessor;
+ private readonly JsonSerializerOptions _serializerOptions;
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Creates a new instance of a PimsContext class.
+ ///
public PimsContext()
+ : base()
{
}
- public PimsContext(DbContextOptions options)
- : base(options)
+ ///
+ /// Creates a new instance of a PimsContext class.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public PimsContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor = null, IOptions serializerOptions = null) : base(options)
{
+ _httpContextAccessor = httpContextAccessor;
+ _serializerOptions = serializerOptions.Value;
}
-
- public virtual DbSet BcaAreaAmendments { get; set; }
- public virtual DbSet BcaAreaBctransitValues { get; set; }
- public virtual DbSet BcaAreaDeletes { get; set; }
- public virtual DbSet BcaAreaGeneralValues { get; set; }
- public virtual DbSet BcaAreaSchoolValues { get; set; }
- public virtual DbSet BcaAssessmentAreas { get; set; }
- public virtual DbSet BcaDataAdvices { get; set; }
- public virtual DbSet BcaDataAdviceAmendments { get; set; }
- public virtual DbSet BcaDataAdviceBctransitValues { get; set; }
- public virtual DbSet BcaDataAdviceDeletes { get; set; }
- public virtual DbSet BcaDataAdviceGeneralValues { get; set; }
- public virtual DbSet BcaDataAdviceSchoolValues { get; set; }
- public virtual DbSet BcaDefineds { get; set; }
- public virtual DbSet BcaElectoralAreas { get; set; }
- public virtual DbSet BcaFolioAddresses { get; set; }
- public virtual DbSet BcaFolioAmendments { get; set; }
- public virtual DbSet BcaFolioBctransitValues { get; set; }
- public virtual DbSet BcaFolioDescriptions { get; set; }
- public virtual DbSet BcaFolioFarms { get; set; }
- public virtual DbSet BcaFolioGeneralValues { get; set; }
- public virtual DbSet BcaFolioLandCharacteristics { get; set; }
- public virtual DbSet BcaFolioLegalDescriptions { get; set; }
- public virtual DbSet BcaFolioManagedForests { get; set; }
- public virtual DbSet BcaFolioManufacturedHomes { get; set; }
- public virtual DbSet BcaFolioOilAndGas { get; set; }
- public virtual DbSet BcaFolioRecords { get; set; }
- public virtual DbSet BcaFolioSales { get; set; }
- public virtual DbSet BcaFolioSchoolValues { get; set; }
- public virtual DbSet BcaFolioValuations { get; set; }
- public virtual DbSet BcaGeneralServices { get; set; }
- public virtual DbSet BcaImprovementDistricts { get; set; }
- public virtual DbSet BcaIslandsTrusts { get; set; }
- public virtual DbSet BcaJurisdictions { get; set; }
- public virtual DbSet BcaJurisdictionAmendments { get; set; }
- public virtual DbSet BcaJurisdictionBctransitValues { get; set; }
- public virtual DbSet BcaJurisdictionDeletes { get; set; }
- public virtual DbSet BcaJurisdictionGeneralValues { get; set; }
- public virtual DbSet BcaJurisdictionSchoolValues { get; set; }
- public virtual DbSet BcaLocalAreas { get; set; }
- public virtual DbSet BcaMinorTaxings { get; set; }
- public virtual DbSet BcaOwners { get; set; }
- public virtual DbSet BcaOwnershipGroups { get; set; }
- public virtual DbSet BcaServiceAreas { get; set; }
- public virtual DbSet BcaSpecifiedMunicipals { get; set; }
- public virtual DbSet BcaSpecifiedRegionals { get; set; }
- public virtual DbSet PimsAccessRequests { get; set; }
- public virtual DbSet PimsAccessRequestHists { get; set; }
- public virtual DbSet PimsAccessRequestOrganizations { get; set; }
- public virtual DbSet PimsAccessRequestOrganizationHists { get; set; }
- public virtual DbSet PimsAccessRequestStatusTypes { get; set; }
- public virtual DbSet PimsActivities { get; set; }
- public virtual DbSet PimsActivityHists { get; set; }
- public virtual DbSet PimsActivityModels { get; set; }
- public virtual DbSet PimsActivityModelHists { get; set; }
- public virtual DbSet PimsAddresses { get; set; }
- public virtual DbSet PimsAddressHists { get; set; }
- public virtual DbSet PimsAddressUsageTypes { get; set; }
- public virtual DbSet PimsAreaUnitTypes { get; set; }
- public virtual DbSet PimsClaims { get; set; }
- public virtual DbSet PimsClaimHists { get; set; }
- public virtual DbSet PimsContactMethods { get; set; }
- public virtual DbSet PimsContactMethodHists { get; set; }
- public virtual DbSet PimsContactMethodTypes { get; set; }
- public virtual DbSet PimsContactMgrVws { get; set; }
- public virtual DbSet PimsCountries { get; set; }
- public virtual DbSet PimsDataSourceTypes { get; set; }
- public virtual DbSet PimsDistricts { get; set; }
- public virtual DbSet PimsInsurances { get; set; }
- public virtual DbSet PimsInsuranceHists { get; set; }
- public virtual DbSet PimsInsuranceTypes { get; set; }
- public virtual DbSet PimsLeases { get; set; }
- public virtual DbSet PimsLeaseCategoryTypes { get; set; }
- public virtual DbSet PimsLeaseHists { get; set; }
- public virtual DbSet PimsLeaseInitiatorTypes { get; set; }
- public virtual DbSet PimsLeaseLicenseTypes { get; set; }
- public virtual DbSet PimsLeasePayRvblTypes { get; set; }
- public virtual DbSet PimsLeasePayments { get; set; }
- public virtual DbSet PimsLeasePaymentHists { get; set; }
- public virtual DbSet PimsLeasePaymentMethodTypes { get; set; }
- public virtual DbSet PimsLeasePaymentStatusTypes { get; set; }
- public virtual DbSet PimsLeasePmtFreqTypes { get; set; }
- public virtual DbSet PimsLeaseProgramTypes { get; set; }
- public virtual DbSet PimsLeasePurposeTypes { get; set; }
- public virtual DbSet PimsLeaseResponsibilityTypes { get; set; }
- public virtual DbSet PimsLeaseStatusTypes { get; set; }
- public virtual DbSet PimsLeaseTenants { get; set; }
- public virtual DbSet PimsLeaseTenantHists { get; set; }
- public virtual DbSet PimsLeaseTerms { get; set; }
- public virtual DbSet PimsLeaseTermHists { get; set; }
- public virtual DbSet PimsLeaseTermStatusTypes { get; set; }
- public virtual DbSet PimsLessorTypes { get; set; }
- public virtual DbSet PimsOrgIdentifierTypes { get; set; }
- public virtual DbSet PimsOrganizations { get; set; }
- public virtual DbSet PimsOrganizationAddresses { get; set; }
- public virtual DbSet PimsOrganizationAddressHists { get; set; }
- public virtual DbSet PimsOrganizationHists { get; set; }
- public virtual DbSet PimsOrganizationTypes { get; set; }
- public virtual DbSet PimsPeople { get; set; }
- public virtual DbSet PimsPersonAddresses { get; set; }
- public virtual DbSet PimsPersonAddressHists { get; set; }
- public virtual DbSet PimsPersonContactVws { get; set; }
- public virtual DbSet PimsPersonHists { get; set; }
- public virtual DbSet PimsPersonOrganizations { get; set; }
- public virtual DbSet PimsPersonOrganizationHists { get; set; }
- public virtual DbSet PimsPphStatusTypes { get; set; }
- public virtual DbSet PimsPrfPropResearchPurposeTypes { get; set; }
- public virtual DbSet PimsProjects { get; set; }
- public virtual DbSet PimsProjectHists { get; set; }
- public virtual DbSet PimsProjectNotes { get; set; }
- public virtual DbSet PimsProjectNoteHists { get; set; }
- public virtual DbSet PimsProjectProperties { get; set; }
- public virtual DbSet PimsProjectPropertyHists { get; set; }
- public virtual DbSet PimsProjectRiskTypes { get; set; }
- public virtual DbSet PimsProjectStatusTypes { get; set; }
- public virtual DbSet PimsProjectTierTypes { get; set; }
- public virtual DbSet PimsProjectTypes { get; set; }
- public virtual DbSet PimsProjectWorkflowModels { get; set; }
- public virtual DbSet PimsProjectWorkflowModelHists { get; set; }
- public virtual DbSet PimsPropPropAdjacentLandTypes { get; set; }
- public virtual DbSet PimsPropPropAnomalyTypes { get; set; }
- public virtual DbSet PimsPropPropRoadTypes { get; set; }
- public virtual DbSet PimsPropPropTenureTypes { get; set; }
- public virtual DbSet PimsPropResearchPurposeTypes { get; set; }
- public virtual DbSet PimsProperties { get; set; }
- public virtual DbSet PimsPropertyActivities { get; set; }
- public virtual DbSet PimsPropertyActivityHists { get; set; }
- public virtual DbSet PimsPropertyAdjacentLandTypes { get; set; }
- public virtual DbSet PimsPropertyAnomalyTypes { get; set; }
- public virtual DbSet PimsPropertyBoundaryVws { get; set; }
- public virtual DbSet PimsPropertyClassificationTypes { get; set; }
- public virtual DbSet PimsPropertyEvaluations { get; set; }
- public virtual DbSet PimsPropertyEvaluationHists { get; set; }
- public virtual DbSet PimsPropertyHists { get; set; }
- public virtual DbSet PimsPropertyImprovements { get; set; }
- public virtual DbSet PimsPropertyImprovementHists { get; set; }
- public virtual DbSet PimsPropertyImprovementTypes { get; set; }
- public virtual DbSet PimsPropertyLeases { get; set; }
- public virtual DbSet PimsPropertyLeaseHists { get; set; }
- public virtual DbSet PimsPropertyLocationVws { get; set; }
- public virtual DbSet PimsPropertyOrganizations { get; set; }
- public virtual DbSet PimsPropertyOrganizationHists { get; set; }
- public virtual DbSet PimsPropertyPropertyServiceFiles { get; set; }
- public virtual DbSet PimsPropertyPropertyServiceFileHists { get; set; }
- public virtual DbSet PimsPropertyResearchFiles { get; set; }
- public virtual DbSet PimsPropertyResearchFileHists { get; set; }
- public virtual DbSet PimsPropertyRoadTypes { get; set; }
- public virtual DbSet PimsPropertyServiceFiles { get; set; }
- public virtual DbSet PimsPropertyServiceFileHists { get; set; }
- public virtual DbSet PimsPropertyServiceFileTypes { get; set; }
- public virtual DbSet PimsPropertyStatusTypes { get; set; }
- public virtual DbSet PimsPropertyTaxes { get; set; }
- public virtual DbSet PimsPropertyTaxHists { get; set; }
- public virtual DbSet PimsPropertyTaxRemitTypes { get; set; }
- public virtual DbSet PimsPropertyTenureTypes { get; set; }
- public virtual DbSet PimsPropertyTypes { get; set; }
- public virtual DbSet PimsProvinceStates { get; set; }
- public virtual DbSet PimsRegions { get; set; }
- public virtual DbSet PimsRegionUsers { get; set; }
- public virtual DbSet PimsRegionUserHists { get; set; }
- public virtual DbSet PimsRequestSourceTypes { get; set; }
- public virtual DbSet PimsResearchFiles { get; set; }
- public virtual DbSet PimsResearchFileHists { get; set; }
- public virtual DbSet PimsResearchFilePurposes { get; set; }
- public virtual DbSet PimsResearchFilePurposeHists { get; set; }
- public virtual DbSet PimsResearchFileStatusTypes { get; set; }
- public virtual DbSet PimsResearchPurposeTypes { get; set; }
- public virtual DbSet PimsRoles { get; set; }
- public virtual DbSet PimsRoleClaims { get; set; }
- public virtual DbSet PimsRoleClaimHists { get; set; }
- public virtual DbSet PimsRoleHists { get; set; }
- public virtual DbSet PimsSecurityDeposits { get; set; }
- public virtual DbSet PimsSecurityDepositHists { get; set; }
- public virtual DbSet PimsSecurityDepositHolders { get; set; }
- public virtual DbSet PimsSecurityDepositHolderHists { get; set; }
- public virtual DbSet PimsSecurityDepositReturns { get; set; }
- public virtual DbSet PimsSecurityDepositReturnHists { get; set; }
- public virtual DbSet PimsSecurityDepositReturnHolders { get; set; }
- public virtual DbSet PimsSecurityDepositReturnHolderHists { get; set; }
- public virtual DbSet PimsSecurityDepositTypes { get; set; }
- public virtual DbSet PimsStaticVariables { get; set; }
- public virtual DbSet PimsStaticVariableHists { get; set; }
- public virtual DbSet PimsSurplusDeclarationTypes { get; set; }
- public virtual DbSet PimsTasks { get; set; }
- public virtual DbSet