diff --git a/.github/workflows/publish-nupkg.yml b/.github/workflows/publish-nupkg.yml
new file mode 100644
index 0000000..dca1323
--- /dev/null
+++ b/.github/workflows/publish-nupkg.yml
@@ -0,0 +1,71 @@
+name: Build and Publish to Azure Artifacts / GitHub Packages
+
+on:
+ release:
+ types: [published]
+
+env:
+ AZURE_ARTIFACTS_FEED_URL: https://pkgs.dev.azure.com/intuitionps/01c84548-9607-4655-80e7-6ad95390a38c/_packaging/private/nuget/v3/index.json
+ GITHUB_PACKAGES_URL: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
+ PROJECT_NAME: Ellucian.Ethos.Integration
+ BUILD_CONFIGURATION: 'Release' # set this to the appropriate build configuration
+ DOTNET_VERSION: '6.x'
+
+jobs:
+ build:
+ runs-on: windows-latest
+ steps:
+ # Checkout the repo
+ - uses: actions/checkout@v2
+
+ # Setup .NET Core SDK
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: ${{ env.DOTNET_VERSION }}
+
+ # Run dotnet build and test
+ - name: dotnet build and test
+ run: |
+ dotnet nuget add source ${{ env.GITHUB_PACKAGES_URL }} --name intuitionps --username ${{ github.actor }} --password ${{ secrets.GH_PACKAGES_PAT }}
+ dotnet nuget add source https://api.nuget.org/v3/index.json -name nugetorg
+ dotnet restore
+ dotnet build --configuration '${{ env.BUILD_CONFIGURATION }}'
+ dotnet test --configuration '${{ env.BUILD_CONFIGURATION }}'
+
+ az-artifacts-build-and-deploy:
+ needs: build
+ runs-on: windows-latest
+ steps:
+ # Checkout the repo
+ - uses: actions/checkout@v2
+
+ # Extract git version
+ - name: Extract git tag
+ run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
+
+ # Setup .NET Core SDK
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: ${{ env.DOTNET_VERSION }}
+ source-url: ${{ env.AZURE_ARTIFACTS_FEED_URL }}
+ env:
+ NUGET_AUTH_TOKEN: ${{ secrets.AZURE_ARTIFACTS_PAT }}
+
+ # Run dotnet build and package
+ - name: dotnet build and publish
+ run: |
+ dotnet nuget add source ${{ env.GITHUB_PACKAGES_URL }} --name intuitionps --username ${{ github.actor }} --password ${{ secrets.GH_PACKAGES_PAT }}
+ dotnet nuget add source https://api.nuget.org/v3/index.json -name nugetorg
+ dotnet restore
+ dotnet build --configuration '${{ env.BUILD_CONFIGURATION }}' /p:Version=${{ github.event.release.tag_name }}
+ dotnet pack -c '${{ env.BUILD_CONFIGURATION }}'
+
+ # Publish the package to Azure Artifacts | must specify our version since the ellucian-developer/integration-sdk-csharp gets packaged as well
+ - name: 'dotnet publish to Azure Artifacts'
+ run: dotnet nuget push --api-key AzureArtifacts ${{ env.PROJECT_NAME }}\bin\Release\${{ env.PROJECT_NAME }}.${{ github.event.release.tag_name }}.nupkg
+
+ # Publish the package to GitHub Packages | must specify our version since the ellucian-developer/integration-sdk-csharp gets packaged as well
+ - name: 'dotnet publish to GitHub Packages'
+ run: dotnet nuget push --api-key ${{ secrets.GH_PACKAGES_PAT }} --source ${{ env.GITHUB_PACKAGES_URL }} ${{ env.PROJECT_NAME }}\bin\Release\${{ env.PROJECT_NAME }}.${{ github.event.release.tag_name }}.nupkg
diff --git a/ColleagueApiExample/ColleagueApiExample.csproj b/ColleagueApiExample/ColleagueApiExample.csproj
new file mode 100644
index 0000000..8100c8c
--- /dev/null
+++ b/ColleagueApiExample/ColleagueApiExample.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/ColleagueApiExample/Program.cs b/ColleagueApiExample/Program.cs
new file mode 100644
index 0000000..717c212
--- /dev/null
+++ b/ColleagueApiExample/Program.cs
@@ -0,0 +1,37 @@
+using Ellucian.Ethos.Integration.Client;
+using Ellucian.Ethos.Integration.Client.Filter.Extensions;
+using Ellucian.Ethos.Integration.Client.Proxy.Filter;
+using Newtonsoft.Json.Linq;
+
+var proxyClient =
+ new EthosClientBuilder(
+ colleagueApiUrl: "",
+ colleagueApiUsername: "",
+ colleagueApiPassword: "")
+ .BuildColleagueWebApiProxyclient();
+
+var academicPeriod =
+ await proxyClient.GetAsJObjectByIdAsync("academic-periods", "a4b5fddc-fa2f-4e94-82e9-cbe219a5029b");
+
+Console.WriteLine(academicPeriod.ToString());
+
+var queryClient =
+ new EthosClientBuilder(
+ colleagueApiUrl: "",
+ colleagueApiUsername: "",
+ colleagueApiPassword: "")
+ .BuildColleagueWebApiFilterQueryClient();
+
+var filter =
+ new CriteriaFilter()
+ .WithSimpleCriteria("startOn", ("$gte", "2020-01-01"));
+
+var responses =
+ await queryClient.GetPagesWithCriteriaFilterAsync("academic-periods", filter);
+
+foreach (var response in responses)
+{
+ var acadPeriods = JArray.Parse(response.Content);
+
+ Console.WriteLine(acadPeriods.ToString());
+}
\ No newline at end of file
diff --git a/Ellucian.Ethos.Integration.sln b/Ellucian.Ethos.Integration.sln
index 3c713e7..e93ce60 100644
--- a/Ellucian.Ethos.Integration.sln
+++ b/Ellucian.Ethos.Integration.sln
@@ -17,6 +17,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{80815184
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{1402F60B-E306-4A41-B41E-2EF9136DFB36}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{B29BB850-080B-4A04-AF80-BB1B3FD91BC9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColleagueApiExample", "ColleagueApiExample\ColleagueApiExample.csproj", "{41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -51,6 +55,18 @@ Global
{DC26CC20-E923-4AA4-93C2-BCBEFC10EF26}.Release|x64.Build.0 = Release|Any CPU
{DC26CC20-E923-4AA4-93C2-BCBEFC10EF26}.Release|x86.ActiveCfg = Release|Any CPU
{DC26CC20-E923-4AA4-93C2-BCBEFC10EF26}.Release|x86.Build.0 = Release|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Debug|x64.Build.0 = Debug|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Debug|x86.Build.0 = Debug|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Release|x64.ActiveCfg = Release|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Release|x64.Build.0 = Release|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Release|x86.ActiveCfg = Release|Any CPU
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -58,6 +74,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{34B4B30E-C0E0-43E9-88D0-27B31BA01574} = {1402F60B-E306-4A41-B41E-2EF9136DFB36}
{DC26CC20-E923-4AA4-93C2-BCBEFC10EF26} = {80815184-6446-4B0F-B7A4-B190E5E53F70}
+ {41EC6B03-53B4-43FC-AA8B-0E74C143F2FD} = {B29BB850-080B-4A04-AF80-BB1B3FD91BC9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB6A0A9A-4183-491A-97B0-86939AB919E1}
diff --git a/Ellucian.Ethos.Integration/Authentication/SupportedRegions.cs b/Ellucian.Ethos.Integration/Authentication/SupportedRegions.cs
index 11403ac..047da63 100644
--- a/Ellucian.Ethos.Integration/Authentication/SupportedRegions.cs
+++ b/Ellucian.Ethos.Integration/Authentication/SupportedRegions.cs
@@ -27,6 +27,10 @@ public enum SupportedRegions
///
/// Europe.
///
- Europe
- }
+ Europe,
+ ///
+ /// Self-Hosted.
+ ///
+ SelfHosted
+ }
}
\ No newline at end of file
diff --git a/Ellucian.Ethos.Integration/Client/EthosClient.cs b/Ellucian.Ethos.Integration/Client/EthosClient.cs
index f2b0f8d..8b49901 100644
--- a/Ellucian.Ethos.Integration/Client/EthosClient.cs
+++ b/Ellucian.Ethos.Integration/Client/EthosClient.cs
@@ -28,6 +28,24 @@ public class EthosClient
///
private string ApiKey { get; }
+ // protected EthosEthosIntegrationUrls EthosIntegrationUrls = new EthosEthosIntegrationUrls();
+
+ // Only used by ColleagueWebAPIProxyClients
+ ///
+ /// Api URL to Self-Hosted Colleague API.
+ ///
+ protected string ColleagueApiUrl;
+ ///
+ /// Self-Hosted Colleague API username.
+ ///
+ protected string ColleagueApiUsername;
+ ///
+ /// Self-Hosted Colleague API password.
+ ///
+ protected string ColleagueApiPassword;
+
+
+
///
/// Default token expiration time.
///
@@ -102,6 +120,31 @@ public EthosClient( string apiKey, HttpClient client )
this.HttpProtocolClientBuilder ??= new HttpProtocolClientBuilder( client );
EthosResponseBuilder ??= new EthosResponseBuilder();
}
+ ///
+ /// Constructor called by subclasses of this class, specifically for ColleagueWebApiProxies.
+ ///
+ /// The URL to the Colleague API instance. If it is null/empty, then an will be thrown.
+ /// The username used to connect to the Colleague API. If it is null/empty, then an will be thrown.
+ /// The password used to connect to the Colleague API. If it is null/empty, then an will be thrown.
+ /// A .
+ public EthosClient(string colleagueApiUrl, string colleagueApiUsername, string colleagueApiPassword, HttpClient client)
+ {
+ if (colleagueApiUrl == null || colleagueApiUsername == null
+ || colleagueApiPassword == null || colleagueApiPassword == null)
+ {
+ throw new ArgumentNullException($"Colleague API URL and Credentials are required.");
+ }
+ if (client == null)
+ {
+ throw new ArgumentNullException($"The '{nameof(client)}' parameter is required.");
+ }
+ ApiKey = null;
+ this.ColleagueApiUrl = colleagueApiUrl;
+ this.ColleagueApiUsername = colleagueApiUsername;
+ this.ColleagueApiPassword = colleagueApiPassword;
+ this.HttpProtocolClientBuilder ??= new HttpProtocolClientBuilder(client);
+ EthosResponseBuilder ??= new EthosResponseBuilder();
+ }
#endregion
@@ -175,7 +218,7 @@ public async Task GetAccessTokenAsync()
/// Returns exception if the request fails.
private async Task GetNewTokenAsync()
{
- string authUrl = $"{ EthosIntegrationUrls.Auth( this.Region )}?expirationMinutes={ ExpirationMinutes }";
+ string authUrl = $"{EthosIntegrationUrls.Auth( this.Region )}?expirationMinutes={ ExpirationMinutes }";
HttpProtocolClientBuilder.Client.DefaultRequestHeaders.Add( "Authorization", $"Bearer { ApiKey }" );
//make request
HttpResponseMessage response = await HttpProtocolClientBuilder.Client.PostAsync( new Uri( authUrl ), null );
@@ -369,11 +412,20 @@ public async Task DeleteAsync( Dictionary headers, string url )
/// A
private async Task AddAccessTokenAuthHeaderAsync( Dictionary headers )
{
- AccessToken token = await GetAccessTokenAsync();
- if ( token.GetAuthHeader().TryGetValue( "Authorization", out string authValue ) )
+ headers.Remove("Authorization");
+ if (Region == SupportedRegions.SelfHosted)
+ {
+ byte[] bytes = System.Text.Encoding.UTF8.GetBytes(ColleagueApiUsername + ":" + ColleagueApiPassword);
+ string token = System.Convert.ToBase64String(bytes);
+ headers.Add("Authorization", "Basic " + token);
+ }
+ else
{
- headers.Remove( "Authorization" );
- headers.Add( "Authorization", authValue );
+ AccessToken token = await GetAccessTokenAsync();
+ if (token.GetAuthHeader().TryGetValue("Authorization", out string authValue))
+ {
+ headers.Add("Authorization", authValue);
+ }
}
}
diff --git a/Ellucian.Ethos.Integration/Client/EthosClientBuilder.cs b/Ellucian.Ethos.Integration/Client/EthosClientBuilder.cs
index 401c493..587abc2 100644
--- a/Ellucian.Ethos.Integration/Client/EthosClientBuilder.cs
+++ b/Ellucian.Ethos.Integration/Client/EthosClientBuilder.cs
@@ -28,6 +28,10 @@ public class EthosClientBuilder
///
private int? ConnectionTimeout = null;
+ private readonly string ColleagueApiUrl;
+ private readonly string ColleagueApiUsername;
+ private readonly string ColleagueApiPassword;
+
///
/// Interface used in HttpProtocolClientBuilder.
///
@@ -42,6 +46,19 @@ public EthosClientBuilder( string apiKey )
this.apiKey = apiKey;
builder ??= new HttpProtocolClientBuilder( null, ConnectionTimeout );
}
+ ///
+ /// Constructs this class with the given Colleauge API URL/Credentials.
+ ///
+ /// The URL to the Colleague API instance.
+ /// The username used to connect to the Colleague API.
+ /// The password used to connect to the Colleague API.
+ public EthosClientBuilder(string colleagueApiUrl, string colleagueApiUsername, string colleagueApiPassword)
+ {
+ builder ??= new HttpProtocolClientBuilder(null, ConnectionTimeout);
+ ColleagueApiUrl = colleagueApiUrl;
+ ColleagueApiUsername = colleagueApiUsername;
+ ColleagueApiPassword = colleagueApiPassword;
+ }
///
/// Give the client factory a connection timeout so that connections will time out after connectionTimeout
@@ -97,7 +114,25 @@ public EthosMessagesClient BuildEthosMessagesClient()
/// An EthosFilterQueryClient using the given apiKey and timeout values.
public EthosFilterQueryClient BuildEthosFilterQueryClient()
{
- return new EthosFilterQueryClient( apiKey, builder.Client );
+ return new EthosFilterQueryClient(apiKey, builder.Client);
+ }
+
+ ///
+ /// Gets an that will use the given Colleague credentials to authenticate.
+ ///
+ /// An ColleagueWebApiProxyClient using the given Colleague credentials.
+ public ColleagueWebApiProxyClient BuildColleagueWebApiProxyclient()
+ {
+ return new ColleagueWebApiProxyClient(ColleagueApiUrl, ColleagueApiUsername, ColleagueApiPassword, builder.Client);
+ }
+
+ ///
+ /// Gets an that will use the Colleague credentials to authenticate.
+ ///
+ /// An ColleagueWebApiFilterQueryClient using the given Colleague credentials.
+ public ColleagueWebApiFilterQueryClient BuildColleagueWebApiFilterQueryClient()
+ {
+ return new ColleagueWebApiFilterQueryClient(ColleagueApiUrl, ColleagueApiUsername, ColleagueApiPassword, builder.Client);
}
}
}
diff --git a/Ellucian.Ethos.Integration/Client/HttpProtocolClientBuilder.cs b/Ellucian.Ethos.Integration/Client/HttpProtocolClientBuilder.cs
index 1c138a8..01634df 100644
--- a/Ellucian.Ethos.Integration/Client/HttpProtocolClientBuilder.cs
+++ b/Ellucian.Ethos.Integration/Client/HttpProtocolClientBuilder.cs
@@ -24,9 +24,9 @@ public class HttpProtocolClientBuilder : IHttpProtocolClientBuilder
/// Time in seconds to allow an http connection to time out. Default is
/// 300 seconds (5 minutes).
///
- private static int CONNECTION_TIMEOUT = 300;
+ private static readonly int CONNECTION_TIMEOUT = 300;
- private const SslProtocols PROTOCOL = SslProtocols.Tls13 | SslProtocols.Tls12 | SslProtocols.Tls11;
+ private const SslProtocols PROTOCOL = SslProtocols.Tls13 | SslProtocols.Tls12;
private const string CLIENT_NAME = "EllucianEthosIntegrationSdk-dotnet";
@@ -44,10 +44,7 @@ public class HttpProtocolClientBuilder : IHttpProtocolClientBuilder
/// 300 seconds (5 minutes).
public HttpProtocolClientBuilder( HttpClient client, int? connectionTimeOut = null )
{
- if ( client == null )
- {
- client = BuildHttpClient( connectionTimeOut.HasValue ? connectionTimeOut : CONNECTION_TIMEOUT );
- }
+ client ??= BuildHttpClient( connectionTimeOut.HasValue ? connectionTimeOut : CONNECTION_TIMEOUT );
Client = client;
}
@@ -66,7 +63,7 @@ public HttpClient BuildHttpClient( int? connectionTimeout )
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add( "pragma", "no-cache" );
client.DefaultRequestHeaders.Add( "cache-control", "no-cache" );
- ProductInfoHeaderValue prodHeaderVal = new ProductInfoHeaderValue( CLIENT_NAME, Assembly.GetExecutingAssembly().GetName()?.Version?.ToString() );
+ ProductInfoHeaderValue prodHeaderVal = new( CLIENT_NAME, Assembly.GetExecutingAssembly().GetName()?.Version?.ToString() );
client.DefaultRequestHeaders.UserAgent.Add( prodHeaderVal );
client.Timeout = TimeSpan.FromMinutes( ( double ) connectionTimeout );
} )
diff --git a/Ellucian.Ethos.Integration/Client/Messages/EthosMessagesClient.cs b/Ellucian.Ethos.Integration/Client/Messages/EthosMessagesClient.cs
index 07b3a34..08458bd 100644
--- a/Ellucian.Ethos.Integration/Client/Messages/EthosMessagesClient.cs
+++ b/Ellucian.Ethos.Integration/Client/Messages/EthosMessagesClient.cs
@@ -113,7 +113,7 @@ private async Task> GetMessagesAsync( int? limit
/// The number of available messages in the application's queue.
public async Task GetNumAvailableMessagesAsync()
{
- EthosResponse response = await HeadAsync( EthosIntegrationUrls.Consume( Region, -1, -1 ) );
+ EthosResponse response = await HeadAsync(EthosIntegrationUrls.Consume( Region, -1, -1 ) );
string remaining = response.GetHeader( "x-remaining" );
if ( int.TryParse( remaining, out int numMessages ) )
{
diff --git a/Ellucian.Ethos.Integration/Client/Proxy/ColleagueWebApiFilterQueryClient.cs b/Ellucian.Ethos.Integration/Client/Proxy/ColleagueWebApiFilterQueryClient.cs
new file mode 100644
index 0000000..23dff29
--- /dev/null
+++ b/Ellucian.Ethos.Integration/Client/Proxy/ColleagueWebApiFilterQueryClient.cs
@@ -0,0 +1,40 @@
+/*
+ * ******************************************************************************
+ * Copyright 2022 Ellucian Company L.P. and its affiliates.
+ * ******************************************************************************
+ */
+
+using Ellucian.Ethos.Integration.Client.Filter.Extensions;
+using Ellucian.Ethos.Integration.Client.Proxy.Filter;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ellucian.Ethos.Integration.Client.Proxy
+{
+ ///
+ /// An EthosProxyClient that provides the ability to submit GET requests supporting filters and/or named queries with support for paging.
+ ///
+ public class ColleagueWebApiFilterQueryClient : EthosFilterQueryClient
+ {
+ ///
+ /// Instantiates this class using the given Colleague API url and credentials.
+ ///
+ /// The URL to the Colleague API instance. If it is null/empty, then an will be thrown.
+ /// The username used to connect to the Colleague API. If it is null/empty, then an will be thrown.
+ /// The password used to connect to the Colleague API. If it is null/empty, then an will be thrown.
+ /// A HttpClient. If it is null/empty, then an will be thrown.
+ public ColleagueWebApiFilterQueryClient(string colleagueApiUrl, string colleagueApiUsername, string colleagueApiPassword, HttpClient client)
+ : base(colleagueApiUrl, colleagueApiUsername, colleagueApiPassword, client)
+ {
+ Region = Authentication.SupportedRegions.SelfHosted;
+ EthosIntegrationUrls.SelfHostBaseUrl = colleagueApiUrl;
+ }
+ }
+}
diff --git a/Ellucian.Ethos.Integration/Client/Proxy/ColleagueWebApiProxyClient.cs b/Ellucian.Ethos.Integration/Client/Proxy/ColleagueWebApiProxyClient.cs
new file mode 100644
index 0000000..00f4557
--- /dev/null
+++ b/Ellucian.Ethos.Integration/Client/Proxy/ColleagueWebApiProxyClient.cs
@@ -0,0 +1,35 @@
+using Ellucian.Ethos.Integration.Client.Filter.Extensions;
+using Ellucian.Ethos.Integration.Client.Proxy.Filter;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ellucian.Ethos.Integration.Client.Proxy
+{
+ ///
+ /// An EthosProxyClient that provides the ability to submit GET requests supporting filters and/or named queries with support for paging.
+ ///
+ public class ColleagueWebApiProxyClient : EthosProxyClient
+ {
+ ///
+ /// Instantiates this class using the given Colleague API url and credentials.
+ ///
+ /// The URL to the Colleague API instance. If it is null/empty, then an will be thrown.
+ /// The username used to connect to the Colleague API. If it is null/empty, then an will be thrown.
+ /// The password used to connect to the Colleague API. If it is null/empty, then an will be thrown.
+ /// A HttpClient. If it is null/empty, then an will be thrown.
+ public ColleagueWebApiProxyClient( string colleagueApiUrl, string colleagueApiUsername, string colleagueApiPassword, HttpClient client )
+ : base(colleagueApiUrl, colleagueApiUsername, colleagueApiPassword, client)
+ {
+ Region = Authentication.SupportedRegions.SelfHosted;
+ EthosIntegrationUrls.SelfHostBaseUrl = colleagueApiUrl;
+ }
+
+ }
+}
diff --git a/Ellucian.Ethos.Integration/Client/Proxy/EthosFilterQueryClient.cs b/Ellucian.Ethos.Integration/Client/Proxy/EthosFilterQueryClient.cs
index d28d933..91b2180 100644
--- a/Ellucian.Ethos.Integration/Client/Proxy/EthosFilterQueryClient.cs
+++ b/Ellucian.Ethos.Integration/Client/Proxy/EthosFilterQueryClient.cs
@@ -36,6 +36,18 @@ public class EthosFilterQueryClient : EthosProxyClient
public EthosFilterQueryClient( string apiKey, HttpClient client ) : base( apiKey, client )
{
+ }
+ ///
+ /// Instantiates this class using the given Colleague URL and credentials and HttpClient.
+ ///
+ /// The URL to the Colleague API instance. If it is null/empty, then an will be thrown.
+ /// The username used to connect to the Colleague API. If it is null/empty, then an will be thrown.
+ /// The password used to connect to the Colleague API. If it is null/empty, then an will be thrown.
+ /// A HttpClient. If it is null/empty, then an will be thrown.
+ public EthosFilterQueryClient(string colleagueApiUrl, string colleagueApiUsername, string colleagueApiPassword, HttpClient client)
+ : base(colleagueApiUrl, colleagueApiUsername, colleagueApiPassword, client)
+ {
+
}
#region Strongly Typed GET
diff --git a/Ellucian.Ethos.Integration/Client/Proxy/EthosProxyClient.cs b/Ellucian.Ethos.Integration/Client/Proxy/EthosProxyClient.cs
index 5025adb..53dbbb3 100644
--- a/Ellucian.Ethos.Integration/Client/Proxy/EthosProxyClient.cs
+++ b/Ellucian.Ethos.Integration/Client/Proxy/EthosProxyClient.cs
@@ -123,6 +123,18 @@ public class EthosProxyClient : EthosClient
public EthosProxyClient( string apiKey, HttpClient client ) : base( apiKey, client )
{
+ }
+ ///
+ /// Constructs an EthosProxyClient using the given Colleague API URL and credentials.
+ ///
+ /// The URL to the Colleague API instance. If it is null/empty, then an will be thrown.
+ /// The username used to connect to the Colleague API. If it is null/empty, then an will be thrown.
+ /// The password used to connect to the Colleague API. If it is null/empty, then an will be thrown.
+ /// A .
+ public EthosProxyClient(string colleagueApiUrl, string colleagueApiUsername, string colleagueApiPassword, HttpClient client)
+ : base(colleagueApiUrl, colleagueApiUsername, colleagueApiPassword, client)
+ {
+
}
#region POST
@@ -553,7 +565,7 @@ internal IEnumerable ConvertEthosResponseContentListToType( IE
var version = DEFAULT_VERSION;
Dictionary headers = BuildHeadersMap( version );
- string url = $"{ EthosIntegrationUrls.Api( Region, resourceName ) }";
+ string url = $"{EthosIntegrationUrls.Api( Region, resourceName ) }";
EthosResponse response = await GetAsync( headers, url );
return response;
}
@@ -570,7 +582,7 @@ public async Task GetAsync( string resourceName, string version =
if ( string.IsNullOrWhiteSpace( resourceName ) ) { throw new ArgumentNullException( nameof( resourceName ) ); }
Dictionary headers = BuildHeadersMap( version );
- string url = $"{ EthosIntegrationUrls.Api( Region, resourceName ) }";
+ string url = $"{EthosIntegrationUrls.Api( Region, resourceName ) }";
EthosResponse response = await GetAsync( headers, url );
return response;
}
@@ -618,7 +630,7 @@ public async Task GetAsync( string resourceName, string version =
if ( string.IsNullOrWhiteSpace( resourceName ) ) { throw new ArgumentNullException( nameof( resourceName ) ); }
Dictionary headers = BuildHeadersMap( version );
- string url = $"{ EthosIntegrationUrls.ApiPaging( Region, resourceName, offset, pageSize ) }";
+ string url = $"{EthosIntegrationUrls.ApiPaging( Region, resourceName, offset, pageSize ) }";
EthosResponse response = await GetAsync( headers, url );
return response;
}
diff --git a/Ellucian.Ethos.Integration/Ellucian.Ethos.Integration.csproj b/Ellucian.Ethos.Integration/Ellucian.Ethos.Integration.csproj
index 59d7ef3..0d21884 100644
--- a/Ellucian.Ethos.Integration/Ellucian.Ethos.Integration.csproj
+++ b/Ellucian.Ethos.Integration/Ellucian.Ethos.Integration.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
Ellucian.Ethos.Integration
1.0.0
Ellucian Ethos Integration Team
@@ -31,6 +31,7 @@ The Ethos Integration SDK makes the application development process less expensi
README.md
True
https://github.com/ellucian-developer/integration-sdk-csharp
+ true
@@ -58,9 +59,9 @@ The Ethos Integration SDK makes the application development process less expensi
-
-
-
+
+
+
diff --git a/Ellucian.Ethos.Integration/EthosIntegrationUrls.cs b/Ellucian.Ethos.Integration/EthosIntegrationUrls.cs
index a1206a8..78bf4f2 100644
--- a/Ellucian.Ethos.Integration/EthosIntegrationUrls.cs
+++ b/Ellucian.Ethos.Integration/EthosIntegrationUrls.cs
@@ -33,6 +33,9 @@ public static class EthosIntegrationUrls
/// -
/// AUSTRALIA: .com.au
///
+ /// -
+ /// SELF-HOSTED
+ ///
///
///
private static readonly Dictionary RegionUrlPostFix = new Dictionary
@@ -40,11 +43,29 @@ public static class EthosIntegrationUrls
[ SupportedRegions.US ] = ".com",
[ SupportedRegions.Canada ] = ".ca",
[ SupportedRegions.Europe ] = ".ie",
- [ SupportedRegions.Australia ] = ".com.au"
+ [ SupportedRegions.Australia ] = ".com.au",
+ [ SupportedRegions.SelfHosted ] = ""
};
+
+#pragma warning disable S1075
+ const string MAIN_ETHOS_BASE_URL = "https://integrate.elluciancloud";
+#pragma warning restore S1075
+
+ /// The override for self-hosted ERP clients.
+ public static string SelfHostBaseUrl { get; set; } = "";
+
///The main domain for Ethos Integration.
- private const string MAIN_BASE_URL = "https://integrate.elluciancloud";
+ public static string MAIN_BASE_URL
+ {
+ get
+ {
+ if (string.IsNullOrWhiteSpace(SelfHostBaseUrl))
+ return MAIN_ETHOS_BASE_URL;
+
+ return SelfHostBaseUrl;
+ }
+ }
///
/// The base URL for getting Api result(s) in Ethos Integration.
@@ -55,13 +76,13 @@ public static class EthosIntegrationUrls
/// A string value containing the URL to use for interacting with Ethos Integration Proxy APIs.
public static string Api( SupportedRegions region, string resource, string id = "" )
{
- string url = BuildUrl( region, "/api" );
- if ( !string.IsNullOrWhiteSpace( resource ) )
+ string url = BuildUrl(region, "/api");
+ if (!string.IsNullOrWhiteSpace(resource))
{
- url += ( "/" + resource );
- if ( !string.IsNullOrWhiteSpace( id ) )
+ url += ("/" + resource);
+ if (!string.IsNullOrWhiteSpace(id))
{
- url += ( "/" + id );
+ url += ("/" + id);
}
}
return url;
@@ -245,12 +266,14 @@ public static string BaseUrl( SupportedRegions region )
/// Builds the URL with the mainBaseUrl, the supported region, and the correct path.
///
/// The appropriate supported region to build the URL with.
- /// The correct path for the type of API the URL will be used with (/api for Proxy API URL,
- /// for Token API URL, etc.).
+ /// The correct path for the type of API the URL will be used with (/api for Proxy API URL,
+ /// for Token API URL, etc.).
///
- private static string BuildUrl( SupportedRegions region, string urlEnd )
+ private static string BuildUrl( SupportedRegions region, string urlEnd)
{
- return $"{MAIN_BASE_URL}{RegionUrlPostFix [ region ]}{urlEnd}";
+ return region == SupportedRegions.SelfHosted
+ ? $"{MAIN_BASE_URL}"
+ : $"{MAIN_BASE_URL}{RegionUrlPostFix[region]}{urlEnd}";
}
///
diff --git a/Ellucian.Ethos.Integration/Service/EthosChangeNotificationService.cs b/Ellucian.Ethos.Integration/Service/EthosChangeNotificationService.cs
index c0187bc..2fb9431 100644
--- a/Ellucian.Ethos.Integration/Service/EthosChangeNotificationService.cs
+++ b/Ellucian.Ethos.Integration/Service/EthosChangeNotificationService.cs
@@ -35,7 +35,20 @@ public class EthosChangeNotificationService : EthosService
/// This constructor is only called from the inner Builder class.
///
/// A api key.
- private EthosChangeNotificationService( string apiKey ) : this( new EthosClientBuilder( apiKey ) )
+ private EthosChangeNotificationService(string apiKey)
+ : this( new EthosClientBuilder(apiKey))
+ {
+
+ }
+
+ ///
+ /// Instantiates this service class with Colleague API and credentials.
+ ///
+ /// The URL to the Colleague API instance.
+ /// The username used to connect to the Colleague API.
+ /// The password used to connect to the Colleague API.
+ private EthosChangeNotificationService(string colleagueApiUrl, string colleagueApiUsername, string colleagueApiPassword)
+ : this( new EthosClientBuilder(colleagueApiUrl, colleagueApiUsername, colleagueApiPassword))
{
}
@@ -333,12 +346,25 @@ private void BuildService( EthosClientBuilder ethosClientBuilder )
/// Actions delegate.
/// A api key.
/// An instance of the EthosChangeNotificationService.
- public static EthosChangeNotificationService Build( Action action, string apiKey )
+ public static EthosChangeNotificationService Build( Action action, string apiKey)
{
- EthosChangeNotificationService ethosChangeNotificationService = new EthosChangeNotificationService( apiKey );
+ EthosChangeNotificationService ethosChangeNotificationService = new EthosChangeNotificationService(apiKey);
action( ethosChangeNotificationService );
return ethosChangeNotificationService;
}
+ ///
+ /// Builds an instance of the EthosChangeNotificationService with the given ethosClientBuilder and any resource version overrides.
+ ///
+ /// Actions delegate.
+ /// The URL to the Colleague API instance.
+ /// The username used to connect to the Colleague API.
+ /// The password used to connect to the Colleague API.
+ public static EthosChangeNotificationService Build(Action action, string colleagueApiUrl, string colleagueApiUsername, string colleagueApiPassword)
+ {
+ EthosChangeNotificationService ethosChangeNotificationService = new EthosChangeNotificationService(colleagueApiUrl, colleagueApiUsername, colleagueApiPassword);
+ action(ethosChangeNotificationService);
+ return ethosChangeNotificationService;
+ }
///
/// Adds resource name and version to a dictionary. If the key (resourceName) already exists with version value, then it replaces same resource with the version provided.
diff --git a/Ellucian.Ethos.Integration/Service/EthosService.cs b/Ellucian.Ethos.Integration/Service/EthosService.cs
index 414b277..d412fb0 100644
--- a/Ellucian.Ethos.Integration/Service/EthosService.cs
+++ b/Ellucian.Ethos.Integration/Service/EthosService.cs
@@ -26,9 +26,20 @@ protected EthosService()
/// Constructs this service with the given API key.
///
/// apiKey The API key used by the EthosClients of this service when obtaining an access token per request.
- protected EthosService( string apiKey )
+ protected EthosService(string apiKey)
{
- EthosClientBuilder = new EthosClientBuilder( apiKey );
+ EthosClientBuilder = new EthosClientBuilder(apiKey);
+ }
+
+ ///
+ /// Constructs this service with the given Colleague API and credentials.
+ ///
+ /// The URL to the Colleague API instance.
+ /// The username used to connect to the Colleague API.
+ /// The password used to connect to the Colleague API.
+ protected EthosService(string colleagueApiUrl, string colleagueApiUsername, string colleagueApiPassword)
+ {
+ EthosClientBuilder = new EthosClientBuilder(colleagueApiUrl, colleagueApiUsername, colleagueApiPassword);
}
///
diff --git a/Ellucian.Ethos.Integration/packages.lock.json b/Ellucian.Ethos.Integration/packages.lock.json
new file mode 100644
index 0000000..e772b8d
--- /dev/null
+++ b/Ellucian.Ethos.Integration/packages.lock.json
@@ -0,0 +1,135 @@
+{
+ "version": 1,
+ "dependencies": {
+ "net8.0": {
+ "Microsoft.Extensions.DependencyInjection": {
+ "type": "Direct",
+ "requested": "[8.0.0, )",
+ "resolved": "8.0.0",
+ "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.Http": {
+ "type": "Direct",
+ "requested": "[8.0.0, )",
+ "resolved": "8.0.0",
+ "contentHash": "cWz4caHwvx0emoYe7NkHPxII/KkTI8R/LC9qdqJqnKv2poTJ4e2qqPGQqvRoQ5kaSA4FU5IV3qFAuLuOhoqULQ==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
+ "Microsoft.Extensions.Diagnostics": "8.0.0",
+ "Microsoft.Extensions.Logging": "8.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "8.0.0",
+ "Microsoft.Extensions.Options": "8.0.0"
+ }
+ },
+ "Newtonsoft.Json": {
+ "type": "Direct",
+ "requested": "[13.0.3, )",
+ "resolved": "13.0.3",
+ "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
+ },
+ "Microsoft.Extensions.Configuration": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+ "Microsoft.Extensions.Primitives": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.Configuration.Abstractions": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.Configuration.Binder": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.DependencyInjection.Abstractions": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg=="
+ },
+ "Microsoft.Extensions.Diagnostics": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "3PZp/YSkIXrF7QK7PfC1bkyRYwqOHpWFad8Qx+4wkuumAeXo1NHaxpS9LboNA9OvNSAu+QOVlXbMyoY+pHSqcw==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "8.0.0",
+ "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0",
+ "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.Diagnostics.Abstractions": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
+ "Microsoft.Extensions.Options": "8.0.0",
+ "System.Diagnostics.DiagnosticSource": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.Logging": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection": "8.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "8.0.0",
+ "Microsoft.Extensions.Options": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.Logging.Abstractions": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.Options": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
+ "Microsoft.Extensions.Primitives": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.Options.ConfigurationExtensions": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+ "Microsoft.Extensions.Configuration.Binder": "8.0.0",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
+ "Microsoft.Extensions.Options": "8.0.0",
+ "Microsoft.Extensions.Primitives": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.Primitives": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
+ },
+ "System.Diagnostics.DiagnosticSource": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ=="
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index e0ddd36..33243f0 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,16 @@
# Ellucian Ethos Integration SDK
+> Modifications by Intuition Payment Solutions
+
+
+
+[](https://github.com/iNtuitionPS/ethos-integration-sdk-fork/actions/workflows/publish-nupkg.yml)
+
+| Branch | Build Pipeline Status |
+|:---|:---|
+| `main` |
|
+| `developmment` | N/A |
+
+#
Ethos Integration SDK provides utilities and libraries that make it easier for developers to quickly start building Ethos-based integrations.
diff --git a/_azure-pipelines-CI.yml b/_azure-pipelines-CI.yml
new file mode 100644
index 0000000..2d1efdd
--- /dev/null
+++ b/_azure-pipelines-CI.yml
@@ -0,0 +1,180 @@
+# Build Pipeline - Continuous Integration
+# if commit is not from a pull request, then the pipeline will abort - bypass this check by setting the SkipPullRequestCheck variable at queue time
+
+trigger:
+- main
+
+pr: none
+
+pool:
+ vmImage: 'windows-latest'
+
+variables:
+ solution: '**/*.sln'
+ buildPlatform: 'Any CPU'
+ NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages
+
+steps:
+- checkout: self
+ persistCredentials: true
+
+- task: PowerShell@2
+ displayName: Get Pull Request
+ condition: and(succeeded(), eq(variables['SkipPullRequestCheck'], ''))
+ inputs:
+ targetType: 'inline'
+ script: |
+ try {
+ $headers = @{ "Authorization" = "Bearer $(GitHubToken)"}
+ $uri = 'https://api.github.com/repos/$(Build.Repository.Name)/commits/$(Build.SourceVersion)/pulls'
+ Write-Output "uri = $uri"
+ $response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Get
+ $content = $response.content | ConvertFrom-Json
+ # Check if the $content is not empty and iterate through each pull request
+ if (-not ($content -and $content.Count -gt 0)) {
+ Write-Host "##vso[task.logissue type=error;]Failed: Commit is not from a pull request."
+ exit 1
+ }
+ $matchedPullRequest = $null
+ foreach ($pullRequest in $content) {
+ if ($pullRequest.merge_commit_sha -eq "$(Build.SourceVersion)") {
+ $matchedPullRequest = $pullRequest
+ break
+ }
+ }
+ # Check if a matching pull request was found
+ if (-not $matchedPullRequest) {
+ Write-Host "##vso[task.logissue type=error;]Failed: No pull request found with merge commit SHA = '$(Build.SourceVersion)'."
+ exit 1
+ }
+ # If a matching pull request is found, set the variables and continue
+ Write-Host "##vso[task.setvariable variable=pullRequestHeadRef;]$($matchedPullRequest.head.ref)"
+ Write-Host "##vso[task.setvariable variable=pullRequestBaseRef;]$($matchedPullRequest.base.ref)"
+ } catch {
+ Write-Host "##vso[task.logissue type=error;]Error getting pull request - | $_"
+ exit 1
+ }
+
+- task: PowerShell@2
+ displayName: Reset Development Branch
+ condition: and(succeeded(), eq(variables['SkipPullRequestCheck'], ''))
+ inputs:
+ targetType: 'inline'
+ script: |
+ if ("$(pullRequestHeadRef)" -eq "development" -and "$(pullRequestBaseRef)" -eq "main") {
+ # Define required headers for Azure DevOps | url is pulled from 'common' variable group in library
+ $azureDevOpsAuthenicationHeader = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$(AzureDevOpsToken)")); "Content-Type" = "application/json"}
+ # Define the base URL for Azure DevOps API
+ $azureDevOpsRequestUri = "$(AzureDevOpsBuildApiUrl)/_apis/build/definitions/$(System.DefinitionId)?api-version=7.0"
+ try {
+ $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "", "$(GitHubToken)")))
+ # Get the current pipeline definition
+ $current_definition = Invoke-RestMethod -Method Get -Uri $azureDevOpsRequestUri -Headers $azureDevOpsAuthenicationHeader
+ # Disable build pipeline
+ $current_definition.queueStatus = "disabled"
+ $disabled_definition = $current_definition | ConvertTo-Json -Depth 100
+ Invoke-RestMethod -Method Put -Uri $azureDevOpsRequestUri -Headers $azureDevOpsAuthenicationHeader -Body $disabled_definition
+ # Perform the reset
+ git config user.email "no-reply@intuitionps.com"
+ git config user.name "intuitionps Azure DevOps"
+ git checkout development
+ if ($LASTEXITCODE -ne 0) { throw "git checkout development" }
+ git fetch origin
+ if ($LASTEXITCODE -ne 0) { throw "git fetch origin" }
+ git reset --hard origin/main
+ if ($LASTEXITCODE -ne 0) { throw "git reset --hard origin/main" }
+ git push --force origin development
+ if ($LASTEXITCODE -ne 0) { throw "git push --force origin development" }
+ Write-Host "Successfully reset development branch from main branch."
+ } catch {
+ Write-Host "##vso[task.logissue type=error;]Error resetting development branch with main branch - | $_"
+ } finally {
+ try {
+ # Get the current pipeline definition
+ $current_definition = Invoke-RestMethod -Method Get -Uri $azureDevOpsRequestUri -Headers $azureDevOpsAuthenicationHeader
+ # Enable build pipeline
+ $current_definition.queueStatus = "enabled"
+ $enabled_definition = $current_definition | ConvertTo-Json -Depth 100
+ Invoke-RestMethod -Method Put -Uri $azureDevOpsRequestUri -Headers $azureDevOpsAuthenicationHeader -Body $enabled_definition
+ } catch {
+ Write-Host "##vso[task.logissue type=error;]Error re-enabling build pipeline - | $_"
+ }
+ }
+ } else {
+ Write-Host "##vso[task.logissue type=warning;]Aborted: Only reset if base branch is 'main' and head branch is 'development'."
+ }
+ ignoreLASTEXITCODE: true
+
+- task: NuGetToolInstaller@1
+ displayName: Install NuGet Tool
+
+- task: Cache@2
+ displayName: Cache NuGet packages
+ inputs:
+ key: 'nuget | "$(Agent.OS)" | **/packages.lock.json,!**/bin/**,!**/obj/**'
+ restoreKeys: |
+ nuget | "$(Agent.OS)"
+ nuget
+ path: '$(NUGET_PACKAGES)'
+
+- task: NuGetCommand@2
+ displayName: Restore Solution
+ inputs:
+ restoreSolution: '$(solution)'
+ feedsToUse: 'select'
+ vstsFeed: '01c84548-9607-4655-80e7-6ad95390a38c/3823d26d-e8ca-40c4-a6f3-c3bd13f9658b'
+
+- task: SonarQubePrepare@5
+ displayName: SonarQube - Prepare Analysis Configuration
+ inputs:
+ SonarQube: 'SonarQube'
+ scannerMode: 'MSBuild'
+ projectKey: '$(SonarQubeProjectKey)'
+
+- task: VSBuild@1
+ displayName: Build Solution
+ inputs:
+ solution: '$(solution)'
+ msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip"'
+ platform: '$(buildPlatform)'
+ configuration: '$(buildConfiguration)'
+
+- task: SonarQubeAnalyze@5
+ displayName: SonarQube - Run Code Analysis
+
+- task: SonarQubePublish@5
+ displayName: SonarQube - Publish Quality Gate Result
+ inputs:
+ pollingTimeoutSec: '300'
+
+- task: PowerShell@2
+ displayName: SonarQube - Get Project Status
+ inputs:
+ targetType: 'inline'
+ script: |
+ try {
+ $headers = @{
+ "Content-Type" = "application/json"
+ "Authorization" = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$(SonarQubeToken):"))
+ }
+ $uri = 'https://ca-intuition-devops-sonarqube.azurewebsites.net/api/qualitygates/project_status?projectKey=$(SonarQubeProjectKey)&branch=$(Build.SourceBranchName)'
+ Write-Output "uri = $uri"
+ $response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Get
+ $content = $response.Content | ConvertFrom-Json
+ $status = $content.projectStatus.status
+ Write-Output "SonarQube Project Status = $status"
+ if ($content.projectStatus.status -eq "ERROR") {
+ Write-Host "##vso[task.logissue type=error;]Failed: Code analysis did not pass SonarQube quality gate."
+ exit 1
+ }
+ } catch {
+ Write-Host "##vso[task.logissue type=error;]Error getting project status - | $_"
+ exit 1
+ }
+
+- task: PublishBuildArtifacts@1
+ displayName: Publish Pipeline Artifacts
+ inputs:
+ PathtoPublish: '$(Build.ArtifactStagingDirectory)'
+ ArtifactName: 'drop'
+ publishLocation: 'Container'
diff --git a/_azure-pipelines-PR.yml b/_azure-pipelines-PR.yml
new file mode 100644
index 0000000..d3d6698
--- /dev/null
+++ b/_azure-pipelines-PR.yml
@@ -0,0 +1,94 @@
+# Build Pipeline - Pull Request
+# if target branch is 'main' and source branch is not 'development', then the pipeline will abort
+
+trigger: none
+
+pr:
+- main
+- development
+
+pool:
+ vmImage: 'windows-latest'
+
+variables:
+ solution: '**/*.sln'
+ buildPlatform: 'Any CPU'
+ NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages
+
+steps:
+- task: PowerShell@2
+ displayName: Check Target Branch
+ inputs:
+ targetType: 'inline'
+ script: |
+ if ("$(system.pullRequest.targetBranch)" -eq "main" -and "$(system.pullRequest.sourceBranch)" -ne "development") {
+ Write-Host "##vso[task.logissue type=error;]Failed: Target branch is 'main' and source branch is not 'development'."
+ exit 1
+ }
+
+- task: NuGetToolInstaller@1
+ displayName: Install NuGet Tool
+
+- task: Cache@2
+ displayName: Cache NuGet packages
+ inputs:
+ key: 'nuget | "$(Agent.OS)" | **/packages.lock.json,!**/bin/**,!**/obj/**'
+ restoreKeys: |
+ nuget | "$(Agent.OS)"
+ nuget
+ path: '$(NUGET_PACKAGES)'
+
+- task: NuGetCommand@2
+ displayName: Restore Solution
+ inputs:
+ restoreSolution: '$(solution)'
+ feedsToUse: 'select'
+ vstsFeed: '01c84548-9607-4655-80e7-6ad95390a38c/3823d26d-e8ca-40c4-a6f3-c3bd13f9658b'
+
+- task: SonarQubePrepare@5
+ displayName: Prepare Analysis Configuration
+ inputs:
+ SonarQube: 'SonarQube'
+ scannerMode: 'MSBuild'
+ projectKey: '$(SonarQubeProjectKey)'
+
+- task: VSBuild@1
+ displayName: Build Solution
+ inputs:
+ solution: '$(solution)'
+ msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip"'
+ platform: '$(buildPlatform)'
+ configuration: '$(buildConfiguration)'
+
+- task: SonarQubeAnalyze@5
+ displayName: SonarQube - Run Code Analysis
+
+- task: SonarQubePublish@5
+ displayName: SonarQube - Publish Quality Gate Result
+ inputs:
+ pollingTimeoutSec: '300'
+
+- task: PowerShell@2
+ displayName: SonarQube - Get Project Status
+ inputs:
+ targetType: 'inline'
+ script: |
+ try {
+ $headers = @{
+ "Content-Type" = "application/json"
+ "Authorization" = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$(SonarQubeToken):"))
+ }
+ $uri = 'https://ca-intuition-devops-sonarqube.azurewebsites.net/api/qualitygates/project_status?projectKey=$(SonarQubeProjectKey)&pullRequest=$(system.pullRequest.pullRequestNumber)'
+ Write-Output "uri = $uri"
+ $response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Get
+ $content = $response.Content | ConvertFrom-Json
+ $status = $content.projectStatus.status
+ Write-Output "SonarQube Project Status = $status"
+ if ($content.projectStatus.status -eq "ERROR") {
+ Write-Host "##vso[task.logissue type=error;]Failed: Code analysis did not pass SonarQube quality gate."
+ exit 1
+ }
+ } catch {
+ Write-Host "##vso[task.logissue type=error;]Error getting project status - | $_"
+ exit 1
+ }
\ No newline at end of file