From 1ccf8c9e75d4a2c76429e4ff17d9ed6e57803329 Mon Sep 17 00:00:00 2001
From: Ted Spence <ted@spence.net>
Date: Mon, 30 Sep 2024 12:32:25 -0700
Subject: [PATCH] Release 119 - Enable custom HTTP client (#42)

* Enable custom HTTP client

* Update for release 119

* Added documentation
---
 ProjectManagerClient.nuspec               | 22 ++++--
 src/Clients/NotificationClient.cs         |  6 +-
 src/Clients/ProjectVersionClient.cs       | 89 +++++++++++++++++++++++
 src/Clients/RiskClient.cs                 | 54 ++++++++++++++
 src/Clients/TaskMetadataClient.cs         |  8 +-
 src/IProjectManagerClient.cs              | 10 ++-
 src/Interfaces/INotificationClient.cs     |  6 +-
 src/Interfaces/IProjectVersionClient.cs   | 60 +++++++++++++++
 src/Interfaces/IRiskClient.cs             | 39 ++++++++++
 src/Interfaces/ITaskMetadataClient.cs     |  8 +-
 src/Models/DiscussionCommentFileDto.cs    |  6 +-
 src/Models/NotificationDataDto.cs         |  3 +
 src/Models/ProjectFileDto.cs              |  4 +
 src/Models/ProjectFileTaskDto.cs          |  3 +
 src/Models/ProjectVersionChangeDataDto.cs | 20 +++++
 src/Models/ProjectVersionDto.cs           |  5 ++
 src/Models/ResourcesCreateDto.cs          |  9 ++-
 src/Models/ResourcesDto.cs                |  8 ++
 src/Models/TaskDto.cs                     | 16 ++++
 src/Models/TimesheetFileDto.cs            |  3 +
 src/Models/UserError.cs                   | 13 ++++
 src/Models/WorkSpaceDto.cs                |  5 --
 src/ProjectManagerClient.cs               | 68 +++++++++++++----
 23 files changed, 429 insertions(+), 36 deletions(-)
 create mode 100644 src/Clients/ProjectVersionClient.cs
 create mode 100644 src/Clients/RiskClient.cs
 create mode 100644 src/Interfaces/IProjectVersionClient.cs
 create mode 100644 src/Interfaces/IRiskClient.cs

diff --git a/ProjectManagerClient.nuspec b/ProjectManagerClient.nuspec
index 8fe7da0..a37072f 100644
--- a/ProjectManagerClient.nuspec
+++ b/ProjectManagerClient.nuspec
@@ -2,7 +2,7 @@
 <package >
 	<metadata>
 		<id>ProjectManager.SDK</id>
-		<version>117.0.4438</version>
+		<version>119.0.4625</version>
 		<title>ProjectManager.SDK</title>
 		<authors>ProjectManager.com</authors>
 		<owners>ProjectManager.com, Inc.</owners>
@@ -14,15 +14,23 @@
 		<readme>docs/README.md</readme>
 		<summary>ProjectManager API for DotNet</summary>
 		<releaseNotes>
-            # Patch notes for 117.0.4438
+            # Patch notes for 119.0.4625
             
-            These patch notes summarize the changes from version 116.0.4391.
+            These patch notes summarize the changes from version 117.0.4438.
+            
+            Added 5 new APIs:
+            * ProjectVersion.RetrieveProjectVersions (GET /api/data/projects/{projectId}/versions)
+            * ProjectVersion.DownloadMSProjectXml (GET /api/data/projects/{projectChangeId}/version/download)
+            * ProjectVersion.RestoreProjectVersion (POST /api/data/projects/{projectId}/version/{version}/restore)
+            * ProjectVersion.CopyProjectVersion (POST /api/data/projects/{projectId}/version/{version}/copy)
+            * Risk.CreateRiskExport (POST /api/data/projects/{projectId}/risks/export)
+            
+            Renamed 1 old APIs:
+            * Renamed 'TaskMetadata.GetTasksByProjectIDAndForeignKeyID' to 'TaskMetadata.TaskMetadataSearch'
             
             Changes to data models:
-            * ResourceCreateDto: Added new field `colorName`
-            * ResourceDto: Added new field `colorName`
-            * ResourceDto: Added new field `color`
-            * ResourceUpdateDto: Added new field `colorName`
+            * TaskDto: Added new field `isLocked`
+            * TaskDto: Added new field `isMilestone`
             
 
         </releaseNotes>
diff --git a/src/Clients/NotificationClient.cs b/src/Clients/NotificationClient.cs
index d29a235..9b902b6 100644
--- a/src/Clients/NotificationClient.cs
+++ b/src/Clients/NotificationClient.cs
@@ -103,7 +103,7 @@ public async Task<AstroResult<string>> DeleteAllNotifications()
         /// workspace. Notifications are ephemeral and may be deleted when they are no longer needed.  When a user has more
         /// than 1,000 pending notifications some old notifications will be deleted automatically.
         /// </summary>
-        /// <param name="id"></param>
+        /// <param name="id">The unique identifier of the notification to mark read</param>
         public async Task<AstroResult<NotificationTimestampDto>> MarkNotificationRead(Guid id)
         {
             var url = $"/api/data/notifications/{id}/markread";
@@ -130,7 +130,7 @@ public async Task<AstroResult<NotificationTimestampDto>> ReadAllNotifications()
         /// workspace. Notifications are ephemeral and may be deleted when they are no longer needed.  When a user has more
         /// than 1,000 pending notifications some old notifications will be deleted automatically.
         /// </summary>
-        /// <param name="id"></param>
+        /// <param name="id">The unique identifier of the notification to mark read</param>
         public async Task<AstroResult<string>> DeleteNotification(Guid id)
         {
             var url = $"/api/data/notifications/delete/{id}";
@@ -144,7 +144,7 @@ public async Task<AstroResult<string>> DeleteNotification(Guid id)
         /// workspace. Notifications are ephemeral and may be deleted when they are no longer needed.  When a user has more
         /// than 1,000 pending notifications some old notifications will be deleted automatically.
         /// </summary>
-        /// <param name="id"></param>
+        /// <param name="id">The unique identifier of the notification to mark read</param>
         public async Task<AstroResult<string>> MarkNotificationUnread(Guid id)
         {
             var url = $"/api/data/notifications/{id}/markunread";
diff --git a/src/Clients/ProjectVersionClient.cs b/src/Clients/ProjectVersionClient.cs
new file mode 100644
index 0000000..a4ac83d
--- /dev/null
+++ b/src/Clients/ProjectVersionClient.cs
@@ -0,0 +1,89 @@
+/***
+ * ProjectManager API for C#
+ *
+ * (c) 2023-2024 ProjectManager.com, Inc.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @author     ProjectManager.com <support@projectmanager.com>
+ * @copyright  2023-2024 ProjectManager.com, Inc.
+ * @link       https://github.com/projectmgr/projectmanager-sdk-csharp
+ */
+
+
+
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading.Tasks;
+using ProjectManager.SDK.Interfaces;
+using ProjectManager.SDK.Models;
+
+
+namespace ProjectManager.SDK.Clients
+{
+    /// <summary>
+    /// API methods related to ProjectVersion
+    /// </summary>
+    public class ProjectVersionClient : IProjectVersionClient
+    {
+        private readonly ProjectManagerClient _client;
+
+        /// <summary>
+        /// Constructor
+        /// </summary>
+        public ProjectVersionClient(ProjectManagerClient client)
+        {
+            _client = client;
+        }
+
+        /// <summary>
+        /// Returns projects versions including version, user who made changes
+        /// </summary>
+        /// <param name="projectId">The unique identifier of the Project</param>
+        public async Task<AstroResult<ProjectVersionDto[]>> RetrieveProjectVersions(Guid projectId)
+        {
+            var url = $"/api/data/projects/{projectId}/versions";
+            return await _client.Request<ProjectVersionDto[]>(HttpMethod.Get, url, null);
+        }
+
+        /// <summary>
+        /// Exports and returns project version as an MS Project XML attachment
+        /// </summary>
+        /// <param name="projectChangeId">Project change Guid</param>
+        public async Task<AstroResult<byte[]>> DownloadMSProjectXml(Guid projectChangeId)
+        {
+            var url = $"/api/data/projects/{projectChangeId}/version/download";
+            return await _client.Request<byte[]>(HttpMethod.Get, url, null);
+        }
+
+        /// <summary>
+        /// Restores a Project to the state it was in at a specific Version in time.
+        ///
+        /// If successful, all changes made to the Project since this Version will be undone and the Project will
+        /// return to its former state.
+        /// </summary>
+        /// <param name="projectId">The unique identifier of the Project to restore</param>
+        /// <param name="version">The version number to restore to</param>
+        public async Task<AstroResult<string>> RestoreProjectVersion(Guid projectId, int version)
+        {
+            var url = $"/api/data/projects/{projectId}/version/{version}/restore";
+            return await _client.Request<string>(HttpMethod.Post, url, null);
+        }
+
+        /// <summary>
+        /// Create a Copy of a Project as of a specific Version, optionally moving it to a new Timezone.
+        /// </summary>
+        /// <param name="projectId">The unique identifier of the Project to copy</param>
+        /// <param name="version">The version number of the Project to copy</param>
+        /// <param name="timezoneOffset">If specified, sets the default timezone of the newly copied Project to this specified timezone</param>
+        public async Task<AstroResult<string>> CopyProjectVersion(Guid projectId, int version, int? timezoneOffset = null)
+        {
+            var url = $"/api/data/projects/{projectId}/version/{version}/copy";
+            var options = new Dictionary<string, object>();
+            if (timezoneOffset != null) { options["timezoneOffset"] = timezoneOffset; }
+            return await _client.Request<string>(HttpMethod.Post, url, options);
+        }
+    }
+}
diff --git a/src/Clients/RiskClient.cs b/src/Clients/RiskClient.cs
new file mode 100644
index 0000000..40e6a80
--- /dev/null
+++ b/src/Clients/RiskClient.cs
@@ -0,0 +1,54 @@
+/***
+ * ProjectManager API for C#
+ *
+ * (c) 2023-2024 ProjectManager.com, Inc.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @author     ProjectManager.com <support@projectmanager.com>
+ * @copyright  2023-2024 ProjectManager.com, Inc.
+ * @link       https://github.com/projectmgr/projectmanager-sdk-csharp
+ */
+
+
+
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading.Tasks;
+using ProjectManager.SDK.Interfaces;
+using ProjectManager.SDK.Models;
+
+
+namespace ProjectManager.SDK.Clients
+{
+    /// <summary>
+    /// API methods related to Risk
+    /// </summary>
+    public class RiskClient : IRiskClient
+    {
+        private readonly ProjectManagerClient _client;
+
+        /// <summary>
+        /// Constructor
+        /// </summary>
+        public RiskClient(ProjectManagerClient client)
+        {
+            _client = client;
+        }
+
+        /// <summary>
+        /// Initiates a new Export action for Risks.
+        ///
+        /// Returns the identifier of this Risk Export.
+        /// </summary>
+        /// <param name="projectId">The unique identifier of the Project for which to export Risks</param>
+        /// <param name="body">The settings to use for this export action</param>
+        public async Task<AstroResult<ExportDto>> CreateRiskExport(Guid projectId, RiskExportSettingsDto body)
+        {
+            var url = $"/api/data/projects/{projectId}/risks/export";
+            return await _client.RequestWithBody<ExportDto>(HttpMethod.Post, url, null, body);
+        }
+    }
+}
diff --git a/src/Clients/TaskMetadataClient.cs b/src/Clients/TaskMetadataClient.cs
index da1b310..dff0217 100644
--- a/src/Clients/TaskMetadataClient.cs
+++ b/src/Clients/TaskMetadataClient.cs
@@ -54,7 +54,13 @@ public async Task<AstroResult<string>> AddMetadata(Guid taskId, TaskMetadataUpda
             return await _client.RequestWithBody<string>(HttpMethod.Put, url, options, body);
         }
 
-        public async Task<AstroResult<TaskMetadataSearchDto[]>> GetTasksByProjectIDAndForeignKeyID(Guid projectId, string foreignKey = null, bool? isSystem = null)
+        /// <summary>
+        /// Get tasks by project ID and foreign key ID
+        /// </summary>
+        /// <param name="foreignKey">Foreign Key ID</param>
+        /// <param name="projectId">Project ID</param>
+        /// <param name="isSystem">If metadata is for system or customer, isSystem = true is only of ProjectManager</param>
+        public async Task<AstroResult<TaskMetadataSearchDto[]>> TaskMetadataSearch(Guid projectId, string foreignKey = null, bool? isSystem = null)
         {
             var url = $"/api/data/projects/{projectId}/tasks/metadata";
             var options = new Dictionary<string, object>();
diff --git a/src/IProjectManagerClient.cs b/src/IProjectManagerClient.cs
index 5929b11..f1d72ae 100644
--- a/src/IProjectManagerClient.cs
+++ b/src/IProjectManagerClient.cs
@@ -9,7 +9,7 @@
  * @author     ProjectManager.com <support@projectmanager.com>
  *             
  * @copyright  2023-2024 ProjectManager.com, Inc.
- * @version    117.0.4438
+ * @version    119.0.4625
  * @link       https://github.com/projectmgr/projectmanager-sdk-csharp
  */
 
@@ -120,6 +120,10 @@ public interface IProjectManagerClient
         /// </summary>
         IProjectTemplateClient ProjectTemplate { get; }
         /// <summary>
+        /// API methods related to ProjectVersion
+        /// </summary>
+        IProjectVersionClient ProjectVersion { get; }
+        /// <summary>
         /// API methods related to Resource
         /// </summary>
         IResourceClient Resource { get; }
@@ -132,6 +136,10 @@ public interface IProjectManagerClient
         /// </summary>
         IResourceTeamClient ResourceTeam { get; }
         /// <summary>
+        /// API methods related to Risk
+        /// </summary>
+        IRiskClient Risk { get; }
+        /// <summary>
         /// API methods related to Tag
         /// </summary>
         ITagClient Tag { get; }
diff --git a/src/Interfaces/INotificationClient.cs b/src/Interfaces/INotificationClient.cs
index b6ec8db..0b44a35 100644
--- a/src/Interfaces/INotificationClient.cs
+++ b/src/Interfaces/INotificationClient.cs
@@ -74,7 +74,7 @@ public interface INotificationClient
         /// workspace. Notifications are ephemeral and may be deleted when they are no longer needed.  When a user has more
         /// than 1,000 pending notifications some old notifications will be deleted automatically.
         /// </summary>
-        /// <param name="id"></param>
+        /// <param name="id">The unique identifier of the notification to mark read</param>
         Task<AstroResult<NotificationTimestampDto>> MarkNotificationRead(Guid id);
 
         /// <summary>
@@ -93,7 +93,7 @@ public interface INotificationClient
         /// workspace. Notifications are ephemeral and may be deleted when they are no longer needed.  When a user has more
         /// than 1,000 pending notifications some old notifications will be deleted automatically.
         /// </summary>
-        /// <param name="id"></param>
+        /// <param name="id">The unique identifier of the notification to mark read</param>
         Task<AstroResult<string>> DeleteNotification(Guid id);
 
         /// <summary>
@@ -103,7 +103,7 @@ public interface INotificationClient
         /// workspace. Notifications are ephemeral and may be deleted when they are no longer needed.  When a user has more
         /// than 1,000 pending notifications some old notifications will be deleted automatically.
         /// </summary>
-        /// <param name="id"></param>
+        /// <param name="id">The unique identifier of the notification to mark read</param>
         Task<AstroResult<string>> MarkNotificationUnread(Guid id);
     }
 }
diff --git a/src/Interfaces/IProjectVersionClient.cs b/src/Interfaces/IProjectVersionClient.cs
new file mode 100644
index 0000000..7eedb81
--- /dev/null
+++ b/src/Interfaces/IProjectVersionClient.cs
@@ -0,0 +1,60 @@
+/***
+ * ProjectManager API for C#
+ *
+ * (c) 2023-2024 ProjectManager.com, Inc.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @author     ProjectManager.com <support@projectmanager.com>
+ * @copyright  2023-2024 ProjectManager.com, Inc.
+ * @link       https://github.com/projectmgr/projectmanager-sdk-csharp
+ */
+
+
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using ProjectManager.SDK.Models;
+
+
+namespace ProjectManager.SDK.Interfaces
+{
+    /// <summary>
+    /// API methods related to ProjectVersion
+    /// </summary>
+    public interface IProjectVersionClient
+    {
+
+        /// <summary>
+        /// Returns projects versions including version, user who made changes
+        /// </summary>
+        /// <param name="projectId">The unique identifier of the Project</param>
+        Task<AstroResult<ProjectVersionDto[]>> RetrieveProjectVersions(Guid projectId);
+
+        /// <summary>
+        /// Exports and returns project version as an MS Project XML attachment
+        /// </summary>
+        /// <param name="projectChangeId">Project change Guid</param>
+        Task<AstroResult<byte[]>> DownloadMSProjectXml(Guid projectChangeId);
+
+        /// <summary>
+        /// Restores a Project to the state it was in at a specific Version in time.
+        ///
+        /// If successful, all changes made to the Project since this Version will be undone and the Project will
+        /// return to its former state.
+        /// </summary>
+        /// <param name="projectId">The unique identifier of the Project to restore</param>
+        /// <param name="version">The version number to restore to</param>
+        Task<AstroResult<string>> RestoreProjectVersion(Guid projectId, int version);
+
+        /// <summary>
+        /// Create a Copy of a Project as of a specific Version, optionally moving it to a new Timezone.
+        /// </summary>
+        /// <param name="projectId">The unique identifier of the Project to copy</param>
+        /// <param name="version">The version number of the Project to copy</param>
+        /// <param name="timezoneOffset">If specified, sets the default timezone of the newly copied Project to this specified timezone</param>
+        Task<AstroResult<string>> CopyProjectVersion(Guid projectId, int version, int? timezoneOffset = null);
+    }
+}
diff --git a/src/Interfaces/IRiskClient.cs b/src/Interfaces/IRiskClient.cs
new file mode 100644
index 0000000..0729c76
--- /dev/null
+++ b/src/Interfaces/IRiskClient.cs
@@ -0,0 +1,39 @@
+/***
+ * ProjectManager API for C#
+ *
+ * (c) 2023-2024 ProjectManager.com, Inc.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @author     ProjectManager.com <support@projectmanager.com>
+ * @copyright  2023-2024 ProjectManager.com, Inc.
+ * @link       https://github.com/projectmgr/projectmanager-sdk-csharp
+ */
+
+
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using ProjectManager.SDK.Models;
+
+
+namespace ProjectManager.SDK.Interfaces
+{
+    /// <summary>
+    /// API methods related to Risk
+    /// </summary>
+    public interface IRiskClient
+    {
+
+        /// <summary>
+        /// Initiates a new Export action for Risks.
+        ///
+        /// Returns the identifier of this Risk Export.
+        /// </summary>
+        /// <param name="projectId">The unique identifier of the Project for which to export Risks</param>
+        /// <param name="body">The settings to use for this export action</param>
+        Task<AstroResult<ExportDto>> CreateRiskExport(Guid projectId, RiskExportSettingsDto body);
+    }
+}
diff --git a/src/Interfaces/ITaskMetadataClient.cs b/src/Interfaces/ITaskMetadataClient.cs
index 268256a..f1e5a1d 100644
--- a/src/Interfaces/ITaskMetadataClient.cs
+++ b/src/Interfaces/ITaskMetadataClient.cs
@@ -36,6 +36,12 @@ public interface ITaskMetadataClient
         /// <param name="body">The metadata</param>
         Task<AstroResult<string>> AddMetadata(Guid taskId, TaskMetadataUpdateDto body, bool? isSystem = null, bool? isOverride = null);
 
-        Task<AstroResult<TaskMetadataSearchDto[]>> GetTasksByProjectIDAndForeignKeyID(Guid projectId, string foreignKey = null, bool? isSystem = null);
+        /// <summary>
+        /// Get tasks by project ID and foreign key ID
+        /// </summary>
+        /// <param name="foreignKey">Foreign Key ID</param>
+        /// <param name="projectId">Project ID</param>
+        /// <param name="isSystem">If metadata is for system or customer, isSystem = true is only of ProjectManager</param>
+        Task<AstroResult<TaskMetadataSearchDto[]>> TaskMetadataSearch(Guid projectId, string foreignKey = null, bool? isSystem = null);
     }
 }
diff --git a/src/Models/DiscussionCommentFileDto.cs b/src/Models/DiscussionCommentFileDto.cs
index 78849b4..2af7ee5 100644
--- a/src/Models/DiscussionCommentFileDto.cs
+++ b/src/Models/DiscussionCommentFileDto.cs
@@ -20,6 +20,10 @@
 namespace ProjectManager.SDK.Models
 {
 
+    /// <summary>
+    /// The DiscussionCommentFile represents a file that has been attached to a discussion
+    /// and is available for download.
+    /// </summary>
     public class DiscussionCommentFileDto : ApiModel
     {
 
@@ -34,7 +38,7 @@ public class DiscussionCommentFileDto : ApiModel
         public string Name { get; set; }
 
         /// <summary>
-        /// The url of the file which can be used for downloading
+        /// The url of the DownloadFile API to retrieve this file
         /// </summary>
         public string Url { get; set; }
     }
diff --git a/src/Models/NotificationDataDto.cs b/src/Models/NotificationDataDto.cs
index 1ff01dd..c17dd2f 100644
--- a/src/Models/NotificationDataDto.cs
+++ b/src/Models/NotificationDataDto.cs
@@ -51,6 +51,9 @@ public class NotificationDataDto : ApiModel
         /// </summary>
         public string ProjectName { get; set; }
 
+        /// <summary>
+        /// Name of the task this notification is related to
+        /// </summary>
         public string TaskName { get; set; }
 
         /// <summary>
diff --git a/src/Models/ProjectFileDto.cs b/src/Models/ProjectFileDto.cs
index 1f7ca87..2dc0cf9 100644
--- a/src/Models/ProjectFileDto.cs
+++ b/src/Models/ProjectFileDto.cs
@@ -20,6 +20,10 @@
 namespace ProjectManager.SDK.Models
 {
 
+    /// <summary>
+    /// The ProjectFile represents an attached file that is connected to a Project
+    /// and can be retrieved for download.
+    /// </summary>
     public class ProjectFileDto : ApiModel
     {
 
diff --git a/src/Models/ProjectFileTaskDto.cs b/src/Models/ProjectFileTaskDto.cs
index 1a0e3ad..e9aa758 100644
--- a/src/Models/ProjectFileTaskDto.cs
+++ b/src/Models/ProjectFileTaskDto.cs
@@ -20,6 +20,9 @@
 namespace ProjectManager.SDK.Models
 {
 
+    /// <summary>
+    /// Represents information about a Task that is relevant to a ProjectFile
+    /// </summary>
     public class ProjectFileTaskDto : ApiModel
     {
 
diff --git a/src/Models/ProjectVersionChangeDataDto.cs b/src/Models/ProjectVersionChangeDataDto.cs
index ec44722..dad429d 100644
--- a/src/Models/ProjectVersionChangeDataDto.cs
+++ b/src/Models/ProjectVersionChangeDataDto.cs
@@ -20,17 +20,37 @@
 namespace ProjectManager.SDK.Models
 {
 
+    /// <summary>
+    /// A ProjectVersionChangeData is information about a change made to a Project that took
+    /// it from one Version to another.  The information in this object can help track the
+    /// details of changes made by the user.
+    /// </summary>
     public class ProjectVersionChangeDataDto : ApiModel
     {
 
+        /// <summary>
+        /// The type of change made
+        /// </summary>
         public string Type { get; set; }
 
+        /// <summary>
+        /// The method used to make the change
+        /// </summary>
         public string Method { get; set; }
 
+        /// <summary>
+        /// The property that was changed, if any
+        /// </summary>
         public string Property { get; set; }
 
+        /// <summary>
+        /// The new value of the property, or null if the property was cleared
+        /// </summary>
         public string Value { get; set; }
 
+        /// <summary>
+        /// The prior version number to restore to
+        /// </summary>
         public int? RestoreVersion { get; set; }
     }
 }
diff --git a/src/Models/ProjectVersionDto.cs b/src/Models/ProjectVersionDto.cs
index 351f42e..35d218a 100644
--- a/src/Models/ProjectVersionDto.cs
+++ b/src/Models/ProjectVersionDto.cs
@@ -20,6 +20,11 @@
 namespace ProjectManager.SDK.Models
 {
 
+    /// <summary>
+    /// A ProjectVersion is a snapshot of a Project at a specific moment in time.  Information on
+    /// the ProjectVersion record keeps track of the unique identity of this version, plus the name
+    /// and details of the user who created this version, and the changes that were made.
+    /// </summary>
     public class ProjectVersionDto : ApiModel
     {
 
diff --git a/src/Models/ResourcesCreateDto.cs b/src/Models/ResourcesCreateDto.cs
index 8fdb09d..7094212 100644
--- a/src/Models/ResourcesCreateDto.cs
+++ b/src/Models/ResourcesCreateDto.cs
@@ -21,6 +21,9 @@ namespace ProjectManager.SDK.Models
 {
 
     /// <summary>
+    /// The ResourcesCreate object allows you to create multiple Users with a single API call.
+    /// In ProjectManager.com, a User is a special class of Resource.
+    ///
     /// A Resource represents a person, material, or tool that is used within your Projects.
     /// When you attach a Resources to more than one Task, the software will schedule the usage
     /// of your Resource so that it is not allocated to more than one Task at the same time.
@@ -31,10 +34,14 @@ public class ResourcesCreateDto : ApiModel
     {
 
         /// <summary>
-        /// When creating a user they will also be added to the projectIds specified. If null or empty the user will be invited but no access will be given to any projects.
+        /// When creating a user they will also be added to the projectIds specified. If null or empty the user will be
+        /// invited but no access will be given to any projects.
         /// </summary>
         public Guid[] ProjectIds { get; set; }
 
+        /// <summary>
+        /// A list of Users to create
+        /// </summary>
         public ResourceCreateDto[] Users { get; set; }
     }
 }
diff --git a/src/Models/ResourcesDto.cs b/src/Models/ResourcesDto.cs
index 4b37d3e..3bb8c76 100644
--- a/src/Models/ResourcesDto.cs
+++ b/src/Models/ResourcesDto.cs
@@ -21,6 +21,8 @@ namespace ProjectManager.SDK.Models
 {
 
     /// <summary>
+    /// The Resources object represents the results of a bulk Resource creation API call.
+    ///
     /// A Resource represents a person, material, or tool that is used within your Projects.
     /// When you attach a Resources to more than one Task, the software will schedule the usage
     /// of your Resource so that it is not allocated to more than one Task at the same time.
@@ -30,8 +32,14 @@ namespace ProjectManager.SDK.Models
     public class ResourcesDto : ApiModel
     {
 
+        /// <summary>
+        /// The list of the Resources created by this API call.
+        /// </summary>
         public ResourceDto[] Resources { get; set; }
 
+        /// <summary>
+        /// The list of errors that occurred for Resources that could not be created.
+        /// </summary>
         public UserError[] Errors { get; set; }
     }
 }
diff --git a/src/Models/TaskDto.cs b/src/Models/TaskDto.cs
index 0c31e75..4bb0848 100644
--- a/src/Models/TaskDto.cs
+++ b/src/Models/TaskDto.cs
@@ -182,6 +182,22 @@ public class TaskDto : ApiModel
         /// </summary>
         public bool? IsSummary { get; set; }
 
+        /// <summary>
+        /// Unlocked tasks can be adjusted by changes to their dependencies, resource leveling, or other factors.
+        ///
+        /// All tasks are unlocked by default.
+        ///
+        /// If a task is set to `IsLocked` = `true`, the dates and assigned resources are locked for this task and will not
+        /// be automatically changed by any process.
+        /// </summary>
+        public bool? IsLocked { get; set; }
+
+        /// <summary>
+        /// True if this task is a milestone.  Milestones represent a specific point in time for the project.  When a
+        /// milestone is locked, it represents a fixed time within the project that can be used to relate to other tasks.
+        /// </summary>
+        public bool? IsMilestone { get; set; }
+
         /// <summary>
         /// Return the priority of a task
         /// </summary>
diff --git a/src/Models/TimesheetFileDto.cs b/src/Models/TimesheetFileDto.cs
index 7d59e87..96cade6 100644
--- a/src/Models/TimesheetFileDto.cs
+++ b/src/Models/TimesheetFileDto.cs
@@ -20,6 +20,9 @@
 namespace ProjectManager.SDK.Models
 {
 
+    /// <summary>
+    /// Represents information about a file attached to a Timesheet.
+    /// </summary>
     public class TimesheetFileDto : ApiModel
     {
 
diff --git a/src/Models/UserError.cs b/src/Models/UserError.cs
index 034e42a..4c414b9 100644
--- a/src/Models/UserError.cs
+++ b/src/Models/UserError.cs
@@ -20,13 +20,26 @@
 namespace ProjectManager.SDK.Models
 {
 
+    /// <summary>
+    /// Represents an individual error for a specific Resource that could not be created in the context
+    /// of a bulk Resource creation API call.
+    /// </summary>
     public class UserError : ApiModel
     {
 
+        /// <summary>
+        /// The email of the Resource that could not be created
+        /// </summary>
         public string Email { get; set; }
 
+        /// <summary>
+        /// A description of the reason this Resource could not be created
+        /// </summary>
         public string Reason { get; set; }
 
+        /// <summary>
+        /// A status code explaining the category of reason this Resource could not be created
+        /// </summary>
         public string StatusCode { get; set; }
     }
 }
diff --git a/src/Models/WorkSpaceDto.cs b/src/Models/WorkSpaceDto.cs
index 933331e..f02539b 100644
--- a/src/Models/WorkSpaceDto.cs
+++ b/src/Models/WorkSpaceDto.cs
@@ -43,11 +43,6 @@ public class WorkSpaceDto : ApiModel
         /// </summary>
         public string CustomProductDomain { get; set; }
 
-        /// <summary>
-        /// TODO - What is this value?
-        /// </summary>
-        public Guid? CustomerId { get; set; }
-
         /// <summary>
         /// This value is set to true if the user who retrieves this Workspace object via an API call is
         /// the owner of this Workspace.
diff --git a/src/ProjectManagerClient.cs b/src/ProjectManagerClient.cs
index 05c78ec..dca403f 100644
--- a/src/ProjectManagerClient.cs
+++ b/src/ProjectManagerClient.cs
@@ -9,7 +9,7 @@
  * @author     ProjectManager.com <support@projectmanager.com>
  *             
  * @copyright  2023-2024 ProjectManager.com, Inc.
- * @version    117.0.4438
+ * @version    119.0.4625
  * @link       https://github.com/projectmgr/projectmanager-sdk-csharp
  */
 
@@ -39,7 +39,7 @@ public class ProjectManagerClient : IProjectManagerClient
         /// <summary>
         /// The version of the SDK
         /// </summary>
-        public const string SdkVersion = "117.0.4438";
+        public const string SdkVersion = "119.0.4625";
         
         private readonly string _apiUrl;
         private readonly HttpClient _client;
@@ -169,6 +169,11 @@ public class ProjectManagerClient : IProjectManagerClient
         /// </summary>
         public IProjectTemplateClient ProjectTemplate { get; }
 
+        /// <summary>
+        /// API methods related to ProjectVersion
+        /// </summary>
+        public IProjectVersionClient ProjectVersion { get; }
+
         /// <summary>
         /// API methods related to Resource
         /// </summary>
@@ -184,6 +189,11 @@ public class ProjectManagerClient : IProjectManagerClient
         /// </summary>
         public IResourceTeamClient ResourceTeam { get; }
 
+        /// <summary>
+        /// API methods related to Risk
+        /// </summary>
+        public IRiskClient Risk { get; }
+
         /// <summary>
         /// API methods related to Tag
         /// </summary>
@@ -249,16 +259,26 @@ public class ProjectManagerClient : IProjectManagerClient
         /// Internal constructor for the client. You should always begin with `WithEnvironment()` or `WithCustomEnvironment`.
         /// </summary>
         /// <param name="baseEndpoint">The API endpoint to contact</param>
+        /// <param name="client">HTTP Client, when null a new one will be constructed</param>
         /// <param name="clientHandler">Handler for the HTTP client, when null the default handler will be used</param>
-        private ProjectManagerClient(Uri baseEndpoint, HttpClientHandler clientHandler)
+        private ProjectManagerClient(Uri baseEndpoint, HttpClient client, HttpClientHandler clientHandler)
         {
-            // Add support for HTTP compression
-            var handler = clientHandler ?? new HttpClientHandler();
-            handler.AutomaticDecompression = DecompressionMethods.GZip;
-            
-            // We intentionally use a single HttpClient object for the lifetime of this API connection.
-            // Best practices: https://bytedev.medium.com/net-core-httpclient-best-practices-4c1b20e32c6
-            _client = new HttpClient(handler);
+            // If customer wants to use a custom client, let them
+            if (client != null)
+            {
+                _client = client;
+            }
+            else
+            {
+                // Add support for HTTP compression
+                var handler = clientHandler ?? new HttpClientHandler();
+                handler.AutomaticDecompression = DecompressionMethods.GZip;
+
+                // We intentionally use a single HttpClient object for the lifetime of this API connection.
+                // Best practices: https://bytedev.medium.com/net-core-httpclient-best-practices-4c1b20e32c6
+                _client = new HttpClient(handler);
+            }
+
             _apiUrl = baseEndpoint.ToString();
             ApiKey = new ApiKeyClient(this);
             Changeset = new ChangesetClient(this);
@@ -284,9 +304,11 @@ private ProjectManagerClient(Uri baseEndpoint, HttpClientHandler clientHandler)
             ProjectPriority = new ProjectPriorityClient(this);
             ProjectStatus = new ProjectStatusClient(this);
             ProjectTemplate = new ProjectTemplateClient(this);
+            ProjectVersion = new ProjectVersionClient(this);
             Resource = new ResourceClient(this);
             ResourceSkill = new ResourceSkillClient(this);
             ResourceTeam = new ResourceTeamClient(this);
+            Risk = new RiskClient(this);
             Tag = new TagClient(this);
             Task = new TaskClient(this);
             TaskAssignee = new TaskAssigneeClient(this);
@@ -319,7 +341,7 @@ public static ProjectManagerClient WithEnvironment(string env, HttpClientHandler
             switch (env)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
             {
                 case "production":
-                    return new ProjectManagerClient(new Uri("https://api.projectmanager.com"), clientHandler);
+                    return new ProjectManagerClient(new Uri("https://api.projectmanager.com"), null, clientHandler);
             }
     
             throw new InvalidOperationException($"Unknown environment: {env}");
@@ -333,11 +355,31 @@ public static ProjectManagerClient WithEnvironment(string env, HttpClientHandler
         /// <param name="customEndpoint">The custom endpoint to use for this client</param>
         /// <param name="clientHandler">Optional handler to set specific settings for the HTTP client</param>
         /// <returns>The API client to use</returns>
-        public static ProjectManagerClient WithCustomEnvironment(Uri customEndpoint, HttpClientHandler clientHandler = null)
+        public static ProjectManagerClient WithCustomEnvironment(Uri customEndpoint, HttpClientHandler clientHandler)
         {
-            return new ProjectManagerClient(customEndpoint, clientHandler);
+            return new ProjectManagerClient(customEndpoint, null, clientHandler);
         }
         
+        /// <summary>
+        /// Construct a client that uses a custom HTTP Client object.  You can use this method to implement your own
+        /// patterns for HttpClient tracking, usage, or to implement a mocked HTTP client during testing.
+        /// Note that for performance reasons your HttpClient should support connection pooling (to avoid unnecessary
+        /// delays in SSL validation) and you should also support GZip encoding.
+        /// </summary>
+        /// <param name="env">The named environment to use; should be "production"</param>
+        /// <param name="client">The HttpClient object to use</param>
+        /// <returns>The API client to use</returns>
+        public static ProjectManagerClient WithCustomHttpClient(string env, HttpClient client)
+        {
+            switch (env)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
+            {
+                case "production":
+                    return new ProjectManagerClient(new Uri("https://api.projectmanager.com"), client, null);
+            }
+    
+            throw new InvalidOperationException($"Unknown environment: {env}");
+        }        
+
         /// <summary>
         /// Configure this SDK client to set an application name which will be sent with each API call for debugging
         /// purposes.