feat: add user-based ticket endpoints
This commit is contained in:
7
AGENTS.md
Normal file
7
AGENTS.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# AGENTS Instructions
|
||||
|
||||
## Pflichtregeln
|
||||
|
||||
1. Bei jeder Dateiänderung oder Neuerstellung sind immer Windows Line Endings (CRLF) zu verwenden.
|
||||
2. Vor jeder Änderung muss `git status` geprüft werden und der Working Tree muss sauber sein.
|
||||
3. Nach Abschluss der Änderungen sind immer ein passender Git Commit und ein Git Push auszuführen.
|
||||
@@ -1,500 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static C4IT.Logging.cLogManager;
|
||||
|
||||
namespace C4IT.FASD.Base
|
||||
{
|
||||
public enum enumFasdInformationClass
|
||||
{
|
||||
Unknown = -1,
|
||||
Main = 0,
|
||||
Computer = 1,
|
||||
User = 2,
|
||||
Ticket = 3
|
||||
}
|
||||
|
||||
public enum enumF4sdSearchResultClass
|
||||
{
|
||||
Unknown = -1,
|
||||
Computer = 0,
|
||||
User = 1,
|
||||
Phone = 2,
|
||||
Ticket = 3
|
||||
}
|
||||
|
||||
public enum enumF4sdSearchResultStatus
|
||||
{
|
||||
Unknown = -1,
|
||||
Inactive = 0,
|
||||
Active = 1
|
||||
}
|
||||
|
||||
public enum enumFasdValueType
|
||||
{
|
||||
Unknown = 0,
|
||||
STRING = 1,
|
||||
GUID = 2,
|
||||
INT = 3,
|
||||
BIGINT = 4,
|
||||
FLOAT = 5,
|
||||
DATETIME = 6,
|
||||
VERSION = 7,
|
||||
MD5 = 8,
|
||||
SID = 9,
|
||||
IPV4 = 10,
|
||||
IPV6 = 11,
|
||||
TEXT = 12,
|
||||
BOOLEAN = 13,
|
||||
AUTOID = 255
|
||||
};
|
||||
|
||||
public enum enumFasdCharCaseType { none = 0, lower = 1, upper = 2 };
|
||||
|
||||
public enum enumFasdConfigurationType
|
||||
{
|
||||
unknown = 0,
|
||||
commonIcons,
|
||||
menuSections,
|
||||
quickActions,
|
||||
copyTemplates,
|
||||
healthCard
|
||||
}
|
||||
|
||||
public class cFasdApiConnectionInfo
|
||||
{
|
||||
public string ServerVersion { get; set; }
|
||||
public string MinCockpitVersion { get; set; }
|
||||
public UInt64 ConfigRevision { get; set; } = 0;
|
||||
}
|
||||
|
||||
public class cAgentApiConfiguration
|
||||
{
|
||||
public string LogonUrl { get; set; }
|
||||
public string ApiUrl { get; set; }
|
||||
public string ClientId { get; set; }
|
||||
public string ClientSecret { get; set; }
|
||||
public int? OrganizationCode { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public static cAgentApiConfiguration Instance { get; set; }
|
||||
}
|
||||
|
||||
public class cFasdApiSearchResultDefault
|
||||
{
|
||||
public enumF4sdSearchResultClass Type { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Guid id { get; set; }
|
||||
public enumF4sdSearchResultStatus Status { get; set; } = enumF4sdSearchResultStatus.Unknown;
|
||||
public Dictionary<string, string> Infos { get; set; } = null;
|
||||
|
||||
static public enumF4sdSearchResultClass getSearchClass(enumFasdInformationClass infoClass)
|
||||
{
|
||||
switch (infoClass)
|
||||
{
|
||||
case enumFasdInformationClass.User:
|
||||
return enumF4sdSearchResultClass.User;
|
||||
case enumFasdInformationClass.Computer:
|
||||
return enumF4sdSearchResultClass.Computer;
|
||||
default:
|
||||
return enumF4sdSearchResultClass.Unknown;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class cF4sdIdentityEntry
|
||||
{
|
||||
public enumFasdInformationClass Class { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public static enumFasdInformationClass GetFromSearchResult(enumF4sdSearchResultClass searchResult)
|
||||
{
|
||||
var infoClass = enumFasdInformationClass.Unknown;
|
||||
switch (searchResult)
|
||||
{
|
||||
case enumF4sdSearchResultClass.Computer:
|
||||
infoClass = enumFasdInformationClass.Computer;
|
||||
break;
|
||||
case enumF4sdSearchResultClass.User:
|
||||
case enumF4sdSearchResultClass.Phone:
|
||||
infoClass = enumFasdInformationClass.User;
|
||||
break;
|
||||
}
|
||||
|
||||
return infoClass;
|
||||
}
|
||||
}
|
||||
|
||||
public class cF4sdApiSearchResultRelation : cFasdApiSearchResultDefault
|
||||
{
|
||||
public DateTime LastUsed { get; set; }
|
||||
public double UsingLevel { get; set; }
|
||||
public List<cF4sdIdentityEntry> Identities { get; set; } = null;
|
||||
}
|
||||
|
||||
public class cF4sdHealthCardRawDataRequest
|
||||
{
|
||||
public List<string> Tables { get; set; }
|
||||
public List<cF4sdIdentityEntry> Identities { get; set; }
|
||||
|
||||
public int MaxAge { get; set; } = 14;
|
||||
|
||||
public DateTime RefTime { get; set; } = DateTime.Now.Date.ToUniversalTime();
|
||||
}
|
||||
|
||||
public class cF4SDHealthCardRawData
|
||||
{
|
||||
public class cHealthCardTableColumn
|
||||
{
|
||||
public string ColumnName { get; set; }
|
||||
public List<object> Values { get; set; } = new List<object>();
|
||||
public bool IsIncomplete { get; set; } = false;
|
||||
}
|
||||
|
||||
public class cHealthCardTable
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int StartingIndex { get; set; } = 0;
|
||||
public bool IsIncomplete { get; set; } = false;
|
||||
public bool IsStatic { get; set; }
|
||||
public DateTime[,] TimeFrames { get; set; }
|
||||
public Dictionary<string, cHealthCardTableColumn> Columns { get; set; } = new Dictionary<string, cHealthCardTableColumn>();
|
||||
}
|
||||
|
||||
public class cHealthCardDetailsTable
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public List<string> Columns { get; set; }
|
||||
public Dictionary<int, List<object[]>> Values { get; set; }
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Dictionary<string, cHealthCardTable> Tables { get; set; } = new Dictionary<string, cHealthCardTable>();
|
||||
|
||||
#region GetObjects
|
||||
|
||||
#region GetInteger
|
||||
|
||||
public static int? GetInteger(object Value, int? Default = null)
|
||||
{
|
||||
int? output = Default;
|
||||
|
||||
if (Value == null)
|
||||
return output;
|
||||
|
||||
try
|
||||
{
|
||||
var valuetype = Value.GetType();
|
||||
|
||||
if (Value is int @int)
|
||||
output = @int;
|
||||
if (Value is Int64 @int64)
|
||||
output = Convert.ToInt32(@int64);
|
||||
else if (Value is double @double)
|
||||
output = Convert.ToInt32(@double);
|
||||
else if (Value is string @string)
|
||||
{
|
||||
if (int.TryParse(@string, out int parsedInt))
|
||||
output = parsedInt;
|
||||
else
|
||||
output = Default;
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public int? GetIntegerByAddressInfo(string TableName, string TableColumn, int index = 0, int? Default = null)
|
||||
{
|
||||
int? output = Default;
|
||||
|
||||
try
|
||||
{
|
||||
if (Tables.TryGetValue(TableName, out var selectedTable))
|
||||
if (selectedTable.Columns.TryGetValue(TableColumn, out var selectedColumn))
|
||||
output = GetInteger(selectedColumn.Values[index], Default);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetString
|
||||
|
||||
public static string GetString(object Value, string Default = null)
|
||||
{
|
||||
string output = Default;
|
||||
try
|
||||
{
|
||||
if (Value is int @int)
|
||||
output = Convert.ToString(@int);
|
||||
else if (Value is double @double)
|
||||
output = Convert.ToString(@double);
|
||||
else if (Value is string @string)
|
||||
output = @string;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return output?.ToString();
|
||||
}
|
||||
|
||||
public string GetStringByAddressInfo(string TableName, string TableColumn, int index = 0, string Default = null)
|
||||
{
|
||||
string output = Default;
|
||||
|
||||
try
|
||||
{
|
||||
if (Tables.TryGetValue(TableName, out var selectedTable))
|
||||
if (selectedTable.Columns.TryGetValue(TableColumn, out var selectedColumn))
|
||||
output = GetString(selectedColumn.Values[index], Default);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetVersion
|
||||
|
||||
public static Version GetVersion(object Value, Version Default = null)
|
||||
{
|
||||
Version output = Default;
|
||||
|
||||
if (Value == null)
|
||||
return output;
|
||||
|
||||
try
|
||||
{
|
||||
if (Value is string @string)
|
||||
{
|
||||
if (Version.TryParse(@string, out var tempVersionFromString))
|
||||
output = tempVersionFromString;
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public Version GetVersionByAddressInfo(string TableName, string TableColumn, int index = 0, Version Default = null)
|
||||
{
|
||||
Version output = Default;
|
||||
|
||||
try
|
||||
{
|
||||
if (Tables.TryGetValue(TableName, out var selectedTable))
|
||||
if (selectedTable.Columns.TryGetValue(TableColumn, out var selectedColumn))
|
||||
output = GetVersion(selectedColumn.Values[index], Default);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetDateTime
|
||||
|
||||
public static DateTime? GetDateTime(object Value, DateTime? Default = null)
|
||||
{
|
||||
DateTime? output = Default;
|
||||
|
||||
if (Value == null)
|
||||
return output;
|
||||
|
||||
try
|
||||
{
|
||||
if (Value is DateTime time)
|
||||
output = time;
|
||||
else if (Value is string @string)
|
||||
{
|
||||
var tempOutput = Convert.ToDateTime(@string);
|
||||
DateTime.SpecifyKind(tempOutput, DateTimeKind.Utc);
|
||||
if (tempOutput != null)
|
||||
output = tempOutput;
|
||||
else
|
||||
output = Default;
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public DateTime? GetDateTimeByAddressInfo(string TableName, string TableColumn, int index = 0, DateTime? Default = null)
|
||||
{
|
||||
DateTime? output = Default;
|
||||
|
||||
try
|
||||
{
|
||||
if (Tables.TryGetValue(TableName, out var selectedTable))
|
||||
if (selectedTable.Columns.TryGetValue(TableColumn, out var selectedColumn))
|
||||
output = GetDateTime(selectedColumn.Values[index], Default);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetDouble
|
||||
|
||||
public static double? GetDouble(object Value, double? Default = null)
|
||||
{
|
||||
double? output = Default;
|
||||
|
||||
try
|
||||
{
|
||||
if (Value is int @int)
|
||||
output = Convert.ToDouble(@int);
|
||||
else if (Value is Int64 @int64)
|
||||
output = Convert.ToDouble(@int64);
|
||||
else if (Value is double @double)
|
||||
output = @double;
|
||||
else if (Value is string @string)
|
||||
{
|
||||
if (double.TryParse(@string, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.GetCultureInfo("en-US").NumberFormat, out double tempOutput))
|
||||
output = tempOutput;
|
||||
else
|
||||
output = Default;
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public double? GetDoubleByAddressInfo(string TableName, string TableColumn, int index = 0, double? Default = null)
|
||||
{
|
||||
double? output = Default;
|
||||
|
||||
try
|
||||
{
|
||||
if (Tables.TryGetValue(TableName, out var selectedTable))
|
||||
if (selectedTable.Columns.TryGetValue(TableColumn, out var selectedColumn))
|
||||
output = GetDouble(selectedColumn.Values[index], Default);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
public class cApiM42TicketQueueInfo
|
||||
{
|
||||
public string QueueName { get; set; }
|
||||
public Guid QueueID { get; set; }
|
||||
}
|
||||
|
||||
public class cF4SDTicketSummary
|
||||
{
|
||||
public Guid TicketObjectId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string ActivityType { get; set; }
|
||||
public string Summary { get; set; }
|
||||
public string Sid { get; set; }
|
||||
public string Status { get; set; }
|
||||
public int StatusId { get; set; }
|
||||
public string Urgency { get; set; }
|
||||
public int UrgencyId { get; set; }
|
||||
public string Impact { get; set; }
|
||||
public int ImpactId { get; set; }
|
||||
public string AssetCIName { get; set; }
|
||||
public string AssetName { get; set; }
|
||||
public string ServiceName { get; set; }
|
||||
public Guid ServiceId { get; set; }
|
||||
public bool IsPrimaryAccount { get; set; }
|
||||
}
|
||||
|
||||
public class cF4SDTicket : cF4SDTicketSummary
|
||||
{
|
||||
public enum enumTicketCreationSource
|
||||
{
|
||||
Unknown = 0,
|
||||
Mail = 1,
|
||||
Phone = 2,
|
||||
F4SD = 3
|
||||
}
|
||||
|
||||
public class cTicketJournalItem
|
||||
{
|
||||
public DateTime CreationDate { get; set; }
|
||||
public string Header { get; set; }
|
||||
public string CreatedBy { get; set; }
|
||||
public string DescriptionHtml { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool IsVisibleForUser { get; set; }
|
||||
public Guid ActivityObjectId { get; set; }
|
||||
public Guid JournalId { get; set; }
|
||||
}
|
||||
|
||||
public Guid AffectedUserId { get; set; }
|
||||
public Guid AssetId { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime? ClosingDate { get; set; }
|
||||
public int CreationSourceId { get; set; }
|
||||
public string CreationSource { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
public string DescriptionHtml { get; set; }
|
||||
public int PriorityId { get; set; }
|
||||
public string Priority { get; set; }
|
||||
public Guid CategoryId { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string CategoryHierarchical { get; set; }
|
||||
public string CIName { get; set; }
|
||||
public string DirectLinkEdit { get; set; }
|
||||
public Guid AssetCIId { get; set; }
|
||||
public int AssetSKUAssetGroupId { get; set; }
|
||||
public string AssetSKUAssetGroup { get; set; }
|
||||
public int AssetSKUTypeId { get; set; }
|
||||
public string AssetSKUType { get; set; }
|
||||
public string DirectLinkPreview { get; set; }
|
||||
public string DirectLinkClose { get; set; }
|
||||
public string AffectedUser { get; set; }
|
||||
public string SolutionHtml { get; set; }
|
||||
public string Solution { get; set; }
|
||||
public string AssetDomain { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -61,10 +61,12 @@
|
||||
<Compile Include="..\..\..\Common Code\Logging\C4IT.Logging.LogManager.cs">
|
||||
<Link>Common\C4IT.Logging.LogManager.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\_Common\C4IT.F4SD.Base.Ticket.cs">
|
||||
<Link>Common\C4IT.F4SD.Base.Ticket.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\SharedAssemblyInfo.cs">
|
||||
<Link>Properties\SharedAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Common\C4IT.FASD.Base.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -80,9 +80,6 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>M42Libraries\Matrix42.Services.Description.Contracts.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.QualityTools.Testing.Fakes, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -132,7 +129,6 @@
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="deploy.ps1" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="SignSourceFiles.cmd" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\M42F4SDUUXWorkspace\M42F4SDUUXWorkspace.projitems" Label="Shared" />
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
using C4IT.F4SDM;
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.Logging;
|
||||
using Matrix42.Common;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Dynamic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
@@ -20,9 +14,17 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
using Matrix42.Common;
|
||||
using update4u.SPS.DataLayer;
|
||||
using update4u.SPS.DataLayer.Transaction;
|
||||
using static C4IT.FASD.Base.cF4SDTicket;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using C4IT.F4SDM;
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.Logging;
|
||||
|
||||
using static C4IT.Logging.cLogManager;
|
||||
|
||||
namespace C4IT.F4SD
|
||||
@@ -152,6 +154,7 @@ namespace C4IT.F4SD
|
||||
}
|
||||
|
||||
|
||||
[Obsolete]
|
||||
internal async Task<List<cF4SDTicketSummary>> getTicketListByUser(string userSid, int hours, int queueoption, List<cApiM42TicketQueueInfo> queues)
|
||||
{
|
||||
var CM = MethodBase.GetCurrentMethod();
|
||||
@@ -265,6 +268,116 @@ namespace C4IT.F4SD
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<List<cF4SDTicketSummary>> getTicketListByUser(Guid userId, int hours, int queueoption, List<cApiM42TicketQueueInfo> queues)
|
||||
{
|
||||
var CM = MethodBase.GetCurrentMethod();
|
||||
LogMethodBegin(CM);
|
||||
try
|
||||
{
|
||||
LogEntry($"Generating ticket list for userId: '{userId}', {hours} hours", LogLevels.Debug);
|
||||
await Task.Delay(0);
|
||||
|
||||
var tickets = new List<cF4SDTicketSummary>();
|
||||
var activityFilter = GetActivityFilter(userId, hours, ticketAndServiceRequestEnabled(), queueoption, queues);
|
||||
LogEntry($"ASql Filter: {activityFilter}");
|
||||
|
||||
var activityTable = FragmentRequestBase.SimpleLoad(SPSActivityClassBaseID,
|
||||
"[Expression-ObjectID] as EOID, TicketNumber, Subject, Service.ID as ServiceId, Service.Name as ServiceName, SUBQUERY(BasicSchemaObjectType as bso, bso.Name, bso.id = base.T(SPSCommonClassBase).TypeID) as ActivityType" +
|
||||
", COALESCE(T(SPSActivityClassIncident).Asset.T(SPSComputerClassBase).Name, T(SPSActivityClassIncident).Asset.T(SPSAssetClassSIMCard).PhoneNumber, T(SPSActivityClassIncident).Asset.Name, T(SPSActivityClassIncident).Asset.objectid) as AssetName" +
|
||||
", SUBQUERY(BasicSchemaObjectType AS t, t.Name, t.ID=base.T(SPSActivityClassIncident).Asset.T(SPSCommonClassBase).TypeID) as AssetCIName",
|
||||
activityFilter);
|
||||
|
||||
if (activityTable?.Rows == null || activityTable.Rows.Count == 0)
|
||||
{
|
||||
LogEntry($"No activity entries found for userId: '{userId}'", LogLevels.Warning);
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < activityTable.Rows.Count; i++)
|
||||
{
|
||||
DataRow entry = activityTable.Rows[i];
|
||||
var activityEOId = getGuidFromObject(entry["EOID"]);
|
||||
var ticketNumber = getStringFromObject(entry["TicketNumber"]);
|
||||
var activityType = getStringFromObject(entry["ActivityType"]);
|
||||
var assetName = getStringFromObject(entry["AssetName"]);
|
||||
var ServiceName = getStringFromObject(entry["ServiceName"]);
|
||||
var assetCIName = getStringFromObject(entry["AssetCIName"]);
|
||||
var subject = getStringFromObject(entry["Subject"]);
|
||||
|
||||
var stateData = GetActivityState(activityEOId);
|
||||
var state = stateData.Item1;
|
||||
var stateDisp = stateData.Item2;
|
||||
|
||||
LogEntry($"Activity found {i + 1}/{activityTable.Rows.Count}: ObjectID={activityEOId}, TicketNumber={ticketNumber}, Subject={subject}, State={state}", LogLevels.Debug);
|
||||
|
||||
if (string.IsNullOrEmpty(ticketNumber))
|
||||
{
|
||||
LogEntry($"No TicketNumber found for activity entry", LogLevels.Warning);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(assetName))
|
||||
{
|
||||
LogEntry($"No AssetName found for activity entry", LogLevels.Debug);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(assetCIName))
|
||||
{
|
||||
LogEntry($"No AssetCIName found for activity entry", LogLevels.Debug);
|
||||
}
|
||||
var ServiceId = getGuidFromObject(entry["ServiceId"]);
|
||||
if (ServiceId == Guid.Empty)
|
||||
{
|
||||
LogEntry($"no ServiceId found for activity entry", LogLevels.Debug);
|
||||
}
|
||||
var AssetCIName = getStringFromObject(entry["AssetCIName"]);
|
||||
if (AssetCIName == string.Empty)
|
||||
{
|
||||
LogEntry($"no AssetCIName found for activity entry", LogLevels.Debug);
|
||||
}
|
||||
var Subject = getStringFromObject(entry["Subject"]);
|
||||
if (Subject == string.Empty)
|
||||
{
|
||||
LogEntry($"no Subject found for activity entry", LogLevels.Warning);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(subject))
|
||||
{
|
||||
LogEntry($"No Subject found for activity entry", LogLevels.Warning);
|
||||
continue;
|
||||
}
|
||||
|
||||
tickets.Add(new cF4SDTicketSummary()
|
||||
{
|
||||
TicketObjectId = activityEOId,
|
||||
Name = ticketNumber,
|
||||
ActivityType = activityType,
|
||||
AssetCIName = assetCIName,
|
||||
AssetName = assetName,
|
||||
ServiceId = ServiceId,
|
||||
ServiceName = ServiceName,
|
||||
StatusId = state,
|
||||
Status = stateDisp,
|
||||
Summary = subject,
|
||||
IsPrimaryAccount = true
|
||||
});
|
||||
}
|
||||
|
||||
tickets = tickets.OrderByDescending(x => x.Name).ToList();
|
||||
return tickets;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogMethodEnd(CM);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly string[] TicketOverviewKeys = new[]
|
||||
{
|
||||
"TicketsNew",
|
||||
@@ -279,6 +392,62 @@ namespace C4IT.F4SD
|
||||
"UnassignedTicketsCritical"
|
||||
};
|
||||
|
||||
public class cF4SDTicket : cF4SDTicketSummary
|
||||
{
|
||||
public enum enumTicketCreationSource
|
||||
{
|
||||
Unknown = 0,
|
||||
Mail = 1,
|
||||
Phone = 2,
|
||||
F4SD = 3
|
||||
}
|
||||
|
||||
public class cTicketJournalItem
|
||||
{
|
||||
public DateTime CreationDate { get; set; }
|
||||
public string Header { get; set; }
|
||||
public string CreatedBy { get; set; }
|
||||
public string DescriptionHtml { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool IsVisibleForUser { get; set; }
|
||||
public Guid ActivityObjectId { get; set; }
|
||||
public Guid JournalId { get; set; }
|
||||
}
|
||||
|
||||
public Guid AffectedUserId { get; set; }
|
||||
public Guid AssetId { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime? ClosingDate { get; set; }
|
||||
public int CreationSourceId { get; set; }
|
||||
public string CreationSource { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
public string DescriptionHtml { get; set; }
|
||||
public int PriorityId { get; set; }
|
||||
public string Priority { get; set; }
|
||||
public Guid CategoryId { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string CategoryHierarchical { get; set; }
|
||||
public string CIName { get; set; }
|
||||
public string DirectLinkEdit { get; set; }
|
||||
public Guid AssetCIId { get; set; }
|
||||
public int AssetSKUAssetGroupId { get; set; }
|
||||
public string AssetSKUAssetGroup { get; set; }
|
||||
public int AssetSKUTypeId { get; set; }
|
||||
public string AssetSKUType { get; set; }
|
||||
public string DirectLinkPreview { get; set; }
|
||||
public string DirectLinkClose { get; set; }
|
||||
public string AffectedUser { get; set; }
|
||||
public string SolutionHtml { get; set; }
|
||||
public string Solution { get; set; }
|
||||
public string AssetDomain { get; set; }
|
||||
public string Urgency { get; set; }
|
||||
public int UrgencyId { get; set; }
|
||||
public string Impact { get; set; }
|
||||
public int ImpactId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class TicketOverviewCountsResult
|
||||
{
|
||||
[JsonProperty("counts")]
|
||||
@@ -327,6 +496,7 @@ namespace C4IT.F4SD
|
||||
public string ActivityType { get; set; }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
internal async Task<TicketOverviewCountsResult> getTicketOverviewCounts(
|
||||
string sid,
|
||||
string scope,
|
||||
@@ -384,6 +554,64 @@ namespace C4IT.F4SD
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<TicketOverviewCountsResult> getTicketOverviewCounts(
|
||||
Guid userId,
|
||||
string scope,
|
||||
IEnumerable<string> keys,
|
||||
int queueoption,
|
||||
List<cApiM42TicketQueueInfo> queues)
|
||||
{
|
||||
var CM = MethodBase.GetCurrentMethod();
|
||||
LogMethodBegin(CM);
|
||||
try
|
||||
{
|
||||
var useRoleScope = string.Equals(scope, "role", StringComparison.OrdinalIgnoreCase);
|
||||
var normalizedKeys = (keys ?? Array.Empty<string>())
|
||||
.Where(k => !string.IsNullOrWhiteSpace(k))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
if (normalizedKeys.Count == 0)
|
||||
{
|
||||
normalizedKeys.AddRange(TicketOverviewKeys);
|
||||
}
|
||||
|
||||
var entries = await LoadTicketOverviewEntries(userId, useRoleScope, queueoption, queues);
|
||||
List<TicketOverviewEntry> unassignedEntries = null;
|
||||
if (!useRoleScope && normalizedKeys.Any(IsUnassignedOverviewKey))
|
||||
{
|
||||
unassignedEntries = await LoadTicketOverviewUnassignedEntriesForPersonalScope(userId, queueoption, queues);
|
||||
}
|
||||
|
||||
var counts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var key in normalizedKeys)
|
||||
{
|
||||
if (!useRoleScope && IsUnassignedOverviewKey(key))
|
||||
{
|
||||
counts[key] = (unassignedEntries ?? new List<TicketOverviewEntry>())
|
||||
.Count(entry => MatchesTicketOverviewKey(entry, key));
|
||||
}
|
||||
else
|
||||
{
|
||||
counts[key] = entries.Count(entry => MatchesTicketOverviewKey(entry, key));
|
||||
}
|
||||
}
|
||||
|
||||
return new TicketOverviewCountsResult { Counts = counts };
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
return new TicketOverviewCountsResult();
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogMethodEnd(CM);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
internal async Task<TicketOverviewCountsByRoleResult> getTicketOverviewCountsByRoles(
|
||||
string sid,
|
||||
IEnumerable<Guid> roleGuids,
|
||||
@@ -447,6 +675,70 @@ namespace C4IT.F4SD
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<TicketOverviewCountsByRoleResult> getTicketOverviewCountsByRoles(
|
||||
Guid userId,
|
||||
IEnumerable<Guid> roleGuids,
|
||||
IEnumerable<string> keys,
|
||||
int queueoption,
|
||||
List<cApiM42TicketQueueInfo> queues)
|
||||
{
|
||||
var CM = MethodBase.GetCurrentMethod();
|
||||
LogMethodBegin(CM);
|
||||
try
|
||||
{
|
||||
var normalizedKeys = (keys ?? Array.Empty<string>())
|
||||
.Where(k => !string.IsNullOrWhiteSpace(k))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
if (normalizedKeys.Count == 0)
|
||||
{
|
||||
normalizedKeys.AddRange(TicketOverviewKeys);
|
||||
}
|
||||
|
||||
var roleIds = await ResolveTicketOverviewRoleIdsAsync(userId, roleGuids);
|
||||
if (roleIds.Count == 0)
|
||||
return new TicketOverviewCountsByRoleResult();
|
||||
|
||||
var entries = await LoadTicketOverviewEntriesByRoleIds(roleIds, queueoption, queues);
|
||||
var entriesByRole = entries
|
||||
.GroupBy(entry => entry.RecipientRoleId)
|
||||
.ToDictionary(group => group.Key, group => group.ToList());
|
||||
|
||||
var countsByRole = new Dictionary<string, Dictionary<string, int>>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var roleId in roleIds)
|
||||
{
|
||||
var roleEntries = entriesByRole.TryGetValue(roleId, out var list)
|
||||
? list
|
||||
: new List<TicketOverviewEntry>();
|
||||
|
||||
var counts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var key in normalizedKeys)
|
||||
{
|
||||
counts[key] = roleEntries.Count(entry => MatchesTicketOverviewKey(entry, key));
|
||||
}
|
||||
|
||||
countsByRole[roleId.ToString()] = counts;
|
||||
}
|
||||
|
||||
return new TicketOverviewCountsByRoleResult
|
||||
{
|
||||
GeneratedAtUtc = DateTime.UtcNow,
|
||||
CountsByRole = countsByRole
|
||||
};
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
return new TicketOverviewCountsByRoleResult();
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogMethodEnd(CM);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
internal async Task<List<TicketOverviewRelationDto>> getTicketOverviewRelations(
|
||||
string sid,
|
||||
string scope,
|
||||
@@ -526,6 +818,86 @@ namespace C4IT.F4SD
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<List<TicketOverviewRelationDto>> getTicketOverviewRelations(
|
||||
Guid userId,
|
||||
string scope,
|
||||
string key,
|
||||
int count,
|
||||
int queueoption,
|
||||
List<cApiM42TicketQueueInfo> queues)
|
||||
{
|
||||
var CM = MethodBase.GetCurrentMethod();
|
||||
LogMethodBegin(CM);
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
return new List<TicketOverviewRelationDto>();
|
||||
|
||||
var useRoleScope = string.Equals(scope, "role", StringComparison.OrdinalIgnoreCase);
|
||||
List<TicketOverviewEntry> entries;
|
||||
if (!useRoleScope && IsUnassignedOverviewKey(key))
|
||||
{
|
||||
entries = await LoadTicketOverviewUnassignedEntriesForPersonalScope(userId, queueoption, queues);
|
||||
}
|
||||
else
|
||||
{
|
||||
entries = await LoadTicketOverviewEntries(userId, useRoleScope, queueoption, queues);
|
||||
}
|
||||
|
||||
var filtered = entries
|
||||
.Where(entry => MatchesTicketOverviewKey(entry, key))
|
||||
.OrderByDescending(entry => entry.CreatedDate)
|
||||
.ToList();
|
||||
|
||||
if (count > 0)
|
||||
filtered = filtered.Take(count).ToList();
|
||||
|
||||
var relations = new List<TicketOverviewRelationDto>(filtered.Count);
|
||||
foreach (var entry in filtered)
|
||||
{
|
||||
var relation = new TicketOverviewRelationDto
|
||||
{
|
||||
Type = enumF4sdSearchResultClass.Ticket,
|
||||
DisplayName = entry.TicketNumber ?? string.Empty,
|
||||
Name = entry.TicketNumber ?? string.Empty,
|
||||
id = entry.TicketId,
|
||||
Status = enumF4sdSearchResultStatus.Active,
|
||||
Infos = new Dictionary<string, string>
|
||||
{
|
||||
["Summary"] = entry.Summary ?? string.Empty,
|
||||
["StatusId"] = ConvertM42State(entry.State),
|
||||
["ActivityType"] = entry.ActivityType ?? string.Empty,
|
||||
["UserDisplayName"] = entry.InitiatorDisplayName ?? string.Empty,
|
||||
["UserAccount"] = entry.InitiatorAccount ?? string.Empty,
|
||||
["UserDomain"] = entry.InitiatorDomain ?? string.Empty,
|
||||
["UserSid"] = entry.InitiatorSid ?? string.Empty,
|
||||
["Sids"] = entry.InitiatorSid ?? string.Empty,
|
||||
["UserId"] = entry.InitiatorId != Guid.Empty ? entry.InitiatorId.ToString() : string.Empty,
|
||||
["UserGuid"] = entry.InitiatorId != Guid.Empty ? entry.InitiatorId.ToString() : string.Empty
|
||||
},
|
||||
Identities = new List<cF4sdIdentityEntry>
|
||||
{
|
||||
new cF4sdIdentityEntry { Class = enumFasdInformationClass.Ticket, Id = entry.TicketId },
|
||||
new cF4sdIdentityEntry { Class = enumFasdInformationClass.User, Id = entry.InitiatorId }
|
||||
}
|
||||
};
|
||||
relations.Add(relation);
|
||||
}
|
||||
|
||||
return relations;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
return new List<TicketOverviewRelationDto>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogMethodEnd(CM);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
private async Task<List<TicketOverviewEntry>> LoadTicketOverviewEntries(
|
||||
string sid,
|
||||
bool useRoleScope,
|
||||
@@ -555,6 +927,34 @@ namespace C4IT.F4SD
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<List<TicketOverviewEntry>> LoadTicketOverviewEntries(
|
||||
Guid userId,
|
||||
bool useRoleScope,
|
||||
int queueoption,
|
||||
List<cApiM42TicketQueueInfo> queues)
|
||||
{
|
||||
var CM = MethodBase.GetCurrentMethod();
|
||||
LogMethodBegin(CM);
|
||||
try
|
||||
{
|
||||
if (userId == Guid.Empty)
|
||||
return new List<TicketOverviewEntry>();
|
||||
|
||||
var filter = await BuildTicketOverviewFilterAsync(userId, useRoleScope, queueoption, queues);
|
||||
return await LoadTicketOverviewEntriesByFilter(filter);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
return new List<TicketOverviewEntry>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogMethodEnd(CM);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
private async Task<List<TicketOverviewEntry>> LoadTicketOverviewUnassignedEntriesForPersonalScope(
|
||||
string sid,
|
||||
int queueoption,
|
||||
@@ -588,6 +988,39 @@ namespace C4IT.F4SD
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<List<TicketOverviewEntry>> LoadTicketOverviewUnassignedEntriesForPersonalScope(
|
||||
Guid userId,
|
||||
int queueoption,
|
||||
List<cApiM42TicketQueueInfo> queues)
|
||||
{
|
||||
var CM = MethodBase.GetCurrentMethod();
|
||||
LogMethodBegin(CM);
|
||||
try
|
||||
{
|
||||
await Task.Delay(0);
|
||||
|
||||
if (userId == Guid.Empty)
|
||||
return new List<TicketOverviewEntry>();
|
||||
|
||||
var roleIds = await ResolveTicketOverviewRoleIdsAsync(userId, null);
|
||||
var filter = BuildTicketOverviewFilterForRoleIds(roleIds, null, queueoption, queues);
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
return new List<TicketOverviewEntry>();
|
||||
|
||||
filter += " AND Recipient IS NULL";
|
||||
return await LoadTicketOverviewEntriesByFilter(filter);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
return new List<TicketOverviewEntry>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogMethodEnd(CM);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<List<TicketOverviewEntry>> LoadTicketOverviewEntriesByRoleIds(
|
||||
IEnumerable<Guid> roleIds,
|
||||
int queueoption,
|
||||
@@ -738,6 +1171,7 @@ namespace C4IT.F4SD
|
||||
return null;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
private async Task<string> BuildTicketOverviewFilterAsync(
|
||||
string sid,
|
||||
bool useRoleScope,
|
||||
@@ -757,6 +1191,25 @@ namespace C4IT.F4SD
|
||||
return BuildTicketOverviewFilterForRoleIds(roleIds, filter);
|
||||
}
|
||||
|
||||
private async Task<string> BuildTicketOverviewFilterAsync(
|
||||
Guid userId,
|
||||
bool useRoleScope,
|
||||
int queueoption,
|
||||
List<cApiM42TicketQueueInfo> queues)
|
||||
{
|
||||
var filter = BuildTicketOverviewBaseFilter(queueoption, queues);
|
||||
|
||||
if (!useRoleScope)
|
||||
{
|
||||
var recipientFilter = $"Recipient = '{Escape(userId.ToString("D"))}'";
|
||||
filter += $" AND ({recipientFilter})";
|
||||
return filter;
|
||||
}
|
||||
|
||||
var roleIds = await ResolveTicketOverviewRoleIdsAsync(userId, null);
|
||||
return BuildTicketOverviewFilterForRoleIds(roleIds, filter);
|
||||
}
|
||||
|
||||
private string BuildTicketOverviewBaseFilter(int queueoption, List<cApiM42TicketQueueInfo> queues)
|
||||
{
|
||||
var filter = "T(SPSCommonClassBase).State <> 204";
|
||||
@@ -855,6 +1308,7 @@ namespace C4IT.F4SD
|
||||
return filter;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
private async Task<List<Guid>> ResolveTicketOverviewRoleIdsAsync(string sid, IEnumerable<Guid> roleGuids)
|
||||
{
|
||||
var roleIds = (roleGuids ?? Enumerable.Empty<Guid>())
|
||||
@@ -880,6 +1334,26 @@ namespace C4IT.F4SD
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private async Task<List<Guid>> ResolveTicketOverviewRoleIdsAsync(Guid userId, IEnumerable<Guid> roleGuids)
|
||||
{
|
||||
var roleIds = (roleGuids ?? Enumerable.Empty<Guid>())
|
||||
.Where(id => id != Guid.Empty)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (roleIds.Count > 0)
|
||||
return roleIds;
|
||||
|
||||
if (userId == Guid.Empty)
|
||||
return new List<Guid>();
|
||||
|
||||
var roles = await getRoleMembershipById(userId) ?? new List<M42Role>();
|
||||
return roles
|
||||
.Where(role => role != null && role.Id != Guid.Empty)
|
||||
.Select(role => role.Id)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
private static bool IsUnassignedOverviewKey(string key)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
@@ -987,6 +1461,8 @@ namespace C4IT.F4SD
|
||||
{
|
||||
return input?.Replace("'", "''");
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
private string GetActivityFilter(
|
||||
string userSid,
|
||||
int hours,
|
||||
@@ -1074,6 +1550,92 @@ namespace C4IT.F4SD
|
||||
return filter;
|
||||
}
|
||||
|
||||
private string GetActivityFilter(
|
||||
Guid userId,
|
||||
int hours,
|
||||
bool ticketAndServiceRequestEnabled,
|
||||
int queueoption,
|
||||
List<cApiM42TicketQueueInfo> queues
|
||||
)
|
||||
{
|
||||
// Baseline-Filter auf User und Datum
|
||||
var filter = $"Initiator = '{Escape(userId.ToString("D"))}'";
|
||||
|
||||
string fStartDate = AsqlHelper.PrepareDateTime(DateTime.UtcNow.AddHours(-hours), true);
|
||||
string fEndDate = AsqlHelper.PrepareDateTime(DateTime.UtcNow, true);
|
||||
|
||||
// Incident vs. Ticket/ServiceRequest
|
||||
if (ticketAndServiceRequestEnabled)
|
||||
{
|
||||
filter +=
|
||||
" AND (UsedInTypeSPSActivityTypeIncident IS NOT NULL" +
|
||||
" OR UsedInTypeSPSActivityTypeTicket IS NOT NULL" +
|
||||
" OR UsedInTypeSPSActivityTypeServiceRequest IS NOT NULL)";
|
||||
}
|
||||
else
|
||||
{
|
||||
filter += " AND (UsedInTypeSPSActivityTypeIncident IS NOT NULL)";
|
||||
}
|
||||
|
||||
// Offene bzw. kürzlich geschlossene Objekte
|
||||
filter +=
|
||||
" AND (T(SPSCommonClassBase).State <> 204" +
|
||||
$" OR (ClosedDate > {fStartDate} AND ClosedDate < {fEndDate}))";
|
||||
|
||||
// Queue-Filter nur, wenn tatsächlich Queues übergeben wurden
|
||||
if (queues != null && queues.Count > 0)
|
||||
{
|
||||
// URL-escaping für Name und ID
|
||||
var escapedNames = queues
|
||||
.Select(q => $"'{Escape(q.QueueName)}'")
|
||||
.ToList();
|
||||
var escapedIds = queues
|
||||
.Select(q => $"'{Escape(q.QueueID.ToString())}'")
|
||||
.ToList();
|
||||
|
||||
string nameList = string.Join(", ", escapedNames);
|
||||
string idList = string.Join(", ", escapedIds);
|
||||
|
||||
switch (queueoption)
|
||||
{
|
||||
// 1 = entweder keine Queue oder eine der übergebenen Queues (Name oder ID)
|
||||
case 1:
|
||||
filter +=
|
||||
$" AND (" +
|
||||
"Queue IS NULL" +
|
||||
$" OR Queue.Name IN ({nameList})" +
|
||||
$" OR Queue.ID IN ({idList})" +
|
||||
")";
|
||||
break;
|
||||
|
||||
// 2 = nur die übergebenen Queues (Name oder ID)
|
||||
case 2:
|
||||
filter +=
|
||||
$" AND (" +
|
||||
"Queue IS NOT NULL" +
|
||||
$" AND (Queue.Name IN ({nameList})" +
|
||||
$" OR Queue.ID IN ({idList}))" +
|
||||
")";
|
||||
break;
|
||||
|
||||
// 3 = nur Objekte ohne Queue
|
||||
case 3:
|
||||
filter += " AND Queue IS NULL";
|
||||
break;
|
||||
|
||||
// 0 oder andere = keine zusätzliche Einschränkung
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (queueoption == 3)
|
||||
{
|
||||
// Ausnahme: wenn keine Queues übergeben, aber Option 3 = nur ohne Queue
|
||||
filter += " AND Queue IS NULL";
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
internal async Task<DirectLink> getDirectLinkCreateTicket(string sid, string assetname)
|
||||
{
|
||||
@@ -1361,21 +1923,21 @@ namespace C4IT.F4SD
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<List<cTicketJournalItem>> GetJournalEntries(Guid activityEOID)
|
||||
internal async Task<List<cF4SDTicket.cTicketJournalItem>> GetJournalEntries(Guid activityEOID)
|
||||
{
|
||||
var CM = MethodBase.GetCurrentMethod();
|
||||
LogMethodBegin(CM);
|
||||
try
|
||||
{
|
||||
await Task.Delay(0);
|
||||
List<cTicketJournalItem> journalEntries = new List<cTicketJournalItem>();
|
||||
List<cF4SDTicket.cTicketJournalItem> journalEntries = new List<cF4SDTicket.cTicketJournalItem>();
|
||||
var entries = F4SDM42WebApiController.defaultInstance._journalService.GetJournalList(activityEOID, false, 0, 50, "<a href=\"#\" class=\"mx-object-journal--link {0} {1}\">{2}</a>", 120);
|
||||
LogEntry($"{entries.Length} Journal entries found for ObjectID={activityEOID}", LogLevels.Debug);
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
Matrix42.Contracts.Platform.Data.JournalEntryInfo item = entries[i];
|
||||
LogEntry($"Journal entry {i + 1}/{entries.Length}: ID={item.Id}, CreatedDate={item.CreatedDate}, CreatedBy={item.Creator}, Header={item.Header}", LogLevels.Debug);
|
||||
journalEntries.Add(new cTicketJournalItem()
|
||||
journalEntries.Add(new cF4SDTicket.cTicketJournalItem()
|
||||
{
|
||||
JournalId = item.Id,
|
||||
ActivityObjectId = activityEOID,
|
||||
@@ -1932,7 +2494,7 @@ namespace C4IT.F4SD
|
||||
try
|
||||
{
|
||||
await Task.Delay(0);
|
||||
var asqlFilter = string.Format("Members.Id = '{0}'", UserId.ToString());
|
||||
var asqlFilter = string.Format("Members.Id = '{0}'", UserId.ToString("D"));
|
||||
LogEntry($"ASql Filter: {asqlFilter}");
|
||||
|
||||
FragmentRequestBase fragmentRequestBase = new FragmentRequestBase(SPSSecurityClassRole, ColumnSelectOption.List, "Id as Id" +
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
using C4IT.F4SDM;
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.Logging;
|
||||
using Matrix42.Common;
|
||||
using Matrix42.Contracts.ServiceManagement.ServiceContracts;
|
||||
using Matrix42.Pandora.Contracts;
|
||||
using Matrix42.Services.Description.Contracts;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
@@ -18,8 +11,17 @@ using System.Web.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.OData;
|
||||
using System.Web.OData.Query;
|
||||
|
||||
using Matrix42.Common;
|
||||
using Matrix42.Contracts.ServiceManagement.ServiceContracts;
|
||||
using Matrix42.Pandora.Contracts;
|
||||
using Matrix42.Services.Description.Contracts;
|
||||
using update4u.SPS.Utility.GlobalConfiguration;
|
||||
using static C4IT.FASD.Base.cF4SDTicket;
|
||||
|
||||
using C4IT.F4SDM;
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.Logging;
|
||||
|
||||
using static C4IT.Logging.cLogManager;
|
||||
|
||||
namespace C4IT.F4SD
|
||||
@@ -28,6 +30,7 @@ namespace C4IT.F4SD
|
||||
public partial class F4SDM42WebApiController : ApiController
|
||||
{
|
||||
public static bool IsInitialized { get; private set; } = false;
|
||||
|
||||
public static F4SDM42WebApiController defaultInstance;
|
||||
//private readonly IIncidentService _incidentService;
|
||||
internal readonly GlobalConfigurationProvider _globalConfigurationProvider;
|
||||
@@ -91,12 +94,14 @@ namespace C4IT.F4SD
|
||||
await Task.Delay(0);
|
||||
return await _f4stHelperService.getDirectLinkCreateTicket(sid, assetname);
|
||||
}
|
||||
|
||||
[Route("getDirectLinkF4SD"), HttpGet]
|
||||
public async Task<string> getDirectLinkF4SD(Guid EOID, string Type)
|
||||
{
|
||||
return await _f4stHelperService.getDirectLinkF4SD(EOID, Type);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
[Route("getTicketList"), HttpGet]
|
||||
public async Task<List<cF4SDTicketSummary>> getTicketList(
|
||||
string sid,
|
||||
@@ -116,9 +121,28 @@ namespace C4IT.F4SD
|
||||
);
|
||||
}
|
||||
|
||||
[Route("getTicketListForUser"), HttpGet]
|
||||
public async Task<List<cF4SDTicketSummary>> getTicketListForUser(
|
||||
Guid userId,
|
||||
int hours,
|
||||
int queueoption = 0,
|
||||
string queues = ""
|
||||
)
|
||||
{
|
||||
var decodedPairs = ParseQueues(queues);
|
||||
|
||||
// Nun weiterreichen an Service
|
||||
return await _f4stHelperService.getTicketListByUser(
|
||||
userId,
|
||||
hours,
|
||||
queueoption,
|
||||
decodedPairs
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
[Route("getTicketDetails"), HttpGet]
|
||||
public async Task<cF4SDTicket> getTicketDetails(Guid objectId)
|
||||
public async Task<F4SDHelperService.cF4SDTicket> getTicketDetails(Guid objectId)
|
||||
{
|
||||
var tickets = await _f4stHelperService.getTicketDetails(new List<Guid>() { objectId });
|
||||
if (tickets.Count > 0)
|
||||
@@ -128,11 +152,12 @@ namespace C4IT.F4SD
|
||||
}
|
||||
|
||||
[Route("getTicketHistory"), HttpGet]
|
||||
public async Task<List<cTicketJournalItem>> getTicketHistory(Guid objectId)
|
||||
public async Task<List<F4SDHelperService.cF4SDTicket.cTicketJournalItem>> getTicketHistory(Guid objectId)
|
||||
{
|
||||
return await _f4stHelperService.GetJournalEntries(objectId);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
[Route("getTicketOverviewCounts"), HttpGet]
|
||||
public async Task<F4SDHelperService.TicketOverviewCountsResult> getTicketOverviewCounts(
|
||||
string sid,
|
||||
@@ -152,6 +177,27 @@ namespace C4IT.F4SD
|
||||
return await _f4stHelperService.getTicketOverviewCounts(sid, scope, parsedKeys, queueoption, decodedQueues);
|
||||
}
|
||||
|
||||
|
||||
[Route("getTicketOverviewCountsForUser"), HttpGet]
|
||||
public async Task<F4SDHelperService.TicketOverviewCountsResult> getTicketOverviewCountsForUser(
|
||||
Guid userId,
|
||||
string scope = "personal",
|
||||
string keys = "",
|
||||
int queueoption = 0,
|
||||
string queues = ""
|
||||
)
|
||||
{
|
||||
var parsedKeys = (keys ?? string.Empty)
|
||||
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(key => key.Trim())
|
||||
.Where(key => !string.IsNullOrWhiteSpace(key))
|
||||
.ToList();
|
||||
|
||||
var decodedQueues = ParseQueues(queues);
|
||||
return await _f4stHelperService.getTicketOverviewCounts(userId, scope, parsedKeys, queueoption, decodedQueues);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
[Route("getTicketOverviewCountsByRoles"), HttpPost]
|
||||
public async Task<F4SDHelperService.TicketOverviewCountsByRoleResult> getTicketOverviewCountsByRoles([FromBody] TicketOverviewCountsByRolesRequest request)
|
||||
{
|
||||
@@ -176,6 +222,31 @@ namespace C4IT.F4SD
|
||||
);
|
||||
}
|
||||
|
||||
[Route("getTicketOverviewCountsByRolesForUser"), HttpPost]
|
||||
public async Task<F4SDHelperService.TicketOverviewCountsByRoleResult> getTicketOverviewCountsByRolesForUser([FromBody] TicketOverviewCountsByRolesForUserRequest request)
|
||||
{
|
||||
var parsedKeys = (request?.Keys ?? new List<string>())
|
||||
.Where(key => !string.IsNullOrWhiteSpace(key))
|
||||
.Select(key => key.Trim())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
var roleGuids = (request?.RoleGuids ?? new List<Guid>())
|
||||
.Where(roleId => roleId != Guid.Empty)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var decodedQueues = ParseQueues(request?.Queues ?? string.Empty);
|
||||
return await _f4stHelperService.getTicketOverviewCountsByRoles(
|
||||
request.userId,
|
||||
roleGuids,
|
||||
parsedKeys,
|
||||
request?.QueueOption ?? 0,
|
||||
decodedQueues
|
||||
);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
[Route("getTicketOverviewRelations"), HttpGet]
|
||||
public async Task<List<F4SDHelperService.TicketOverviewRelationDto>> getTicketOverviewRelations(
|
||||
string sid,
|
||||
@@ -189,6 +260,22 @@ namespace C4IT.F4SD
|
||||
var decodedQueues = ParseQueues(queues);
|
||||
return await _f4stHelperService.getTicketOverviewRelations(sid, scope, key, count, queueoption, decodedQueues);
|
||||
}
|
||||
|
||||
[Route("getTicketOverviewRelationsForUser"), HttpGet]
|
||||
public async Task<List<F4SDHelperService.TicketOverviewRelationDto>> getTicketOverviewRelations(
|
||||
Guid userId,
|
||||
string scope = "personal",
|
||||
string key = "",
|
||||
int count = 0,
|
||||
int queueoption = 0,
|
||||
string queues = ""
|
||||
)
|
||||
{
|
||||
var decodedQueues = ParseQueues(queues);
|
||||
return await _f4stHelperService.getTicketOverviewRelations(userId, scope, key, count, queueoption, decodedQueues);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
[Route("updateActivitySolution/{objectId}"), HttpPost]
|
||||
public async Task<HttpResponseMessage> updateActivitySolution(Guid objectId, [FromBody] string SolutionHtml)
|
||||
@@ -234,8 +321,8 @@ namespace C4IT.F4SD
|
||||
var filter = AsqlHelper.BuildInCondition("ID", new Guid[] { User.Id });
|
||||
return Request.CreateResponse(HttpStatusCode.OK, await _f4stHelperService.UserPermissionsInfo(filter));
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("getRoleMemberships", Order = 1)]
|
||||
|
||||
[Route("getRoleMemberships", Order = 1), HttpGet]
|
||||
public async Task<HttpResponseMessage> getRoleMemberships([FromUri] GetRoleMembershipsRequest req)
|
||||
{
|
||||
var filter = "";
|
||||
@@ -263,6 +350,14 @@ namespace C4IT.F4SD
|
||||
}
|
||||
}
|
||||
|
||||
[Route("getRoleMemberships/{userId}", Order = 1), HttpGet]
|
||||
public async Task<HttpResponseMessage> getRoleMembershipForUser(Guid userId)
|
||||
{
|
||||
var filter = AsqlHelper.BuildInCondition("ID", new Guid[] { userId });
|
||||
return Request.CreateResponse(HttpStatusCode.OK, await _f4stHelperService.UserPermissionsInfo(filter));
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public class GetRoleMembershipsRequest
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
@@ -271,6 +366,7 @@ namespace C4IT.F4SD
|
||||
public GetRoleMembershipsRequest() { }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public class TicketOverviewCountsByRolesRequest
|
||||
{
|
||||
public string Sid { get; set; }
|
||||
@@ -280,22 +376,52 @@ namespace C4IT.F4SD
|
||||
public string Queues { get; set; }
|
||||
}
|
||||
|
||||
public class TicketOverviewCountsByRolesForUserRequest
|
||||
{
|
||||
public Guid userId { get; set; }
|
||||
public List<Guid> RoleGuids { get; set; } = new List<Guid>();
|
||||
public List<string> Keys { get; set; } = new List<string>();
|
||||
public int? QueueOption { get; set; }
|
||||
public string Queues { get; set; }
|
||||
}
|
||||
|
||||
private static List<cApiM42TicketQueueInfo> ParseQueues(string queues)
|
||||
{
|
||||
return (queues ?? string.Empty)
|
||||
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(part =>
|
||||
{
|
||||
var segments = part.Split(':');
|
||||
if (segments.Length != 2)
|
||||
var decodedPart = HttpUtility.UrlDecode(part)?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(decodedPart))
|
||||
return null;
|
||||
|
||||
var name = HttpUtility.UrlDecode(segments[0]);
|
||||
var idStr = HttpUtility.UrlDecode(segments[1]);
|
||||
var name = decodedPart;
|
||||
string idStr = null;
|
||||
|
||||
return Guid.TryParse(idStr, out var guid)
|
||||
? new cApiM42TicketQueueInfo { QueueName = name, QueueID = guid }
|
||||
: null;
|
||||
var separatorIndex = decodedPart.IndexOf(':');
|
||||
if (separatorIndex >= 0)
|
||||
{
|
||||
name = decodedPart.Substring(0, separatorIndex).Trim();
|
||||
idStr = decodedPart.Substring(separatorIndex + 1).Trim();
|
||||
}
|
||||
|
||||
var queue = new cApiM42TicketQueueInfo();
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
queue.QueueName = name;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(idStr))
|
||||
{
|
||||
if (!Guid.TryParse(idStr, out var guid))
|
||||
return null;
|
||||
|
||||
queue.QueueID = guid;
|
||||
queue.QueueName = null;
|
||||
}
|
||||
|
||||
if (queue.QueueID == Guid.Empty && string.IsNullOrWhiteSpace(queue.QueueName))
|
||||
return null;
|
||||
|
||||
return queue;
|
||||
})
|
||||
.Where(q => q != null)
|
||||
.ToList();
|
||||
@@ -343,6 +469,7 @@ namespace C4IT.F4SD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[RoutePrefix("api/C4ITF4SDWebApi/Logs")]
|
||||
public partial class F4SDM42LogsWebApiController : ApiController
|
||||
{
|
||||
@@ -372,8 +499,6 @@ namespace C4IT.F4SD
|
||||
catch { };
|
||||
}
|
||||
|
||||
|
||||
|
||||
public F4SDM42LogsWebApiController()
|
||||
{
|
||||
_f4stHelperService = new F4SDHelperService();
|
||||
|
||||
28
F4SDM42WebApi/M42ExtensionScaffolder/.gitignore
vendored
Normal file
28
F4SDM42WebApi/M42ExtensionScaffolder/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
.idea/
|
||||
.vs/
|
||||
.vscode/
|
||||
node_modules/
|
||||
dist/
|
||||
|
||||
package-lock.json
|
||||
workspace.json
|
||||
|
||||
.vs/
|
||||
.libs/
|
||||
.build/
|
||||
|
||||
bin/
|
||||
obj/
|
||||
|
||||
*.user
|
||||
|
||||
/**/AssemblyInfoProduct.cs
|
||||
/**/_Resharper.Caches
|
||||
/Solutions/*.sln.DotSettings.user
|
||||
/Solutions/**/*.csproj.user
|
||||
/Solutions/**/packages/*.*
|
||||
/Solutions/**/**/AssemblyInfoProduct.cs
|
||||
/Solutions/TestResult/*.*
|
||||
|
||||
BasePackage/Assemblies/
|
||||
14
F4SDM42WebApi/M42ExtensionScaffolder/README.md
Normal file
14
F4SDM42WebApi/M42ExtensionScaffolder/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# M42ExtensionScaffolder
|
||||
|
||||
## Description
|
||||
Template project acting as an extension scaffolder. Automatically generate extension structure including provided projects and runtimes
|
||||
|
||||
## Owner
|
||||
Name: Titan
|
||||
Email: dmytro.romanii@matrix42.com
|
||||
|
||||
## Product
|
||||
M42 Enterprise
|
||||
|
||||
## Documentation
|
||||
- [Service Information](./service-info.yml)
|
||||
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Matrix42.Extensions.Scaffolder.Core\Matrix42.Extensions.Scaffolder.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,61 @@
|
||||
using Matrix42.Extensions.Scaffolder.Core;
|
||||
using Matrix42.Extensions.Scaffolder.Core.Project;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.ConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var config = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory()) // required for appsettings.json
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
.Build();
|
||||
|
||||
var extensionPath = config["Scaffolder:ExtensionPath"];
|
||||
var extensionIdStr = config["Scaffolder:ExtensionId"];
|
||||
var extensionNamespace = config["Scaffolder:ExtensionNamespace"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(extensionPath))
|
||||
throw new Exception("Scaffolder:ExtensionPath is missing in appsettings.json");
|
||||
|
||||
var extensionId = Guid.TryParse(extensionIdStr, out var gid) ? gid : Guid.NewGuid();
|
||||
|
||||
var options = new ExtensionScaffoldingOptions
|
||||
{
|
||||
ExtensionPath = extensionPath,
|
||||
ExtensionId = extensionId,
|
||||
ExtensionNamespace = extensionNamespace,
|
||||
Projects = [
|
||||
new ProjectDefinition
|
||||
{
|
||||
Prefix = "Services",
|
||||
Runtimes = RuntimeTargets.WinCore | RuntimeTargets.LinuxCore,
|
||||
Functionality = ProjectFunctionality.WebApi
|
||||
},
|
||||
|
||||
new ProjectDefinition
|
||||
{
|
||||
Prefix = "Contracts",
|
||||
Runtimes = RuntimeTargets.All,
|
||||
Functionality = ProjectFunctionality.Custom
|
||||
},
|
||||
new ProjectDefinition
|
||||
{
|
||||
Prefix = "BizLogic",
|
||||
Runtimes = RuntimeTargets.All,
|
||||
Functionality = ProjectFunctionality.Custom
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var scaffolder = new ExtensionScaffolder();
|
||||
var projects = scaffolder.Generate(options);
|
||||
|
||||
Console.WriteLine("Generated projects:");
|
||||
foreach (var p in projects)
|
||||
Console.WriteLine(" - " + p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Scaffolder": {
|
||||
"ExtensionPath": "C:\\Projects\\Matrix42\\Titan_Linux\\ESM-FieldServiceManagement-Extension_demo",
|
||||
"ExtensionId": "c55407f9-9b46-4483-8b65-1bc1c3827efc",
|
||||
"ExtensionNamespace": "Matrix42.FieldServiceManagement"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!--
|
||||
Layout assumptions
|
||||
|
||||
This .targets file lives in:
|
||||
{ExtensionPath}\Extension.Assemblies.targets
|
||||
|
||||
We want to copy to:
|
||||
BasePackage\Assemblies\
|
||||
→ {ExtensionPath}\BasePackage\Assemblies\
|
||||
|
||||
Root\InstalledPackages\Assemblies\{ExtensionId}\
|
||||
→ {ExtensionPath}\..\Root\InstalledPackages\Assemblies\{ExtensionId}\
|
||||
|
||||
Properties:
|
||||
- M42BasePackageAssembliesDir (can override in csproj)
|
||||
- M42RootAssembliesDir (needs M42ExtensionId)
|
||||
- M42AssemblyPattern (default: $(MSBuildProjectName)*.*)
|
||||
- M42DefaultRids (default: win-x64;linux-x64)
|
||||
- M42ExtensionId (set in csproj)
|
||||
- M42EnableRidBuild (true for net8.0 w/ RIDs, false for netstandard)
|
||||
-->
|
||||
<PropertyGroup>
|
||||
<!-- BasePackage under extension root -->
|
||||
<M42BasePackageAssembliesDir Condition="'$(M42BasePackageAssembliesDir)' == ''">$(MSBuildThisFileDirectory)BasePackage\Assemblies\</M42BasePackageAssembliesDir>
|
||||
|
||||
<!-- Root one level above extension root -->
|
||||
<M42RootAssembliesDir Condition="'$(M42RootAssembliesDir)' == '' AND '$(M42ExtensionId)' != ''">$(MSBuildThisFileDirectory)..\Root\InstalledPackages\Assemblies\$(M42ExtensionId)\</M42RootAssembliesDir>
|
||||
|
||||
<!-- Pattern & default RIDs -->
|
||||
<M42AssemblyPattern Condition="'$(M42AssemblyPattern)' == ''">$(MSBuildProjectName)*.*</M42AssemblyPattern>
|
||||
<M42DefaultRids Condition="'$(M42DefaultRids)' == ''">win-x64;linux-x64</M42DefaultRids>
|
||||
|
||||
<!-- Enable multi-RID builds per project; can be set to false in csproj -->
|
||||
<M42EnableRidBuild Condition="'$(M42EnableRidBuild)' == ''">true</M42EnableRidBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<!--
|
||||
M42_BuildAllRids
|
||||
|
||||
Local only: if no RID was specified AND we're not in Azure DevOps,
|
||||
build all default RIDs (M42DefaultRids) by re-invoking MSBuild
|
||||
with RuntimeIdentifier and AppendRuntimeIdentifierToOutputPath=true.
|
||||
|
||||
This prevents the outer RID-less build by setting SkipBuild=true.
|
||||
|
||||
Skipped when M42EnableRidBuild=false (e.g. netstandard projects).
|
||||
-->
|
||||
<Target Name="M42_BuildAllRids"
|
||||
BeforeTargets="Build"
|
||||
Condition="'$(RuntimeIdentifier)' == ''
|
||||
AND ( '$(TF_BUILD)' != 'True' OR '$(M42BuildAllRidsOnCI)' == 'true' )
|
||||
AND '$(M42EnableRidBuild)' == 'true'">
|
||||
|
||||
<ItemGroup>
|
||||
<_M42RidList Include="$(M42DefaultRids)" />
|
||||
</ItemGroup>
|
||||
|
||||
<MSBuild Projects="$(MSBuildProjectFullPath)"
|
||||
Targets="Restore;Build"
|
||||
Properties="RuntimeIdentifier=%(_M42RidList.Identity);Configuration=$(Configuration);AppendRuntimeIdentifierToOutputPath=true"
|
||||
BuildInParallel="true" />
|
||||
|
||||
<PropertyGroup>
|
||||
<SkipBuild>true</SkipBuild>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
|
||||
<!--
|
||||
M42_PostBuild_CopyRidless
|
||||
|
||||
For RID-less builds (RuntimeIdentifier == ''), copy top-level output files
|
||||
that match M42AssemblyPattern directly from $(TargetDir) to:
|
||||
|
||||
1) BasePackage\Assemblies\
|
||||
2) Root\InstalledPackages\Assemblies\{ExtId}\ (if configured)
|
||||
|
||||
Useful for projects that don't support RID-specific builds (e.g. netstandard),
|
||||
or when M42EnableRidBuild=false.
|
||||
-->
|
||||
<Target Name="M42_PostBuild_CopyRidless"
|
||||
AfterTargets="Build"
|
||||
Condition="'$(RuntimeIdentifier)' == ''">
|
||||
|
||||
<Message Text="M42: RID-less copy from $(TargetDir) → $(M42BasePackageAssembliesDir)" Importance="high" />
|
||||
|
||||
<ItemGroup>
|
||||
<_M42FlatRootRidless Include="$(TargetDir)$(M42AssemblyPattern)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- BasePackage flat copy -->
|
||||
<MakeDir Directories="$(M42BasePackageAssembliesDir)"
|
||||
Condition="!Exists('$(M42BasePackageAssembliesDir)')" />
|
||||
|
||||
<Copy
|
||||
SourceFiles="@(_M42FlatRootRidless)"
|
||||
DestinationFolder="$(M42BasePackageAssembliesDir)"
|
||||
SkipUnchangedFiles="true"
|
||||
Condition="Exists('$(TargetDir)')" />
|
||||
|
||||
<!-- Root flat copy (if configured) -->
|
||||
<Message Text="M42: RID-less copy to Root → $(M42RootAssembliesDir)" Importance="high"
|
||||
Condition="'$(M42RootAssembliesDir)' != ''" />
|
||||
|
||||
<MakeDir Directories="$(M42RootAssembliesDir)"
|
||||
Condition="'$(M42RootAssembliesDir)' != '' AND !Exists('$(M42RootAssembliesDir)')" />
|
||||
|
||||
<Copy
|
||||
SourceFiles="@(_M42FlatRootRidless)"
|
||||
DestinationFolder="$(M42RootAssembliesDir)"
|
||||
SkipUnchangedFiles="true"
|
||||
Condition="'$(M42RootAssembliesDir)' != '' AND Exists('$(TargetDir)')" />
|
||||
|
||||
</Target>
|
||||
|
||||
|
||||
<!--
|
||||
M42_PostBuild_CopyRID
|
||||
|
||||
After each RID-specific build, copy assemblies to:
|
||||
|
||||
1) BasePackage\Assemblies\ (FLAT, from RID-less bin\...\netX\)
|
||||
2) BasePackage\Assemblies\<RID>\... (RECURSIVE, from current RID build)
|
||||
|
||||
And, if M42RootAssembliesDir is set:
|
||||
|
||||
3) Root\InstalledPackages\Assemblies\{ExtId}\ (FLAT)
|
||||
4) Root\InstalledPackages\Assemblies\{ExtId}\<RID>\ (RECURSIVE)
|
||||
-->
|
||||
<Target Name="M42_PostBuild_CopyRID"
|
||||
AfterTargets="Build"
|
||||
Condition="'$(RuntimeIdentifier)' != ''">
|
||||
|
||||
<Message Text="M42: RID=$(RuntimeIdentifier) TargetDir=$(TargetDir)" Importance="high" />
|
||||
|
||||
<!-- Parent of ...\netX\<rid>\ -->
|
||||
<PropertyGroup>
|
||||
<M42_RidlessTargetDir>$([System.IO.Path]::GetFullPath('$(TargetDir)..\'))</M42_RidlessTargetDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- 1) FLAT copy from rid-less netX to BasePackage\Assemblies -->
|
||||
<Message Text="M42: flat copy from $(M42_RidlessTargetDir) → $(M42BasePackageAssembliesDir)" Importance="high" />
|
||||
|
||||
<ItemGroup>
|
||||
<_M42FlatRoot Include="$(M42_RidlessTargetDir)$(M42AssemblyPattern)" />
|
||||
</ItemGroup>
|
||||
|
||||
<MakeDir Directories="$(M42BasePackageAssembliesDir)"
|
||||
Condition="!Exists('$(M42BasePackageAssembliesDir)')" />
|
||||
|
||||
<Copy
|
||||
SourceFiles="@(_M42FlatRoot)"
|
||||
DestinationFolder="$(M42BasePackageAssembliesDir)"
|
||||
SkipUnchangedFiles="true"
|
||||
Condition="Exists('$(M42_RidlessTargetDir)')" />
|
||||
|
||||
|
||||
<!-- 1b) FLAT copy to Root if configured -->
|
||||
<Message Text="M42: flat copy to Root → $(M42RootAssembliesDir)" Importance="high"
|
||||
Condition="'$(M42RootAssembliesDir)' != ''" />
|
||||
|
||||
<MakeDir Directories="$(M42RootAssembliesDir)"
|
||||
Condition="'$(M42RootAssembliesDir)' != '' AND !Exists('$(M42RootAssembliesDir)')" />
|
||||
|
||||
<Copy
|
||||
SourceFiles="@(_M42FlatRoot)"
|
||||
DestinationFolder="$(M42RootAssembliesDir)"
|
||||
SkipUnchangedFiles="true"
|
||||
Condition="'$(M42RootAssembliesDir)' != '' AND Exists('$(M42_RidlessTargetDir)')" />
|
||||
|
||||
|
||||
<!-- 2) RID copy to BasePackage\Assemblies\<RID>\... -->
|
||||
<Message Text="M42: RID copy → $(M42BasePackageAssembliesDir)$(RuntimeIdentifier)\" Importance="high" />
|
||||
|
||||
<ItemGroup>
|
||||
<_M42Rid Include="$(TargetDir)**\$(M42AssemblyPattern)" />
|
||||
</ItemGroup>
|
||||
|
||||
<MakeDir Directories="$(M42BasePackageAssembliesDir)$(RuntimeIdentifier)"
|
||||
Condition="!Exists('$(M42BasePackageAssembliesDir)$(RuntimeIdentifier)')" />
|
||||
|
||||
<Copy
|
||||
SourceFiles="@(_M42Rid)"
|
||||
DestinationFolder="$(M42BasePackageAssembliesDir)$(RuntimeIdentifier)\%(RecursiveDir)"
|
||||
SkipUnchangedFiles="true" />
|
||||
|
||||
|
||||
<!-- 2b) RID copy to Root\...\<RID>\... -->
|
||||
<Message Text="M42: RID copy to Root → $(M42RootAssembliesDir)$(RuntimeIdentifier)\" Importance="high"
|
||||
Condition="'$(M42RootAssembliesDir)' != ''" />
|
||||
|
||||
<MakeDir Directories="$(M42RootAssembliesDir)$(RuntimeIdentifier)"
|
||||
Condition="'$(M42RootAssembliesDir)' != '' AND !Exists('$(M42RootAssembliesDir)$(RuntimeIdentifier)')" />
|
||||
|
||||
<Copy
|
||||
SourceFiles="@(_M42Rid)"
|
||||
DestinationFolder="$(M42RootAssembliesDir)$(RuntimeIdentifier)\%(RecursiveDir)"
|
||||
SkipUnchangedFiles="true"
|
||||
Condition="'$(M42RootAssembliesDir)' != ''" />
|
||||
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,232 @@
|
||||
using Matrix42.Extensions.Scaffolder.Core.ProjectGenerators;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Core;
|
||||
|
||||
public sealed class ExtensionScaffolder : IExtensionScaffolder
|
||||
{
|
||||
private readonly DefaultProjectGenerator _projectGenerator = new();
|
||||
|
||||
public IReadOnlyList<string> Generate(ExtensionScaffoldingOptions options)
|
||||
{
|
||||
if (options is null)
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.ExtensionPath)) throw new ArgumentException("ExtensionPath must be set.", nameof(options));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.ExtensionNamespace)) throw new ArgumentException("ExtensionNamespace must be set.", nameof(options));
|
||||
|
||||
if (options.Projects == null) throw new ArgumentException("Projects must be set.", nameof(options));
|
||||
|
||||
var result = new List<string>();
|
||||
|
||||
PrepareRootStructure(options);
|
||||
PrepareBasePackage(options);
|
||||
var solutionDir = PrepareSolutionFolder(options);
|
||||
PrepareRootArtifacts(options);
|
||||
PrepareSolutionFiles(options, solutionDir);
|
||||
|
||||
foreach (var def in options.Projects)
|
||||
{
|
||||
var csprojPath = _projectGenerator.GenerateProject(options, def);
|
||||
result.Add(csprojPath);
|
||||
}
|
||||
|
||||
GenerateSolutionFile(options, solutionDir, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void PrepareRootStructure(ExtensionScaffoldingOptions options)
|
||||
{
|
||||
Directory.CreateDirectory(options.ExtensionPath);
|
||||
}
|
||||
|
||||
private void PrepareBasePackage(ExtensionScaffoldingOptions options)
|
||||
{
|
||||
var basePackageDir = Path.Combine(options.ExtensionPath, "BasePackage");
|
||||
Directory.CreateDirectory(basePackageDir);
|
||||
|
||||
var packageJsonPath = Path.Combine(basePackageDir, "package.json");
|
||||
if (!File.Exists(packageJsonPath))
|
||||
{
|
||||
var json = GetDefaultPackageJson(options);
|
||||
File.WriteAllText(packageJsonPath, json);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates /Solutions/{ns} and returns that path (solution-level folder).
|
||||
/// </summary>
|
||||
private string PrepareSolutionFolder(ExtensionScaffoldingOptions options)
|
||||
{
|
||||
var solutionDir = Path.Combine(options.ExtensionPath, "Solutions", options.ExtensionNamespace);
|
||||
Directory.CreateDirectory(solutionDir);
|
||||
return solutionDir;
|
||||
}
|
||||
|
||||
private void PrepareRootArtifacts(ExtensionScaffoldingOptions options)
|
||||
{
|
||||
/*var azurePath = Path.Combine(options.ExtensionPath, "azure-pipelines-ci.yml");
|
||||
if (!File.Exists(azurePath))
|
||||
{
|
||||
File.WriteAllText(azurePath, Templates.AzurePipelinesYml);
|
||||
}*/
|
||||
|
||||
var azurePath = Path.Combine(options.ExtensionPath, "azure-pipelines-ci.yaml");
|
||||
if (!File.Exists(azurePath))
|
||||
{
|
||||
var repoName = new DirectoryInfo(options.ExtensionPath).Name;
|
||||
|
||||
var content = Templates.GetAzurePiplineCi();
|
||||
var replacedContent = content.Replace("{repoName}", repoName).Replace("{ns}", options.ExtensionNamespace);
|
||||
File.WriteAllText(azurePath, replacedContent);
|
||||
}
|
||||
|
||||
var gitignorePath = Path.Combine(options.ExtensionPath, ".gitignore");
|
||||
if (!File.Exists(gitignorePath))
|
||||
{
|
||||
File.WriteAllText(gitignorePath, Templates.Gitignore);
|
||||
}
|
||||
|
||||
/*var veracodePath = Path.Combine(options.ExtensionPath, "PrepareVeracodeScan.cmd");
|
||||
if (!File.Exists(veracodePath))
|
||||
{
|
||||
var content = Templates.PrepareVeracodeScanCmd.Replace("{ns}", options.ExtensionNamespace);
|
||||
File.WriteAllText(veracodePath, content);
|
||||
}*/
|
||||
|
||||
var targetsPath = Path.Combine(options.ExtensionPath, "Extension.Assemblies.targets");
|
||||
if (!File.Exists(targetsPath))
|
||||
{
|
||||
var content = Templates.GetMatrix42AssembliesTargets();
|
||||
File.WriteAllText(targetsPath, content);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Solution-level files that should live next to the .sln (e.g. BuildEvent.cmd).
|
||||
/// </summary>
|
||||
private void PrepareSolutionFiles(ExtensionScaffoldingOptions options, string solutionDir)
|
||||
{
|
||||
var buildEventPath = Path.Combine(solutionDir, "BuildEvent.cmd");
|
||||
if (!File.Exists(buildEventPath))
|
||||
{
|
||||
var content = Templates.BuildEventCmdTemplate.Replace("{ns}", options.ExtensionNamespace);
|
||||
File.WriteAllText(buildEventPath, content);
|
||||
}
|
||||
|
||||
// If later you want to actually create the .sln here (for console scenario),
|
||||
// this is the right place as well.
|
||||
}
|
||||
|
||||
private static string GetDefaultPackageJson(ExtensionScaffoldingOptions options) =>
|
||||
$@"{{
|
||||
""Id"": ""{options.ExtensionId}"",
|
||||
""Vendor"": ""Not Specified"",
|
||||
""Name"": ""{options.ExtensionNamespace}"",
|
||||
""Version"": ""1.0"",
|
||||
""Prerequisites"": {{
|
||||
""MinimalRequiredProductVersion"": ""26.1.0"",
|
||||
""DependentPackages"": []
|
||||
}},
|
||||
""SetupDirectives"": {{
|
||||
""MaintenanceMode"": false,
|
||||
""RecycleWebApplication"": false,
|
||||
""RestartM42WindowsServices"": false
|
||||
}},
|
||||
""Sandboxed"": true
|
||||
}}";
|
||||
|
||||
/// <summary>
|
||||
/// Creates {ns}.sln under solutionDir and adds all csproj files to it.
|
||||
/// </summary>
|
||||
private void GenerateSolutionFile(
|
||||
ExtensionScaffoldingOptions options,
|
||||
string solutionDir,
|
||||
IReadOnlyList<string> projectPaths)
|
||||
{
|
||||
if (projectPaths == null || projectPaths.Count == 0)
|
||||
return;
|
||||
|
||||
var solutionName = options.ExtensionNamespace;
|
||||
var slnPath = Path.Combine(solutionDir, solutionName + ".sln");
|
||||
|
||||
// Basic VS solution header
|
||||
var lines = new List<string>();
|
||||
lines.Add("Microsoft Visual Studio Solution File, Format Version 12.00");
|
||||
lines.Add("# Visual Studio Version 17");
|
||||
lines.Add("VisualStudioVersion = 17.0.31912.275");
|
||||
lines.Add("MinimumVisualStudioVersion = 10.0.40219.1");
|
||||
|
||||
// C# project type GUID
|
||||
const string csProjTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
|
||||
|
||||
// Collect project GUIDs
|
||||
var projectEntries = new List<Tuple<string, string, string>>();
|
||||
// (projGuid, projName, relativePath)
|
||||
|
||||
foreach (var projPath in projectPaths)
|
||||
{
|
||||
var projName = Path.GetFileNameWithoutExtension(projPath);
|
||||
var relativePath = GetRelativePath(solutionDir, projPath);
|
||||
var projGuid = "{" + Guid.NewGuid().ToString().ToUpperInvariant() + "}";
|
||||
|
||||
projectEntries.Add(Tuple.Create(projGuid, projName, relativePath));
|
||||
|
||||
lines.Add(string.Format(
|
||||
"Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\"",
|
||||
csProjTypeGuid,
|
||||
projName,
|
||||
relativePath,
|
||||
projGuid));
|
||||
lines.Add("EndProject");
|
||||
}
|
||||
|
||||
// Global section (configs + project configs)
|
||||
lines.Add("Global");
|
||||
lines.Add("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution");
|
||||
lines.Add("\t\tDebug|Any CPU = Debug|Any CPU");
|
||||
lines.Add("\t\tRelease|Any CPU = Release|Any CPU");
|
||||
lines.Add("\tEndGlobalSection");
|
||||
|
||||
lines.Add("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
|
||||
foreach (var entry in projectEntries)
|
||||
{
|
||||
var guid = entry.Item1;
|
||||
lines.Add(string.Format("\t\t{0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU", guid));
|
||||
lines.Add(string.Format("\t\t{0}.Debug|Any CPU.Build.0 = Debug|Any CPU", guid));
|
||||
lines.Add(string.Format("\t\t{0}.Release|Any CPU.ActiveCfg = Release|Any CPU", guid));
|
||||
lines.Add(string.Format("\t\t{0}.Release|Any CPU.Build.0 = Release|Any CPU", guid));
|
||||
}
|
||||
lines.Add("\tEndGlobalSection");
|
||||
|
||||
lines.Add("\tGlobalSection(SolutionProperties) = preSolution");
|
||||
lines.Add("\t\tHideSolutionNode = FALSE");
|
||||
lines.Add("\tEndGlobalSection");
|
||||
|
||||
lines.Add("EndGlobal");
|
||||
|
||||
File.WriteAllLines(slnPath, lines);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple helper to get relative path from one folder to a file.
|
||||
/// </summary>
|
||||
private static string GetRelativePath(string basePath, string fullPath)
|
||||
{
|
||||
var baseUri = new Uri(AppendDirectorySeparatorChar(basePath));
|
||||
var fullUri = new Uri(fullPath);
|
||||
var relative = baseUri.MakeRelativeUri(fullUri).ToString();
|
||||
|
||||
return relative.Replace('/', Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
private static string AppendDirectorySeparatorChar(string path)
|
||||
{
|
||||
return !path.EndsWith(Path.DirectorySeparatorChar.ToString()) ? path + Path.DirectorySeparatorChar : path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Matrix42.Extensions.Scaffolder.Core.Project;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Core
|
||||
{
|
||||
public sealed class ExtensionScaffoldingOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Absolute path where scaffolding should be created.
|
||||
/// Typically solution directory from VS or some folder from CLI.
|
||||
/// </summary>
|
||||
public string ExtensionPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Extension id (Guid) – used in package.json or other metadata.
|
||||
/// </summary>
|
||||
public Guid ExtensionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Root namespace, e.g. "Matrix42.MyAwesomeExtension".
|
||||
/// </summary>
|
||||
public string ExtensionNamespace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Projects to create.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ProjectDefinition> Projects { get; set; } = Array.Empty<ProjectDefinition>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Matrix42.Extensions.Scaffolder.Core.Project;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Core.Functionality
|
||||
{
|
||||
public interface IProjectFunctionalityDefinition
|
||||
{
|
||||
ProjectFunctionality Kind { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Files to create for this functionality (relative paths, template content).
|
||||
/// </summary>
|
||||
IEnumerable<TemplateFile> GetFiles(ExtensionScaffoldingOptions options, ProjectDefinition project);
|
||||
|
||||
/// <summary>
|
||||
/// References to add to the csproj.
|
||||
/// </summary>
|
||||
IEnumerable<ProjectReferenceDefinition> GetReferences(ExtensionScaffoldingOptions options, ProjectDefinition project);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using Matrix42.Extensions.Scaffolder.Core.Project;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Core.Functionality
|
||||
{
|
||||
public sealed class WebApiFunctionalityDefinition : IProjectFunctionalityDefinition
|
||||
{
|
||||
public ProjectFunctionality Kind => ProjectFunctionality.WebApi;
|
||||
|
||||
public IEnumerable<TemplateFile> GetFiles(
|
||||
ExtensionScaffoldingOptions options,
|
||||
ProjectDefinition project)
|
||||
{
|
||||
var ns = options.ExtensionNamespace;
|
||||
|
||||
yield return new TemplateFile
|
||||
{
|
||||
RelativePath = @"Controllers\TestController.cs",
|
||||
Content = WebApi_TestControllerTemplate.Replace("{ns}", ns),
|
||||
CreateIfMissingOnly = true
|
||||
};
|
||||
}
|
||||
|
||||
public IEnumerable<ProjectReferenceDefinition> GetReferences(ExtensionScaffoldingOptions options, ProjectDefinition project)
|
||||
{
|
||||
yield return new ProjectReferenceDefinition
|
||||
{
|
||||
Include = "Matrix42.WebApi.Contracts",
|
||||
HintPath = @"..\..\..\..\Root\bin\Matrix42.WebApi.Contracts.dll"
|
||||
};
|
||||
|
||||
yield return new ProjectReferenceDefinition
|
||||
{
|
||||
Include = "Matrix42.Hosting.Contracts",
|
||||
HintPath = @"..\..\..\..\Root\bin\Matrix42.Hosting.Contracts.dll"
|
||||
};
|
||||
}
|
||||
|
||||
private const string WebApi_TestControllerTemplate =
|
||||
@"
|
||||
using Matrix42.WebApi.Contracts;
|
||||
using Matrix42.Hosting.Contracts;
|
||||
|
||||
namespace {ns}.Services
|
||||
{
|
||||
[RoutePrefix(""api/test"")]
|
||||
public class TestController : ApiController
|
||||
{
|
||||
private readonly IDependencyResolver _resolver;
|
||||
|
||||
public TestController(IDependencyResolver resolver)
|
||||
{
|
||||
_resolver = resolver;
|
||||
}
|
||||
|
||||
[HttpPost, Route(""test"")]
|
||||
public string TestMethod()
|
||||
{
|
||||
// TODO: implement your test method logic
|
||||
return ""OK"";
|
||||
}
|
||||
}
|
||||
}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Core;
|
||||
|
||||
public interface IExtensionScaffolder
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates all files/folders according to options and returns the list of generated csproj paths.
|
||||
/// </summary>
|
||||
IReadOnlyList<string> Generate(ExtensionScaffoldingOptions options);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>11.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Extension.Assemblies.targets" />
|
||||
<EmbeddedResource Include="azure-pipelines-ci.yaml" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Core.Project;
|
||||
|
||||
public class ProjectDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// E.g. "Services", "BizLogic", etc.
|
||||
/// If empty, project name will be equal to ExtensionNamespace.
|
||||
/// </summary>
|
||||
public string Prefix { get; set; } = string.Empty;
|
||||
|
||||
public RuntimeTargets Runtimes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Combination of functionality flags: WebApi, Engine, Behavior, Connector, Custom.
|
||||
/// </summary>
|
||||
public ProjectFunctionality Functionality { get; set; } = ProjectFunctionality.None;
|
||||
|
||||
public string Display => $"Prefix: {Prefix} - Runtimes: {Runtimes} - Functionality: {Functionality}";
|
||||
|
||||
public string GetProjectName(string extensionNamespace)
|
||||
{
|
||||
var pfx = Prefix?.Trim();
|
||||
return string.IsNullOrWhiteSpace(pfx)
|
||||
? extensionNamespace
|
||||
: $"{extensionNamespace}.{pfx}";
|
||||
}
|
||||
|
||||
public string GetNamespace(string extensionNamespace) => GetProjectName(extensionNamespace);
|
||||
|
||||
public bool DoesSupportAllRuntimes() => Runtimes.HasFlag(RuntimeTargets.All);
|
||||
|
||||
public string GetRuntimeIdentifiers()
|
||||
{
|
||||
if (DoesSupportAllRuntimes())
|
||||
{
|
||||
return "netstandard2.0";
|
||||
}
|
||||
|
||||
var list = new List<string>();
|
||||
if (Runtimes.HasFlag(RuntimeTargets.Win) || Runtimes.HasFlag(RuntimeTargets.WinCore))
|
||||
{
|
||||
list.Add("win-x64");
|
||||
}
|
||||
|
||||
if (Runtimes.HasFlag(RuntimeTargets.LinuxCore))
|
||||
{
|
||||
list.Add("linux-x64");
|
||||
}
|
||||
|
||||
return string.Join(";", list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Core.Project;
|
||||
|
||||
[Flags]
|
||||
public enum ProjectFunctionality
|
||||
{
|
||||
None = 0,
|
||||
WebApi = 1,
|
||||
Engine = 2,
|
||||
Behavior = 4,
|
||||
Connector = 8,
|
||||
Custom = 16
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum RuntimeTargets
|
||||
{
|
||||
Win = 1,
|
||||
WinCore = 2,
|
||||
LinuxCore = 4,
|
||||
All = 8
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Matrix42.Extensions.Scaffolder.Core.Project
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a single extra reference to put in the csproj.
|
||||
/// </summary>
|
||||
public sealed class ProjectReferenceDefinition
|
||||
{
|
||||
public string Include { get; set; }
|
||||
public string HintPath { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
using Matrix42.Extensions.Scaffolder.Core.Functionality;
|
||||
using Matrix42.Extensions.Scaffolder.Core.Project;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Core.ProjectGenerators
|
||||
{
|
||||
internal class DefaultProjectGenerator
|
||||
{
|
||||
private readonly IReadOnlyList<IProjectFunctionalityDefinition> _functionalityDefinitions;
|
||||
|
||||
public DefaultProjectGenerator()
|
||||
{
|
||||
_functionalityDefinitions = new IProjectFunctionalityDefinition[]
|
||||
{
|
||||
new WebApiFunctionalityDefinition(),
|
||||
// new EngineFunctionalityDefinition(),
|
||||
// new BehaviorFunctionalityDefinition(),
|
||||
// new ConnectorFunctionalityDefinition(),
|
||||
// new CustomFunctionalityDefinition(),
|
||||
};
|
||||
}
|
||||
|
||||
public string GenerateProject(ExtensionScaffoldingOptions options, ProjectDefinition def)
|
||||
{
|
||||
var ns = options.ExtensionNamespace;
|
||||
var projectName = def.GetProjectName(ns); // e.g. Matrix42.MyExt.Services
|
||||
|
||||
var solutionDir = Path.Combine(options.ExtensionPath, "Solutions", options.ExtensionNamespace);
|
||||
Directory.CreateDirectory(solutionDir);
|
||||
|
||||
var projectDir = Path.Combine(solutionDir, projectName);
|
||||
Directory.CreateDirectory(projectDir);
|
||||
|
||||
var csprojPath = Path.Combine(projectDir, $"{projectName}.csproj");
|
||||
|
||||
GenerateCsproj(csprojPath, def, options, projectName, ns, CollectFunctionalityReferences(options, def));
|
||||
GenerateHostFile(projectDir, projectName);
|
||||
GenerateFiles(projectDir, CollectFunctionalityFiles(options, def));
|
||||
|
||||
return csprojPath;
|
||||
}
|
||||
|
||||
#region Csproj / Host
|
||||
|
||||
protected virtual void GenerateCsproj(
|
||||
string csprojPath,
|
||||
ProjectDefinition def,
|
||||
ExtensionScaffoldingOptions options,
|
||||
string projectName,
|
||||
string rootNamespace,
|
||||
IReadOnlyList<ProjectReferenceDefinition> extraReferences)
|
||||
{
|
||||
var allRuntimes = def.DoesSupportAllRuntimes();
|
||||
|
||||
var tfm = allRuntimes ? def.GetRuntimeIdentifiers() : "net8.0"; // Todo: dynamic based on runtimes
|
||||
var runtimeIds = def.GetRuntimeIdentifiers();
|
||||
|
||||
var anyRids = !allRuntimes && !string.IsNullOrWhiteSpace(runtimeIds);
|
||||
var ridBlock = anyRids ? $"<RuntimeIdentifiers>{runtimeIds}</RuntimeIdentifiers>{Environment.NewLine}" : string.Empty;
|
||||
|
||||
var referencesXml = BuildReferencesXml(extraReferences);
|
||||
|
||||
var content =
|
||||
$@"<Project Sdk=""Microsoft.NET.Sdk"">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>{tfm}</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>{rootNamespace}</RootNamespace>
|
||||
{ridBlock}
|
||||
<AppendRuntimeIdentifierToOutputPath>true</AppendRuntimeIdentifierToOutputPath>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>latestmajor</LangVersion>
|
||||
<BasePackageAssembliesDir>$(MSBuildProjectDirectory)\..\..\..\BasePackage\Assemblies\</BasePackageAssembliesDir>
|
||||
</PropertyGroup>
|
||||
|
||||
{referencesXml}
|
||||
|
||||
<PropertyGroup>
|
||||
<M42ExtensionId>{options.ExtensionId}</M42ExtensionId>
|
||||
<M42AssemblyPattern>$(MSBuildProjectName)*.*</M42AssemblyPattern>
|
||||
<M42EnableRidBuild>{anyRids.ToString().ToLowerInvariant()}</M42EnableRidBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project=""..\..\..\Extension.Assemblies.targets"" Condition=""Exists('..\..\..\Extension.Assemblies.targets')"" />
|
||||
</Project>
|
||||
";
|
||||
|
||||
File.WriteAllText(csprojPath, content);
|
||||
}
|
||||
|
||||
protected virtual void GenerateHostFile(string projectDir, string projectName)
|
||||
{
|
||||
var hostFileName = $"{projectName}.dll.host";
|
||||
var hostPath = Path.Combine(projectDir, hostFileName);
|
||||
|
||||
if (!File.Exists(hostPath))
|
||||
{
|
||||
File.WriteAllText(hostPath, Templates.DefaultHostFileContent);
|
||||
}
|
||||
}
|
||||
|
||||
private static string BuildReferencesXml(IEnumerable<ProjectReferenceDefinition> refs)
|
||||
{
|
||||
var list = refs?.ToList() ?? new List<ProjectReferenceDefinition>();
|
||||
if (list.Count == 0)
|
||||
return string.Empty;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(" <ItemGroup>");
|
||||
|
||||
foreach (var r in list)
|
||||
{
|
||||
sb.AppendLine($@" <Reference Include=""{r.Include}"">");
|
||||
if (!string.IsNullOrWhiteSpace(r.HintPath))
|
||||
{
|
||||
sb.AppendLine($@" <HintPath>{r.HintPath}</HintPath>");
|
||||
}
|
||||
sb.AppendLine(" </Reference>");
|
||||
}
|
||||
|
||||
sb.AppendLine(" </ItemGroup>");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functionality integration (files + references)
|
||||
|
||||
/// <summary>
|
||||
/// Collects files to generate from all enabled functionalities.
|
||||
/// </summary>
|
||||
protected virtual IReadOnlyList<TemplateFile> CollectFunctionalityFiles(
|
||||
ExtensionScaffoldingOptions options, ProjectDefinition def)
|
||||
{
|
||||
var result = new List<TemplateFile>();
|
||||
var flags = def.Functionality;
|
||||
|
||||
foreach (var defn in _functionalityDefinitions)
|
||||
{
|
||||
if (!flags.HasFlag(defn.Kind))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.AddRange(defn.GetFiles(options, def));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects references to add to csproj from all enabled functionalities.
|
||||
/// </summary>
|
||||
protected virtual IReadOnlyList<ProjectReferenceDefinition> CollectFunctionalityReferences(
|
||||
ExtensionScaffoldingOptions options, ProjectDefinition def)
|
||||
{
|
||||
var result = new List<ProjectReferenceDefinition>();
|
||||
var flags = def.Functionality;
|
||||
|
||||
foreach (var defn in _functionalityDefinitions)
|
||||
{
|
||||
if (!flags.HasFlag(defn.Kind))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.AddRange(defn.GetReferences(options, def));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected virtual void GenerateFiles(string projectDir, IReadOnlyList<TemplateFile> files)
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
var targetPath = Path.Combine(projectDir, file.RelativePath);
|
||||
var targetDir = Path.GetDirectoryName(targetPath);
|
||||
if (!string.IsNullOrEmpty(targetDir))
|
||||
{
|
||||
Directory.CreateDirectory(targetDir);
|
||||
}
|
||||
|
||||
if (file.CreateIfMissingOnly && File.Exists(targetPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
File.WriteAllText(targetPath, file.Content);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Matrix42.Extensions.Scaffolder.Core.Project;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Core.ProjectGenerators;
|
||||
|
||||
internal interface IProjectGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates the project and returns the path to the created .csproj file.
|
||||
/// </summary>
|
||||
string GenerateProject(ExtensionScaffoldingOptions options, ProjectDefinition def);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Matrix42.Extensions.Scaffolder.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a single file to generate from a template.
|
||||
/// </summary>
|
||||
public sealed class TemplateFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Path relative to project folder (for project files)
|
||||
/// or ExtensionPath (for root-level files).
|
||||
/// Example: "Controllers\\TestController.cs" or "BuildEvent.cmd".
|
||||
/// </summary>
|
||||
public string RelativePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Template content (with tokens like {ns}, {projectName} etc.).
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, file is only created if it doesn't exist.
|
||||
/// If false, it will be overwritten.
|
||||
/// </summary>
|
||||
public bool CreateIfMissingOnly { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Core
|
||||
{
|
||||
internal static class Templates
|
||||
{
|
||||
public const string Gitignore = @"
|
||||
.idea/
|
||||
.vs/
|
||||
.vscode/
|
||||
node_modules/
|
||||
dist/
|
||||
|
||||
package-lock.json
|
||||
workspace.json
|
||||
|
||||
.vs/
|
||||
.libs/
|
||||
.build/
|
||||
|
||||
bin/
|
||||
obj/
|
||||
|
||||
*.user
|
||||
|
||||
/**/AssemblyInfoProduct.cs
|
||||
/**/_Resharper.Caches
|
||||
/Solutions/*.sln.DotSettings.user
|
||||
/Solutions/**/*.csproj.user
|
||||
/Solutions/**/packages/*.*
|
||||
/Solutions/**/**/AssemblyInfoProduct.cs
|
||||
/Solutions/TestResult/*.*
|
||||
|
||||
BasePackage/Assemblies/
|
||||
";
|
||||
|
||||
public const string PrepareVeracodeScanCmd = @"set arch=""C:\Program Files\7-Zip\7z.exe""
|
||||
echo [*] Building backend archive...
|
||||
%arch% a -tzip -mx5 -x!*.config %Build_ArtifactStagingDirectory%\{ns}.zip %BaseDir%\Solutions\{ns}\.build\*.*
|
||||
";
|
||||
|
||||
public const string DefaultHostFileContent =
|
||||
@"<?xml version=""1.0"" encoding=""utf-8"" ?>
|
||||
<host xmlns=""urn:m42/host.config"">
|
||||
<modules></modules>
|
||||
<sections></sections>
|
||||
</host>
|
||||
";
|
||||
|
||||
public const string BuildEventCmdTemplate =
|
||||
@"call %~dps0..\..\TeamBuildEvent.cmd %1 %2 %3 ""/Solutions/{ns}""";
|
||||
|
||||
internal static string GetMatrix42AssembliesTargets()
|
||||
{
|
||||
var asm = typeof(Templates).Assembly;
|
||||
const string resourceName = "Matrix42.Extensions.Scaffolder.Core.Extension.Assemblies.targets";
|
||||
|
||||
using (var stream = asm.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new FileNotFoundException($"Embedded resource '{resourceName}' not found in assembly '{asm.FullName}'.");
|
||||
}
|
||||
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetAzurePiplineCi()
|
||||
{
|
||||
var asm = typeof(Templates).Assembly;
|
||||
const string resourceName = "Matrix42.Extensions.Scaffolder.Core.azure-pipelines-ci.yaml";
|
||||
|
||||
using (var stream = asm.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new FileNotFoundException($"Embedded resource '{resourceName}' not found in assembly '{asm.FullName}'.");
|
||||
}
|
||||
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
# azure-pipelines.yml
|
||||
pr: none
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- release_26_1_0
|
||||
paths:
|
||||
include:
|
||||
- 'Solutions/**'
|
||||
|
||||
parameters:
|
||||
- name: BaseDir
|
||||
type: string
|
||||
default: $(Build.SourcesDirectory)
|
||||
|
||||
- name: SuiteRarShare
|
||||
type: string
|
||||
default: '\\m42-sto-ffm01.matrix42.de\ServiceStore\Builds\TitanLinux Continuous'
|
||||
|
||||
- name: RepoName
|
||||
type: string
|
||||
default: '{repoName}'
|
||||
|
||||
- name: SolutionName
|
||||
type: string
|
||||
default: '{ns}'
|
||||
|
||||
variables:
|
||||
- name: BaseDir
|
||||
value: ${{ parameters.BaseDir }}
|
||||
|
||||
- name: ExtensionTarget
|
||||
value: ${{ parameters.BaseDir }}/${{ parameters.RepoName }}
|
||||
|
||||
- name: SolutionDir
|
||||
value: $(ExtensionTarget)/Solutions/{ns}
|
||||
|
||||
- name: SolutionPath
|
||||
value: $(SolutionDir)/{ns}.sln
|
||||
|
||||
# Tests
|
||||
- name: TestsDir
|
||||
value: $(SolutionDir)
|
||||
- name: TestProject
|
||||
value: '{ns}.Tests/{ns}.Tests.csproj'
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
displayName: 'Build ${{ parameters.RepoName }}'
|
||||
jobs:
|
||||
- job: Build_Backend
|
||||
displayName: 'Build, Test, Package'
|
||||
pool:
|
||||
name: M42BPM
|
||||
demands:
|
||||
- DotNetFramework
|
||||
- vstest
|
||||
|
||||
steps:
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy extension $(ExtensionTarget) source files'
|
||||
inputs:
|
||||
SourceFolder: '${{ parameters.BaseDir }}'
|
||||
Contents: '**'
|
||||
TargetFolder: '$(ExtensionTarget)'
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy suite.rar'
|
||||
inputs:
|
||||
SourceFolder: '${{ parameters.SuiteRarShare }}'
|
||||
Contents: 'suite.rar'
|
||||
TargetFolder: '${{ parameters.BaseDir }}'
|
||||
OverWrite: true
|
||||
|
||||
- task: ExtractFiles@1
|
||||
displayName: 'Extract suite.rar'
|
||||
inputs:
|
||||
archiveFilePatterns: '${{ parameters.BaseDir }}/suite.rar'
|
||||
destinationFolder: '${{ parameters.BaseDir }}'
|
||||
cleanDestinationFolder: false
|
||||
overwriteExistingFiles: true
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Restore $(ExtensionTarget) solution'
|
||||
inputs:
|
||||
command: 'restore'
|
||||
projects: '$(SolutionPath)'
|
||||
|
||||
# Optional: override default RIDs globally if needed: -p:M42DefaultRids=win-x64;linux-x64
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Build {ns} solution'
|
||||
inputs:
|
||||
command: 'build'
|
||||
projects: '$(SolutionPath)'
|
||||
arguments: >
|
||||
-c Release
|
||||
--no-restore
|
||||
-p:SolutionDir=$(SolutionDir)\
|
||||
-p:M42BuildAllRidsOnCI=true
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Test (net8.0)'
|
||||
inputs:
|
||||
command: 'test'
|
||||
projects: '$(TestsDir)/$(TestProject)'
|
||||
arguments: '-c Release -f net8.0 --no-build'
|
||||
|
||||
- task: ArchiveFiles@2
|
||||
displayName: 'Zip $(ExtensionTarget) BasePackage'
|
||||
inputs:
|
||||
rootFolderOrFile: '$(ExtensionTarget)/BasePackage'
|
||||
includeRootFolder: false
|
||||
archiveType: 'zip'
|
||||
sevenZipCompression: 'normal'
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/${{ parameters.RepoName }}_1.0.zip'
|
||||
replaceExistingArchive: true
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: 'Publish ${{ parameters.RepoName }}_1.0.zip'
|
||||
inputs:
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)/${{ parameters.RepoName }}_1.0.zip'
|
||||
artifact: '${{ parameters.RepoName }}'
|
||||
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<MinimumVisualStudioVersion>17.0</MinimumVisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<ProjectGuid>{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Matrix42.Extensions.Scaffolder.Vsix</RootNamespace>
|
||||
<AssemblyName>Matrix42.Extensions.Scaffolder.Vsix</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<GeneratePkgDefFile>true</GeneratePkgDefFile>
|
||||
<UseCodebase>true</UseCodebase>
|
||||
<IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
|
||||
<IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
|
||||
<IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
|
||||
<CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory>
|
||||
<CopyOutputSymbolsToOutputDirectory>true</CopyOutputSymbolsToOutputDirectory>
|
||||
<StartAction>Program</StartAction>
|
||||
<StartProgram Condition="'$(DevEnvDir)' != ''">$(DevEnvDir)devenv.exe</StartProgram>
|
||||
<StartArguments>/rootsuffix Exp</StartArguments>
|
||||
<GeneratePkgDefFile>false</GeneratePkgDefFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Wizard\ExtensionProjectsWizard.cs" />
|
||||
<Compile Include="Wizard\ProjectDefinition.cs" />
|
||||
<Compile Include="Wizard\RuntimeTargets.cs" />
|
||||
<Compile Include="Wizard\WizardForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Wizard\WizardForm.Designer.cs">
|
||||
<DependentUpon>WizardForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ProjectTemplates\Directory.Build.props.template" />
|
||||
<None Include="ProjectTemplates\Placeholder.csproj" />
|
||||
<None Include="source.extension.vsixmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.0.32112.339" ExcludeAssets="runtime" NoWarn="NU1604" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.TemplateWizardInterface">
|
||||
<Version>17.10.40170</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.14.2096" NoWarn="NU1604" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="ProjectTemplates\ExtensionSolution.vstemplate" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36310.24 d17.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Matrix42.Extensions.Scaffolder.Vsix", "Matrix42.Extensions.Scaffolder.Vsix.csproj", "{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|arm64 = Debug|arm64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|arm64 = Release|arm64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Debug|arm64.ActiveCfg = Debug|arm64
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Debug|arm64.Build.0 = Debug|arm64
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Debug|x86.Build.0 = Debug|x86
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Release|arm64.ActiveCfg = Release|arm64
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Release|arm64.Build.0 = Release|arm64
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Release|x86.ActiveCfg = Release|x86
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {ECBF8D8B-3FBE-4CDA-BE52-C37D76054B09}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,21 @@
|
||||
<VSTemplate Version="3.0.0" Type="Project">
|
||||
<TemplateData>
|
||||
<Name>Matrix42 Extension Solution</Name>
|
||||
<Description>Create a new Matrix42 extension solution (WebApi / Contracts / BizLogic / Custom) using Matrix42.Extensions.SDK.</Description>
|
||||
<ProjectType>CSharp</ProjectType>
|
||||
<SortOrder>1000</SortOrder>
|
||||
<DefaultName>Matrix42.Extension</DefaultName>
|
||||
<CreateNewFolder>true</CreateNewFolder>
|
||||
<ProvideDefaultName>true</ProvideDefaultName>
|
||||
<LocationField>Enabled</LocationField>
|
||||
</TemplateData>
|
||||
|
||||
<TemplateContent>
|
||||
<Project File="Placeholder.csproj" ReplaceParameters="true" TargetFileName="$safeprojectname$.csproj" />
|
||||
</TemplateContent>
|
||||
|
||||
<WizardExtension>
|
||||
<Assembly>Matrix42.Extensions.Scaffolder.Vsix, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</Assembly>
|
||||
<FullClassName>Matrix42.Extensions.Scaffolder.Wizard.ExtensionProjectsWizard</FullClassName>
|
||||
</WizardExtension>
|
||||
</VSTemplate>
|
||||
@@ -0,0 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Matrix42.Extensions.Scaffolder.Vsix")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Matrix42.Extensions.Scaffolder.Vsix")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using EnvDTE;
|
||||
using EnvDTE80;
|
||||
using Microsoft.VisualStudio.TemplateWizard;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Wizard
|
||||
{
|
||||
public class ExtensionProjectsWizard : IWizard
|
||||
{
|
||||
private DTE2 _dte;
|
||||
private Project _placeholderProject;
|
||||
private string _extensionNamespace = string.Empty;
|
||||
private List<ProjectDefinition> _projects = new List<ProjectDefinition>();
|
||||
private string _solutionDirectory = string.Empty;
|
||||
|
||||
public void RunStarted(
|
||||
object automationObject,
|
||||
Dictionary<string, string> replacementsDictionary,
|
||||
WizardRunKind runKind,
|
||||
object[] customParams)
|
||||
{
|
||||
_dte = automationObject as DTE2
|
||||
?? throw new InvalidOperationException("DTE2 not available.");
|
||||
|
||||
using (var form = new WizardForm())
|
||||
{
|
||||
var result = form.ShowDialog();
|
||||
if (result != System.Windows.Forms.DialogResult.OK)
|
||||
throw new WizardBackoutException();
|
||||
|
||||
_extensionNamespace = form.ExtensionNamespace;
|
||||
_projects = new List<ProjectDefinition>(form.Projects);
|
||||
}
|
||||
}
|
||||
|
||||
// 🔹 This is the method that was missing
|
||||
public void BeforeOpeningFile(ProjectItem projectItem)
|
||||
{
|
||||
// We don't need to do anything before a file is opened.
|
||||
// Leave empty on purpose.
|
||||
}
|
||||
|
||||
public void ProjectFinishedGenerating(Project project)
|
||||
{
|
||||
_placeholderProject = project;
|
||||
}
|
||||
|
||||
public void ProjectItemFinishedGenerating(ProjectItem projectItem)
|
||||
{
|
||||
}
|
||||
|
||||
public void RunFinished()
|
||||
{
|
||||
var solution = _dte.Solution;
|
||||
_solutionDirectory = Path.GetDirectoryName(solution.FullName);
|
||||
|
||||
if (string.IsNullOrEmpty(_solutionDirectory))
|
||||
return;
|
||||
|
||||
// Remove placeholder project from solution and disk
|
||||
if (_placeholderProject != null)
|
||||
{
|
||||
var placeholderPath = _placeholderProject.FullName;
|
||||
solution.Remove(_placeholderProject);
|
||||
TryDeleteDirectorySafe(Path.GetDirectoryName(placeholderPath));
|
||||
}
|
||||
|
||||
// Drop Directory.Build.props
|
||||
var propsTemplatePath = Path.Combine(
|
||||
GetExtensionInstallPath(),
|
||||
"ProjectTemplates",
|
||||
"Directory.Build.props.template");
|
||||
|
||||
if (File.Exists(propsTemplatePath))
|
||||
{
|
||||
var destProps = Path.Combine(_solutionDirectory, "Directory.Build.props");
|
||||
if (!File.Exists(destProps))
|
||||
File.Copy(propsTemplatePath, destProps, overwrite: false);
|
||||
}
|
||||
|
||||
foreach (var proj in _projects)
|
||||
{
|
||||
CreateProjectFromDefinition(solution, proj);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldAddProjectItem(string filePath) => true;
|
||||
|
||||
private void CreateProjectFromDefinition(Solution solution, ProjectDefinition def)
|
||||
{
|
||||
var projectName = def.GetProjectName(_extensionNamespace);
|
||||
var projectNamespace = def.GetNamespace(_extensionNamespace);
|
||||
var projectDir = Path.Combine(_solutionDirectory, projectName);
|
||||
|
||||
Directory.CreateDirectory(projectDir);
|
||||
|
||||
var csprojPath = Path.Combine(projectDir, projectName + ".csproj");
|
||||
var runtimeIds = def.GetRuntimeIdentifiers();
|
||||
var tfm = "net8.0"; // default target framework
|
||||
|
||||
var csprojContent = GenerateCsproj(def.Type, tfm, runtimeIds, projectNamespace);
|
||||
File.WriteAllText(csprojPath, csprojContent);
|
||||
|
||||
var codeFilePath = Path.Combine(
|
||||
projectDir,
|
||||
def.Type == ProjectType.WebApi ? "Program.cs" : "Class1.cs");
|
||||
|
||||
var codeContent = GenerateCodeFile(def.Type, projectNamespace);
|
||||
File.WriteAllText(codeFilePath, codeContent);
|
||||
|
||||
solution.AddFromFile(csprojPath);
|
||||
}
|
||||
|
||||
private string GenerateCsproj(
|
||||
ProjectType type,
|
||||
string tfm,
|
||||
string runtimeIds,
|
||||
string rootNamespace)
|
||||
{
|
||||
var outputType = type == ProjectType.WebApi ? "Exe" : "Library";
|
||||
var sdk = type == ProjectType.WebApi ? "Microsoft.NET.Sdk.Web" : "Microsoft.NET.Sdk";
|
||||
|
||||
var ridBlock = string.IsNullOrWhiteSpace(runtimeIds)
|
||||
? string.Empty
|
||||
: $" <RuntimeIdentifiers>{runtimeIds}</RuntimeIdentifiers>{Environment.NewLine}";
|
||||
|
||||
return
|
||||
$@"<Project Sdk=""{sdk}"">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>{tfm}</TargetFramework>
|
||||
<OutputType>{outputType}</OutputType>
|
||||
<RootNamespace>{rootNamespace}</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
{ridBlock} </PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include=""Matrix42.Extensions.SDK"" Version=""1.0.0"" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
";
|
||||
}
|
||||
|
||||
private string GenerateCodeFile(ProjectType type, string ns)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ProjectType.WebApi:
|
||||
return
|
||||
$@"using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Matrix42.Extensions.SDK;
|
||||
|
||||
namespace {ns}
|
||||
{{
|
||||
public class Program
|
||||
{{
|
||||
public static void Main(string[] args)
|
||||
{{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// TODO: configure Matrix42.Extensions.SDK services
|
||||
|
||||
var app = builder.Build();
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
}}
|
||||
}}
|
||||
}}";
|
||||
case ProjectType.Contracts:
|
||||
return
|
||||
$@"namespace {ns}
|
||||
{{
|
||||
// Place your contracts (DTOs, interfaces) here
|
||||
public class SampleContract
|
||||
{{
|
||||
public string Id {{ get; set; }} = string.Empty;
|
||||
}}
|
||||
}}";
|
||||
case ProjectType.BizLogic:
|
||||
return
|
||||
$@"using Matrix42.Extensions.SDK;
|
||||
|
||||
namespace {ns}
|
||||
{{
|
||||
// Place your business logic services here
|
||||
public class SampleService
|
||||
{{
|
||||
public string SayHello() => ""Hello from {ns}"";
|
||||
}}
|
||||
}}";
|
||||
case ProjectType.Custom:
|
||||
default:
|
||||
return
|
||||
$@"namespace {ns}
|
||||
{{
|
||||
// Custom project for your extension
|
||||
public class CustomEntry
|
||||
{{
|
||||
public string Name {{ get; set; }} = ""Custom"";
|
||||
}}
|
||||
}}";
|
||||
}
|
||||
}
|
||||
|
||||
private string GetExtensionInstallPath()
|
||||
{
|
||||
var asmPath = typeof(ExtensionProjectsWizard).Assembly.Location;
|
||||
return Path.GetDirectoryName(asmPath);
|
||||
}
|
||||
|
||||
private void TryDeleteDirectorySafe(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path))
|
||||
Directory.Delete(path, recursive: true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Wizard
|
||||
{
|
||||
public enum ProjectType
|
||||
{
|
||||
WebApi,
|
||||
Contracts,
|
||||
BizLogic,
|
||||
Custom
|
||||
}
|
||||
|
||||
public class ProjectDefinition
|
||||
{
|
||||
public ProjectType Type { get; set; }
|
||||
public string Prefix { get; set; } = string.Empty; // custom for Custom type, otherwise derived
|
||||
public RuntimeTargets Runtimes { get; set; }
|
||||
|
||||
public string EffectivePrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Type == ProjectType.Custom)
|
||||
return Prefix;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case ProjectType.WebApi:
|
||||
return string.IsNullOrWhiteSpace(Prefix) ? "Services" : Prefix;
|
||||
case ProjectType.Contracts:
|
||||
return string.IsNullOrWhiteSpace(Prefix) ? "Contracts" : Prefix;
|
||||
case ProjectType.BizLogic:
|
||||
return string.IsNullOrWhiteSpace(Prefix) ? "BizLogic" : Prefix;
|
||||
default:
|
||||
return Prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Display => $"{Type} - Prefix: {EffectivePrefix} - Runtimes: {Runtimes}";
|
||||
|
||||
public string GetProjectName(string extensionNamespace)
|
||||
{
|
||||
var pfx = EffectivePrefix?.Trim();
|
||||
return string.IsNullOrWhiteSpace(pfx)
|
||||
? extensionNamespace
|
||||
: $"{extensionNamespace}.{pfx}";
|
||||
}
|
||||
|
||||
public string GetNamespace(string extensionNamespace) => GetProjectName(extensionNamespace);
|
||||
|
||||
public string GetRuntimeIdentifiers()
|
||||
{
|
||||
if (Runtimes.HasFlag(RuntimeTargets.All))
|
||||
return "win-x64;linux-x64";
|
||||
|
||||
var list = new List<string>();
|
||||
if (Runtimes.HasFlag(RuntimeTargets.Win) || Runtimes.HasFlag(RuntimeTargets.WinCore))
|
||||
list.Add("win-x64");
|
||||
if (Runtimes.HasFlag(RuntimeTargets.LinuxCore))
|
||||
list.Add("linux-x64");
|
||||
|
||||
return string.Join(";", list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Matrix42.Extensions.Scaffolder.Wizard
|
||||
{
|
||||
[System.Flags]
|
||||
public enum RuntimeTargets
|
||||
{
|
||||
None = 0,
|
||||
Win = 1,
|
||||
WinCore = 2,
|
||||
LinuxCore = 4,
|
||||
All = 8
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
namespace Matrix42.Extensions.Scaffolder.Wizard
|
||||
{
|
||||
partial class WizardForm
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
private System.Windows.Forms.Label lblTitle;
|
||||
private System.Windows.Forms.Label lblExtensionNamespace;
|
||||
private System.Windows.Forms.TextBox txtExtensionNamespace;
|
||||
private System.Windows.Forms.Label lblHint;
|
||||
private System.Windows.Forms.Label lblAddProjects;
|
||||
private System.Windows.Forms.Label lblProjectType;
|
||||
private System.Windows.Forms.ComboBox cboProjectType;
|
||||
private System.Windows.Forms.Label lblProjectPrefix;
|
||||
private System.Windows.Forms.TextBox txtProjectPrefix;
|
||||
private System.Windows.Forms.Label lblRuntimes;
|
||||
private System.Windows.Forms.CheckedListBox clbRuntimes;
|
||||
private System.Windows.Forms.Button btnAddProject;
|
||||
private System.Windows.Forms.ListBox lstProjects;
|
||||
private System.Windows.Forms.Button btnRemoveSelected;
|
||||
private System.Windows.Forms.Button btnOK;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
components.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.lblTitle = new System.Windows.Forms.Label();
|
||||
this.lblExtensionNamespace = new System.Windows.Forms.Label();
|
||||
this.txtExtensionNamespace = new System.Windows.Forms.TextBox();
|
||||
this.lblHint = new System.Windows.Forms.Label();
|
||||
this.lblAddProjects = new System.Windows.Forms.Label();
|
||||
this.lblProjectType = new System.Windows.Forms.Label();
|
||||
this.cboProjectType = new System.Windows.Forms.ComboBox();
|
||||
this.lblProjectPrefix = new System.Windows.Forms.Label();
|
||||
this.txtProjectPrefix = new System.Windows.Forms.TextBox();
|
||||
this.lblRuntimes = new System.Windows.Forms.Label();
|
||||
this.clbRuntimes = new System.Windows.Forms.CheckedListBox();
|
||||
this.btnAddProject = new System.Windows.Forms.Button();
|
||||
this.lstProjects = new System.Windows.Forms.ListBox();
|
||||
this.btnRemoveSelected = new System.Windows.Forms.Button();
|
||||
this.btnOK = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// lblTitle
|
||||
//
|
||||
this.lblTitle.AutoSize = true;
|
||||
this.lblTitle.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
|
||||
this.lblTitle.Location = new System.Drawing.Point(12, 9);
|
||||
this.lblTitle.Name = "lblTitle";
|
||||
this.lblTitle.Size = new System.Drawing.Size(120, 20);
|
||||
this.lblTitle.TabIndex = 0;
|
||||
this.lblTitle.Text = "Projects Wizard";
|
||||
//
|
||||
// lblExtensionNamespace
|
||||
//
|
||||
this.lblExtensionNamespace.AutoSize = true;
|
||||
this.lblExtensionNamespace.Location = new System.Drawing.Point(13, 44);
|
||||
this.lblExtensionNamespace.Name = "lblExtensionNamespace";
|
||||
this.lblExtensionNamespace.Size = new System.Drawing.Size(113, 15);
|
||||
this.lblExtensionNamespace.TabIndex = 1;
|
||||
this.lblExtensionNamespace.Text = "Extension Namespace";
|
||||
//
|
||||
// txtExtensionNamespace
|
||||
//
|
||||
this.txtExtensionNamespace.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.txtExtensionNamespace.Location = new System.Drawing.Point(16, 62);
|
||||
this.txtExtensionNamespace.Name = "txtExtensionNamespace";
|
||||
this.txtExtensionNamespace.Size = new System.Drawing.Size(444, 23);
|
||||
this.txtExtensionNamespace.TabIndex = 2;
|
||||
//
|
||||
// lblHint
|
||||
//
|
||||
this.lblHint.AutoSize = true;
|
||||
this.lblHint.Location = new System.Drawing.Point(466, 65);
|
||||
this.lblHint.Name = "lblHint";
|
||||
this.lblHint.Size = new System.Drawing.Size(121, 15);
|
||||
this.lblHint.TabIndex = 3;
|
||||
this.lblHint.Text = "aka Matrix42.MyExtension";
|
||||
//
|
||||
// lblAddProjects
|
||||
//
|
||||
this.lblAddProjects.AutoSize = true;
|
||||
this.lblAddProjects.Location = new System.Drawing.Point(13, 103);
|
||||
this.lblAddProjects.Name = "lblAddProjects";
|
||||
this.lblAddProjects.Size = new System.Drawing.Size(71, 15);
|
||||
this.lblAddProjects.TabIndex = 4;
|
||||
this.lblAddProjects.Text = "Add Projects";
|
||||
//
|
||||
// lblProjectType
|
||||
//
|
||||
this.lblProjectType.AutoSize = true;
|
||||
this.lblProjectType.Location = new System.Drawing.Point(13, 129);
|
||||
this.lblProjectType.Name = "lblProjectType";
|
||||
this.lblProjectType.Size = new System.Drawing.Size(70, 15);
|
||||
this.lblProjectType.TabIndex = 5;
|
||||
this.lblProjectType.Text = "Project Type";
|
||||
//
|
||||
// cboProjectType
|
||||
//
|
||||
this.cboProjectType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.cboProjectType.FormattingEnabled = true;
|
||||
this.cboProjectType.Location = new System.Drawing.Point(16, 147);
|
||||
this.cboProjectType.Name = "cboProjectType";
|
||||
this.cboProjectType.Size = new System.Drawing.Size(150, 23);
|
||||
this.cboProjectType.TabIndex = 6;
|
||||
//
|
||||
// lblProjectPrefix
|
||||
//
|
||||
this.lblProjectPrefix.AutoSize = true;
|
||||
this.lblProjectPrefix.Location = new System.Drawing.Point(179, 129);
|
||||
this.lblProjectPrefix.Name = "lblProjectPrefix";
|
||||
this.lblProjectPrefix.Size = new System.Drawing.Size(75, 15);
|
||||
this.lblProjectPrefix.TabIndex = 7;
|
||||
this.lblProjectPrefix.Text = "Project Prefix";
|
||||
//
|
||||
// txtProjectPrefix
|
||||
//
|
||||
this.txtProjectPrefix.Location = new System.Drawing.Point(182, 147);
|
||||
this.txtProjectPrefix.Name = "txtProjectPrefix";
|
||||
this.txtProjectPrefix.Size = new System.Drawing.Size(150, 23);
|
||||
this.txtProjectPrefix.TabIndex = 8;
|
||||
//
|
||||
// lblRuntimes
|
||||
//
|
||||
this.lblRuntimes.AutoSize = true;
|
||||
this.lblRuntimes.Location = new System.Drawing.Point(345, 129);
|
||||
this.lblRuntimes.Name = "lblRuntimes";
|
||||
this.lblRuntimes.Size = new System.Drawing.Size(121, 15);
|
||||
this.lblRuntimes.TabIndex = 9;
|
||||
this.lblRuntimes.Text = "Runtimes (multiselect)";
|
||||
//
|
||||
// clbRuntimes
|
||||
//
|
||||
this.clbRuntimes.CheckOnClick = true;
|
||||
this.clbRuntimes.FormattingEnabled = true;
|
||||
this.clbRuntimes.Location = new System.Drawing.Point(348, 147);
|
||||
this.clbRuntimes.Name = "clbRuntimes";
|
||||
this.clbRuntimes.Size = new System.Drawing.Size(150, 76);
|
||||
this.clbRuntimes.TabIndex = 10;
|
||||
//
|
||||
// btnAddProject
|
||||
//
|
||||
this.btnAddProject.Location = new System.Drawing.Point(504, 147);
|
||||
this.btnAddProject.Name = "btnAddProject";
|
||||
this.btnAddProject.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnAddProject.TabIndex = 11;
|
||||
this.btnAddProject.Text = "Add";
|
||||
this.btnAddProject.UseVisualStyleBackColor = true;
|
||||
this.btnAddProject.Click += new System.EventHandler(this.btnAddProject_Click);
|
||||
//
|
||||
// lstProjects
|
||||
//
|
||||
this.lstProjects.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.lstProjects.FormattingEnabled = true;
|
||||
this.lstProjects.ItemHeight = 15;
|
||||
this.lstProjects.Location = new System.Drawing.Point(16, 236);
|
||||
this.lstProjects.Name = "lstProjects";
|
||||
this.lstProjects.Size = new System.Drawing.Size(563, 139);
|
||||
this.lstProjects.TabIndex = 12;
|
||||
//
|
||||
// btnRemoveSelected
|
||||
//
|
||||
this.btnRemoveSelected.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnRemoveSelected.Location = new System.Drawing.Point(16, 385);
|
||||
this.btnRemoveSelected.Name = "btnRemoveSelected";
|
||||
this.btnRemoveSelected.Size = new System.Drawing.Size(120, 23);
|
||||
this.btnRemoveSelected.TabIndex = 13;
|
||||
this.btnRemoveSelected.Text = "Remove selected";
|
||||
this.btnRemoveSelected.UseVisualStyleBackColor = true;
|
||||
this.btnRemoveSelected.Click += new System.EventHandler(this.btnRemoveSelected_Click);
|
||||
//
|
||||
// btnOK
|
||||
//
|
||||
this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnOK.Location = new System.Drawing.Point(423, 385);
|
||||
this.btnOK.Name = "btnOK";
|
||||
this.btnOK.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnOK.TabIndex = 14;
|
||||
this.btnOK.Text = "Create";
|
||||
this.btnOK.UseVisualStyleBackColor = true;
|
||||
this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnCancel.Location = new System.Drawing.Point(504, 385);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnCancel.TabIndex = 15;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||
//
|
||||
// WizardForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(592, 420);
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.Controls.Add(this.btnOK);
|
||||
this.Controls.Add(this.btnRemoveSelected);
|
||||
this.Controls.Add(this.lstProjects);
|
||||
this.Controls.Add(this.btnAddProject);
|
||||
this.Controls.Add(this.clbRuntimes);
|
||||
this.Controls.Add(this.lblRuntimes);
|
||||
this.Controls.Add(this.txtProjectPrefix);
|
||||
this.Controls.Add(this.lblProjectPrefix);
|
||||
this.Controls.Add(this.cboProjectType);
|
||||
this.Controls.Add(this.lblProjectType);
|
||||
this.Controls.Add(this.lblAddProjects);
|
||||
this.Controls.Add(this.lblHint);
|
||||
this.Controls.Add(this.txtExtensionNamespace);
|
||||
this.Controls.Add(this.lblExtensionNamespace);
|
||||
this.Controls.Add(this.lblTitle);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "WizardForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Matrix42 Extension Solution Wizard";
|
||||
this.Load += new System.EventHandler(this.WizardForm_Load);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Matrix42.Extensions.Scaffolder.Wizard
|
||||
{
|
||||
public partial class WizardForm : Form
|
||||
{
|
||||
private readonly List<ProjectDefinition> _projects = new List<ProjectDefinition>();
|
||||
|
||||
public string ExtensionNamespace => txtExtensionNamespace.Text.Trim();
|
||||
public IReadOnlyList<ProjectDefinition> Projects => _projects;
|
||||
|
||||
public WizardForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
cboProjectType.DataSource = Enum.GetValues(typeof(ProjectType));
|
||||
|
||||
clbRuntimes.Items.Add("Win");
|
||||
clbRuntimes.Items.Add("WinCore");
|
||||
clbRuntimes.Items.Add("LinuxCore");
|
||||
clbRuntimes.Items.Add("ALL");
|
||||
|
||||
cboProjectType.SelectedIndexChanged += CboProjectType_SelectedIndexChanged;
|
||||
}
|
||||
|
||||
private void WizardForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
lblHint.Text = "aka Matrix42.MyExtension";
|
||||
}
|
||||
|
||||
private void CboProjectType_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
var type = (ProjectType)cboProjectType.SelectedItem;
|
||||
switch (type)
|
||||
{
|
||||
case ProjectType.WebApi:
|
||||
txtProjectPrefix.Text = "Services";
|
||||
txtProjectPrefix.ReadOnly = false;
|
||||
break;
|
||||
case ProjectType.Contracts:
|
||||
txtProjectPrefix.Text = "Contracts";
|
||||
txtProjectPrefix.ReadOnly = false;
|
||||
break;
|
||||
case ProjectType.BizLogic:
|
||||
txtProjectPrefix.Text = "BizLogic";
|
||||
txtProjectPrefix.ReadOnly = false;
|
||||
break;
|
||||
case ProjectType.Custom:
|
||||
txtProjectPrefix.Text = string.Empty;
|
||||
txtProjectPrefix.ReadOnly = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void btnAddProject_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!ValidateProjectInputs())
|
||||
return;
|
||||
|
||||
var def = new ProjectDefinition
|
||||
{
|
||||
Type = (ProjectType)cboProjectType.SelectedItem,
|
||||
Prefix = txtProjectPrefix.Text.Trim(),
|
||||
Runtimes = GetSelectedRuntimes()
|
||||
};
|
||||
|
||||
_projects.Add(def);
|
||||
lstProjects.Items.Add(def.Display);
|
||||
|
||||
txtProjectPrefix.Clear();
|
||||
for (int i = 0; i < clbRuntimes.Items.Count; i++)
|
||||
clbRuntimes.SetItemChecked(i, false);
|
||||
}
|
||||
|
||||
private RuntimeTargets GetSelectedRuntimes()
|
||||
{
|
||||
RuntimeTargets result = RuntimeTargets.None;
|
||||
|
||||
foreach (var item in clbRuntimes.CheckedItems)
|
||||
{
|
||||
switch (item.ToString())
|
||||
{
|
||||
case "Win":
|
||||
result |= RuntimeTargets.Win;
|
||||
break;
|
||||
case "WinCore":
|
||||
result |= RuntimeTargets.WinCore;
|
||||
break;
|
||||
case "LinuxCore":
|
||||
result |= RuntimeTargets.LinuxCore;
|
||||
break;
|
||||
case "ALL":
|
||||
result |= RuntimeTargets.All;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool ValidateProjectInputs()
|
||||
{
|
||||
if (cboProjectType.SelectedItem == null)
|
||||
{
|
||||
MessageBox.Show("Please select project type.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var type = (ProjectType)cboProjectType.SelectedItem;
|
||||
if (string.IsNullOrWhiteSpace(txtProjectPrefix.Text))
|
||||
{
|
||||
if (type == ProjectType.Custom)
|
||||
{
|
||||
MessageBox.Show("Project prefix is required for Custom project type.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (clbRuntimes.CheckedItems.Count == 0)
|
||||
{
|
||||
MessageBox.Show("Please select at least one runtime.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void btnRemoveSelected_Click(object sender, EventArgs e)
|
||||
{
|
||||
int idx = lstProjects.SelectedIndex;
|
||||
if (idx < 0 || idx >= _projects.Count)
|
||||
return;
|
||||
|
||||
_projects.RemoveAt(idx);
|
||||
lstProjects.Items.RemoveAt(idx);
|
||||
}
|
||||
|
||||
private void btnOK_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ExtensionNamespace))
|
||||
{
|
||||
MessageBox.Show("Extension Namespace is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_projects.Count == 0)
|
||||
{
|
||||
MessageBox.Show("Please add at least one project.");
|
||||
return;
|
||||
}
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
|
||||
<Metadata>
|
||||
<Identity Id="Matrix42.Extensions.Scaffolder.Vsix.7c0f30ed-547f-4e1d-a046-8120ae21fc8b" Version="1.0" Language="en-US" Publisher="Matrix42" />
|
||||
<DisplayName>Matrix42.Extensions.Scaffolder.Vsix</DisplayName>
|
||||
<Description xml:space="preserve">Creates a new Matrix42 extension solution with multiple projects based on Matrix42.Extensions.SDK.</Description>
|
||||
</Metadata>
|
||||
<Installation>
|
||||
<InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[17.0, 18.0)">
|
||||
<ProductArchitecture>amd64</ProductArchitecture>
|
||||
</InstallationTarget>
|
||||
</Installation>
|
||||
<Dependencies>
|
||||
<Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.7,)" />
|
||||
</Dependencies>
|
||||
<Prerequisites>
|
||||
<Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[17.0,18.0)" DisplayName="Visual Studio core editor" />
|
||||
</Prerequisites>
|
||||
<Assets>
|
||||
<Asset Type="Microsoft.VisualStudio.ProjectTemplate" Path="ProjectTemplates\ExtensionSolution.vstemplate" />
|
||||
</Assets>
|
||||
</PackageManifest>
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36310.24
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Matrix42.Extensions.Scaffolder.Core", "Matrix42.Extensions.Scaffolder.Core\Matrix42.Extensions.Scaffolder.Core.csproj", "{43E82070-2941-4589-8BF5-1FD1D427BDAE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Matrix42.Extensions.Scaffolder.Console", "Matrix42.Extensions.Scaffolder.Console\Matrix42.Extensions.Scaffolder.Console.csproj", "{F7979A70-0D89-46B7-814C-1E7D0B0DF441}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Matrix42.Extensions.Scaffolder.Vsix", "Matrix42.Extensions.Scaffolder.Vsix\Matrix42.Extensions.Scaffolder.Vsix.csproj", "{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{43E82070-2941-4589-8BF5-1FD1D427BDAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{43E82070-2941-4589-8BF5-1FD1D427BDAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{43E82070-2941-4589-8BF5-1FD1D427BDAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{43E82070-2941-4589-8BF5-1FD1D427BDAE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F7979A70-0D89-46B7-814C-1E7D0B0DF441}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F7979A70-0D89-46B7-814C-1E7D0B0DF441}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F7979A70-0D89-46B7-814C-1E7D0B0DF441}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F7979A70-0D89-46B7-814C-1E7D0B0DF441}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{50F8DCAA-48B9-47D5-ABB8-F8D33BEF1BD1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3329054A-94A3-4953-8650-68C9B450515A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
17
F4SDM42WebApi/M42ExtensionScaffolder/service-info.yml
Normal file
17
F4SDM42WebApi/M42ExtensionScaffolder/service-info.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
product: "M42 Enterprise" # The project or platform this service belongs to
|
||||
service_name: "Extensions Scaffolder" # Name of the service
|
||||
description: "Template project acting as an extension scaffolder. Automatically generate extension structure including provided projects and runtimes" # A brief description of the service
|
||||
|
||||
owner:
|
||||
name: "Titan" # Owner of the service (could be an individual or team)
|
||||
email: "dmytro.romanii@matrix42.com" # Contact email for the owner
|
||||
|
||||
pipelines:
|
||||
pull_request_pipeline: "N/A" # URL to the pipeline that runs for pull requests
|
||||
ci_pipeline: "N/A" # URL to the continuous integration (CI) pipeline
|
||||
cd_pipeline: "N/A" # URL to the continuous delivery (CD) pipeline
|
||||
|
||||
links:
|
||||
repository: "https://dev.azure.com/matrix42/DWP/_git/M42ExtensionScaffolder"
|
||||
documentation: "https://dev.azure.com/matrix42/DWP/_git/M42ExtensionScaffolder?path=/README.md"
|
||||
backlog: "https://dev.azure.com/matrix42/BPM/_backlogs/backlog/Titan"
|
||||
BIN
F4SDM42WebApi/M42Libraries/26.1/AdminUI.AppManFoundation.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/AdminUI.AppManFoundation.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/AdminUI.DcmObjectWrapper.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/AdminUI.DcmObjectWrapper.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/AdminUI.WqlQueryEngine.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/AdminUI.WqlQueryEngine.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Ajax.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Ajax.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/AjaxControlToolkit.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/AjaxControlToolkit.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/AngleSharp.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/AngleSharp.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/AntiXSSLibrary.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/AntiXSSLibrary.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/AutoMapper.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/AutoMapper.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Azure.Core.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Azure.Core.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Azure.Identity.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Azure.Identity.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/BouncyCastle.Crypto.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/BouncyCastle.Crypto.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/CsvHelper.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/CsvHelper.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/DcmObjectModel.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/DcmObjectModel.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/EPPlus.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/EPPlus.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/EntityFramework.SqlServer.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/EntityFramework.SqlServer.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/EntityFramework.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/EntityFramework.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Google.Apis.Auth.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Google.Apis.Auth.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Google.Apis.Core.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Google.Apis.Core.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Google.Apis.Gmail.v1.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Google.Apis.Gmail.v1.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Google.Apis.PlatformServices.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Google.Apis.PlatformServices.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Google.Apis.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Google.Apis.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/HtmlAgilityPack.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/HtmlAgilityPack.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/ICSharpCode.SharpZipLib.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/ICSharpCode.SharpZipLib.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Interop.ActiveDs.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Interop.ActiveDs.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Interop.DTS.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Interop.DTS.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Interop.WindowsInstaller.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Interop.WindowsInstaller.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Ionic.Zip.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Ionic.Zip.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/MailBee.NET.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/MailBee.NET.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/MailKit.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/MailKit.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.AI.Contracts.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.AI.Contracts.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Api.Contracts.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Api.Contracts.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Api.DbClient.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Api.DbClient.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Auth.BizLogic.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Auth.BizLogic.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Auth.Contracts.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Auth.Contracts.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Auth.Engines.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Auth.Engines.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Auth.STS.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Auth.STS.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Auth.Services.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Auth.Services.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Behaviors.Contracts.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Behaviors.Contracts.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.BizLogic.Client.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.BizLogic.Client.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.BizLogic.Common.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.BizLogic.Common.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.BizLogic.Journal.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.BizLogic.Journal.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Blob.BizLogic.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Blob.BizLogic.dll
Normal file
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Blob.Contracts.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.Blob.Contracts.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.CUSTCascDel.Core.dll
Normal file
BIN
F4SDM42WebApi/M42Libraries/26.1/Matrix42.CUSTCascDel.Core.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user