ps/Modules/Alkami.PowerShell.Configuration/Public/New-AlkamiManifest.ps1
2023-05-30 22:51:22 -07:00

391 lines
14 KiB
PowerShell

function New-AlkamiManifest {
<#
.SYNOPSIS
Create a new AlkamiManifest.xml (v 1.0) in a specified location for a known type.
.PARAMETER Type
The type of manifest to be created
.PARAMETER Destination
The target folder location where the manifest is going to be created. Defaults to the current folder.
.PARAMETER ProjectLocation
The location of the project file to use for manifest creation. Defaults to looking in the current folder.
.PARAMETER FileType
Create files in xml or json format
.PARAMETER Force
Used to overwrite existing files
#>
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[ValidateSet('Widget', 'Service', 'Migration', 'Theme', 'WebApplication', 'WebExtension', 'Repository', 'Provider', 'WebSite', 'LegacyUtility', 'Hotfix', 'ApiComponent')]
[String]$Type,
[Parameter(Position = 1, Mandatory = $false)]
[Alias('Path', 'Folder', 'Target')]
[String]$Destination,
[Parameter(Position = 2, Mandatory = $false)]
[String]$ProjectLocation,
[Parameter(Position = 3, Mandatory = $false)]
[ValidateSet('Xml', 'Json')]
[string]$FileType = 'Xml',
[Switch]$Force
)
process {
$logLead = (Get-LogLeadName)
$widgetType = 'Widget'
$serviceType = 'Service'
$migrationType = 'Migration'
$themeType = 'Theme'
$webApplicationType = 'WebApplication'
$webExtensionType = 'WebExtension'
$repositoryType = 'Repository'
$providerType = 'Provider'
$websiteType = 'WebSite'
$legacyUtilityType = 'LegacyUtility'
$hotfixType = 'Hotfix'
$apiComponentType = 'ApiComponent'
# Get the current folder so we can put/look for things in the working folder if not specified
$CurrentWorkingFolder = (Get-Location).Path
# The base name is like C:\git\Alkami.Services.Permissions\ <- I want "Alkami.Services.Permissions"
# The base name can also be derived from the csproj name later
# So if the folder is C:\git\perms with an Alkami.Services.Permissions.csproj we can absorb that as the basename later
# The reason for a basename is when we start supporting node or ruby, etc, they don't have an assemblyInfo
$baseName = (Split-Path $CurrentWorkingFolder -Leaf)
if ([string]::IsNullOrWhiteSpace($Destination)) {
$Destination = $CurrentWorkingFolder
}
# Target path is where we store the final file at
# Easier to work with non-relative paths. This gives us a full path.
$targetPath = (Resolve-Path $Destination).Path
# Determine where we want to drop the file when we're done
# Ensure that our target path ends in the manifest filename
$defaultFilename = "AlkamiManifest.$($FileType.ToLower())"
$targetItemIsContainer = (Get-Item $targetPath).PSIsContainer
if (!$targetItemIsContainer -and ((Split-Path $targetPath -Leaf) -ne $defaultFilename)) {
Write-Warning "$logLead : The specified filename is not the expected filename. Matching to required filename."
$targetPath = (Join-Path (Split-Path $targetPath -Parent) $defaultFilename)
}
# if the target path is a container (is not a file)
# append the filename
if ($targetItemIsContainer) {
$targetPath = (Join-Path $targetPath $defaultFilename)
}
# Target path is now a file that we want to target
if (!$Force -and (Test-Path $targetPath)) {
Write-Warning "$logLead : There's already a file at $targetPath - Did you want to Test-AlkamiManifest this file instead? Use -Force to overwrite the existing file."
return
}
Write-Verbose "$logLead : Getting CSProj"
if ([string]::IsNullOrWhiteSpace($ProjectLocation)) {
$ProjectLocation = $CurrentWorkingFolder
}
if (!(Test-Path $ProjectLocation)) {
Write-Warning "$logLead : Project location does not exist, looking in current folder for a csproj"
$ProjectLocation = $CurrentWorkingFolder
}
# If it's a folder, look in the folder
if ((Get-Item $ProjectLocation).PSIsContainer) {
$csprojFile = ((Get-ChildItem (Join-Path $ProjectLocation "*.csproj") -Recurse) | Sort-Object -Unique | Select-Object -First 1)
} else {
# Test path did exist, it wasn't a folder, use that
$csprojFile = (Get-Item $ProjectLocation)
}
# This could be a node project, or just an empty folder with no content yet
if ($null -ne $csprojFile) {
$baseName = (Get-Item $csprojFile).BaseName
}
$assemblyName = $baseName
$rootNamespace = $baseName
$creatorCode = ""
$areaName = ""
$framework = "framework"
$splitRoots = @()
# If we have a csproj file then let's collect information from it
if ((![string]::IsNullOrWhiteSpace($csprojFile)) -and (Test-Path $csprojFile)) {
$xml = [xml](Get-Content $csprojFile.FullName)
$rootNamespaces = @($xml.Project.PropertyGroup.RootNamespace)
if (![string]::IsNullOrWhiteSpace($rootNamespaces[0])) {
$rootNamespace = $rootNamespaces[0]
}
$assemblyNames = @($xml.Project.PropertyGroup.AssemblyName)
if (![string]::IsNullOrWhiteSpace($assemblyNames[0])) {
$assemblyName = $assemblyNames[0]
}
$targetFrameworks = @($xml.Project.PropertyGroup.TargetFramework)
if (![string]::IsNullOrWhiteSpace($targetFrameworks[0])) {
$targetFramework = $targetFrameworks[0]
}
if ($targetFramework -match "core") {
$framework = "dotnetcore"
} else {
$framework = "framework"
}
$splitRoots = $rootNamespace.Split('.')
$creatorCode = $splitRoots[0]
} else {
# Refactor when we have node project examples to emit something more generic
Write-Warning "$logLead : There is no csproj file at [$ProjectLocation|$csprojFile]. Please run this from within a project code folder, or supply a valid ProjectLocation"
}
if ([string]::IsNullOrWhiteSpace($assemblyName) -and ![string]::IsNullOrWhiteSpace($rootNamespace)) {
$assemblyName = $rootNamespace
}
if ([string]::IsNullOrWhiteSpace($rootNamespace) -and ![string]::IsNullOrWhiteSpace($assemblyName)) {
$rootNamespace = $assemblyName
}
$sectionManifest = ""
if ($Type -eq $hotfixType) {
Write-Verbose "Building Hotfix Section"
$sectionManifest = @"
<hotfixManifest>
<fixedInOrbVersion>REPLACEME</fixedInOrbVersion>
<serverTier>REPLACESERVERTIER</serverTier>
</hotfixManifest>
"@
}
if($Type -eq $apiComponentType){
Write-Verbose "Building ApiComponent Section"
$sectionManifest = @"
<apiComponentsManifest>
<targetApp>REPLACEME</targetApp>
</apiComponentsManifest>
"@
}
if ($Type -eq $widgetType) {
Write-Verbose "Building Widget Section"
if (!!$splitRoots) {
# Widget area names are always the last part of the project name
# Alkami.Client.Widgets.Lending = Lending
$areaName = $splitRoots[-1]
# In the case of SDK they are actually:
# BCU.Client.Widgets.Lending = BCULending
$areaName = $splitRoots[0] + $splitRoots[-1]
}
$sectionManifest = @"
<widgetManifest>
<widgetName>This is a displayable widget name for the UI</widgetName>
<widgetDescription>This is a displayable widget description for the UI</widgetDescription>
<widgetInstall>Client</widgetInstall>
<areaName>$areaName</areaName>
<assemblyInfo>$rootNamespace</assemblyInfo>
<displaySettings>Desktop</displaySettings>
<iconName></iconName>
</widgetManifest>
"@
}
if ($Type -eq $serviceType) {
Write-Verbose "$logLead : Building Service Section"
$jsonServiceManifestChunk = @"
<migrations>
<assembly target="master|TENANT|tde" online="true|FALSE" role="my_service_role">$assemblyName.Migrations</assembly>
<package id="$assemblyName.Migrations" version="1.0" />
</migrations>
<db_role>my_service_role</db_role>
<!--
<provider>
<providerName>my_provider_name</providerName>
<providerType>my_provider_type</providerType>
</provider>
-->
"@
$xmlServiceManifestChunk = @"
<migrations>
<!--<assembly target="master|TENANT|tde" online="true|FALSE" role="my_service_role">$assemblyName.Migrations</assembly> -->
<!--<package id="$assemblyName.Migrations" version="1.0" />-->
</migrations>
<!-- <db_role>my_service_role</db_role> -->
"@
$useChunk = $xmlServiceManifestChunk
if ($FileType -eq 'Json') {
$useChunk = $jsonServiceManifestChunk
}
$sectionManifest = @"
<serviceManifest>
<runtime>$framework</runtime>
<entryPoint>$assemblyName</entryPoint>
<!-- Populating the migrations or db_role values will assume the microservice needs connectivity to a tenant database. -->$useChunk
</serviceManifest>
"@
}
if ($Type -eq $legacyUtilityType) {
Write-Verbose "Building LegacyUtility Section"
$sectionManifest = @"
<legacyUtilityManifest>
<!-- If legacy projects use Alkami.Ioc from AppDev/Common or the Alkami.Common nuget package, then needsShared should be set to true. -->
<needsShared>true</needsShared>
</legacyUtilityManifest>
"@
}
if ($Type -eq $migrationType) {
Write-Verbose "Building Migration Section"
$sectionManifest = @"
<migrationManifest>
<runnerType>FluentMigrator|Fluent</runnerType>
<roleName>REPLACEME</roleName>
<!-- The assemblyName of the file in this current package (only the migration dll name "My.Service.Migrations.dll" is required) -->
<!-- The assemblyName can be array-ified by repeating <assemblyName> tags as many as you need, one entry per -->
<assemblyName>$assemblyName</assemblyName>
</migrationManifest>
"@
}
if ($Type -eq $themeType) {
Write-Verbose "Building Theme Section"
$sectionManifest = @"
<themeManifest>
<themeName></themeName>
<defaultOverrideForUrlSignature></defaultOverrideForUrlSignature>
</themeManifest>
"@
}
if ($Type -eq $webApplicationType) {
Write-Verbose "Building Web Application Section"
$sectionManifest = @"
<webApplicationManifest>
<appInstall></appInstall>
<appName>$areaName</appName>
<!-- most apps do not need needsShared set to true. Ask SDK/Vanguard for more details. -->
<needsShared>false</needsShared>
</webApplicationManifest>
"@
}
if ($Type -eq $webExtensionType) {
Write-Verbose "Building Web Extension Section"
$sectionManifest = @"
<webExtensionManifest>
<appInstall>Client</appInstall>
</webExtensionManifest>
"@
}
if ($Type -eq $repositoryType) {
Write-Verbose "Building Repository Section"
$sectionManifest = @"
<repositoryManifest>
<appInstall>Client</appInstall>
</repositoryManifest>
"@
}
if ($Type -eq $websiteType) {
Write-Verbose "Building WebSite Section"
$sectionManifest = @"
<websiteManifest>
<websiteName>$baseName</websiteName>
<url></url>
</websiteManifest>
"@
}
if ($Type -eq $providerType) {
Write-Verbose "Building Provider Section"
$sectionManifest = @"
<!-- This is a legacy project. Providers are no longer supported as new submissions for Alkami SDK projects. -->
<providerManifest>
<!-- appInstall requires some combination of the default provided values. Use pipes or commas to separate service names. -->
<appInstall>BankService|Bank|SchedulerService|CoreService|Core|NotificationService|SecurityManagementService|SecurityManagement|Security|Radium|Nag|NagConfigurationService|NagConfig|NagConfiguration|All</appInstall>
<assemblyInfo>$rootNamespace</assemblyInfo>
<providerType></providerType>
<providerName></providerName>
<pluginType>Connector</pluginType>
</providerManifest>
"@
}
Write-Verbose "Building Manifest"
$sdkSection = @"
<!-- SDK clients should add their BankIdentifier(s) in this list. No quotes or brackets - {} - are required. Dashes are encouraged. -->
<bankIdentifiers>
<bankIdentifier name="MyFIName">DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF</bankIdentifier>
</bankIdentifiers>
"@
# Special case for SDK clients to get extra help
if ($creatorCode -eq "Alkami") {
$sdkSection = ""
}
$manifestPayload = @"
<?xml version="1.0"?>
<packageManifest>
<version>1.0</version>
<general>
<creatorCode>$creatorCode</creatorCode>
<element>$assemblyName</element>
<componentType>$Type</componentType>$sdkSection
<!--
<releaseManagement>
<alwaysDeploy>false</alwaysDeploy>
</releaseManagement>
-->
</general>
$sectionManifest
</packageManifest>
"@
Write-Host "$logLead : Creating a new manifest file for a $FileType [$Type] at [$targetPath]"
if ($FileType -eq 'Json') {
$manifestPayload = ([xml]$manifestPayload | ConvertFrom-Xml).PackageManifest | Format-Json
}
Set-Content -Path $targetPath -Value $manifestPayload;
}
}