Skip to content
234 changes: 218 additions & 16 deletions src/code/InstallPSResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
// Licensed under the MIT License.

using Microsoft.PowerShell.PowerShellGet.UtilClasses;
using Newtonsoft.Json;
using NuGet.Versioning;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management.Automation;
using Microsoft.PowerShell.Commands;

using Dbg = System.Diagnostics.Debug;

Expand Down Expand Up @@ -113,6 +117,63 @@ class InstallPSResource : PSCmdlet
[ValidateNotNullOrEmpty]
public PSResourceInfo InputObject { get; set; }

/// <summary>
/// Installs resources based on input from a JSON file.
/// </summary>
[Parameter(ParameterSetName = RequiredResourceFileParameterSet)]
[ValidateNotNullOrEmpty]
public String RequiredResourceFile
{
get
{
return _requiredResourceFile;
}
set
{
string resolvedPath = string.Empty;
if (!string.IsNullOrWhiteSpace(value))
{
resolvedPath = SessionState.Path.GetResolvedPSPathFromPSPath(value).First().Path;
}

if (!File.Exists(resolvedPath))
{
var exMessage = String.Format("The RequiredResourceFile does not exist. Please try specifying a path to a valid .json file");
var ex = new ArgumentException(exMessage);
var RequiredResourceFileDoesNotExist = new ErrorRecord(ex, "RequiredResourceFileDoesNotExist", ErrorCategory.ObjectNotFound, null);

ThrowTerminatingError(RequiredResourceFileDoesNotExist);
}

_requiredResourceFile = resolvedPath;
}
}

/// <summary>
/// Installs resources in a hashtable or JSON string format.
/// </summary>
[Parameter(ParameterSetName = RequiredResourceParameterSet)]
public Object RequiredResource // takes either string (json) or hashtable
{
get { return _requiredResourceHash != null ? _requiredResourceHash : (Object)_requiredResourceJson; }

set
{
if (value is String jsonResource)
{
_requiredResourceJson = jsonResource;
}
else if (value is Hashtable hashResource)
{
_requiredResourceHash = hashResource;
}
else
{
throw new ParameterBindingException("Object is not a JSON or Hashtable");
}
}
}

#endregion

#region Members
Expand All @@ -122,6 +183,9 @@ class InstallPSResource : PSCmdlet
private const string RequiredResourceFileParameterSet = "RequiredResourceFileParameterSet";
private const string RequiredResourceParameterSet = "RequiredResourceParameterSet";
List<string> _pathsToInstallPkg;
private string _requiredResourceFile;
private string _requiredResourceJson;
private Hashtable _requiredResourceHash;
VersionRange _versionRange;
InstallHelper _installHelper;

Expand Down Expand Up @@ -161,8 +225,11 @@ protected override void ProcessRecord()

ProcessInstallHelper(
pkgNames: Name,
pkgVersion: _versionRange,
pkgPrerelease: Prerelease,
pkgRepository: Repository);
pkgRepository: Repository,
pkgCredential: Credential,
reqResourceParams: null);
break;

case InputObjectParameterSet:
Expand All @@ -177,37 +244,172 @@ protected override void ProcessRecord()

ProcessInstallHelper(
pkgNames: new string[] { InputObject.Name },
pkgVersion: _versionRange,
pkgPrerelease: InputObject.IsPrerelease,
pkgRepository: new string[]{ InputObject.Repository });
pkgRepository: new string[]{ InputObject.Repository },
pkgCredential: Credential,
reqResourceParams: null);
break;

case RequiredResourceFileParameterSet:
ThrowTerminatingError(new ErrorRecord(
new PSNotImplementedException("RequiredResourceFileParameterSet is not yet implemented. Please rerun cmdlet with other parameter set."),
"CommandParameterSetNotImplementedYet",
ErrorCategory.NotImplemented,
this));
/* json file contents should look like:
{
"Pester": {
"allowPrerelease": true,
"version": "[4.4.2,4.7.0]",
"repository": "PSGallery",
"credential": null
}
}
*/

string requiredResourceFileStream = string.Empty;
using (StreamReader sr = new StreamReader(_requiredResourceFile))
{
requiredResourceFileStream = sr.ReadToEnd();
}

Hashtable pkgsInJsonFile = null;
try
{
pkgsInJsonFile = Utils.ConvertJsonToHashtable(this, requiredResourceFileStream);
}
catch (Exception)
{
var exMessage = String.Format("Argument for parameter -RequiredResourceFile is not in proper json format. Make sure argument is either a valid json file.");
var ex = new ArgumentException(exMessage);
var RequiredResourceFileNotInProperJsonFormat = new ErrorRecord(ex, "RequiredResourceFileNotInProperJsonFormat", ErrorCategory.InvalidData, null);

ThrowTerminatingError(RequiredResourceFileNotInProperJsonFormat);
}

RequiredResourceHelper(pkgsInJsonFile);
break;

case RequiredResourceParameterSet:
ThrowTerminatingError(new ErrorRecord(
new PSNotImplementedException("RequiredResourceParameterSet is not yet implemented. Please rerun cmdlet with other parameter set."),
"CommandParameterSetNotImplementedYet",
ErrorCategory.NotImplemented,
this));
if (!string.IsNullOrWhiteSpace(_requiredResourceJson))
{
/* json would look like:
{
"Pester": {
"allowPrerelease": true,
"version": "[4.4.2,4.7.0]",
"repository": "PSGallery",
"credential": null
}
}
*/

Hashtable pkgsHash = null;
try
{
pkgsHash = Utils.ConvertJsonToHashtable(this, _requiredResourceJson);
}
catch (Exception)
{
var exMessage = String.Format("Argument for parameter -RequiredResource is not in proper json format. Make sure argument is either a valid json file.");
var ex = new ArgumentException(exMessage);
var RequiredResourceFileNotInProperJsonFormat = new ErrorRecord(ex, "RequiredResourceFileNotInProperJsonFormat", ErrorCategory.InvalidData, null);

ThrowTerminatingError(RequiredResourceFileNotInProperJsonFormat);
}

RequiredResourceHelper(pkgsHash);
}

if (_requiredResourceHash != null)
{
/* hashtable would look like:
@{
"Configuration" = @{ version = "[4.4.2,4.7.0]" }
"Pester" = @{
version = "[4.4.2,4.7.0]"
repository = PSGallery
credential = $cred
prerelease = $true
}
}
*/

RequiredResourceHelper(_requiredResourceHash);
}
break;

default:
Dbg.Assert(false, "Invalid parameter set");
break;
}
}

#endregion

#region Methods

private void ProcessInstallHelper(string[] pkgNames, bool pkgPrerelease, string[] pkgRepository)
private void RequiredResourceHelper(Hashtable reqResourceHash)
{
var pkgNames = reqResourceHash.Keys;

foreach (string pkgName in pkgNames)
{
var pkgParamInfo = reqResourceHash[pkgName];

// Format should now be a hashtable, whether the original input format was json or hashtable
if (!(pkgParamInfo is Hashtable pkgInstallInfo))
{
return;
}

InstallPkgParams pkgParams = new InstallPkgParams();
var pkgParamNames = pkgInstallInfo.Keys;

PSCredential pkgCredential = Credential;
foreach (string paramName in pkgParamNames)
{
if (string.Equals(paramName, "credential", StringComparison.InvariantCultureIgnoreCase))
{
WriteVerbose("Credential specified for required resource");
pkgCredential = pkgInstallInfo[paramName] as PSCredential;
}

pkgParams.SetProperty(paramName, pkgInstallInfo[paramName] as string, out ErrorRecord IncorrectVersionFormat);

if (IncorrectVersionFormat != null)
{
ThrowTerminatingError(IncorrectVersionFormat);
}
}

if (pkgParams.Scope == ScopeType.AllUsers)
{
_pathsToInstallPkg = Utils.GetAllInstallationPaths(this, pkgParams.Scope);
}

VersionRange pkgVersion;
// If no Version specified, install latest version for the package.
// Otherwise validate Version can be parsed out successfully.
if (pkgInstallInfo["version"] == null || string.IsNullOrWhiteSpace(pkgInstallInfo["version"].ToString()))
{
pkgVersion = VersionRange.All;
}
else if (!Utils.TryParseVersionOrVersionRange(pkgInstallInfo["version"].ToString(), out pkgVersion))
{
var exMessage = "Argument for Version parameter is not in the proper format.";
var ex = new ArgumentException(exMessage);
var IncorrectVersionFormat = new ErrorRecord(ex, "IncorrectVersionFormat", ErrorCategory.InvalidArgument, null);
ThrowTerminatingError(IncorrectVersionFormat);
}

ProcessInstallHelper(
pkgNames: new string[] { pkgName },
pkgVersion: pkgVersion,
pkgPrerelease: pkgParams.Prerelease,
pkgRepository: new string[] { pkgParams.Repository },
pkgCredential: pkgCredential,
reqResourceParams: pkgParams);
}
}

private void ProcessInstallHelper(string[] pkgNames, VersionRange pkgVersion, bool pkgPrerelease, string[] pkgRepository, PSCredential pkgCredential, InstallPkgParams reqResourceParams)
{
var inputNameToInstall = Utils.ProcessNameWildcards(pkgNames, out string[] errorMsgs, out bool nameContainsWildcard);
if (nameContainsWildcard)
Expand Down Expand Up @@ -244,7 +446,7 @@ private void ProcessInstallHelper(string[] pkgNames, bool pkgPrerelease, string[

var installedPkgs = _installHelper.InstallPackages(
names: pkgNames,
versionRange: _versionRange,
versionRange: pkgVersion,
prerelease: pkgPrerelease,
repository: pkgRepository,
acceptLicense: AcceptLicense,
Expand All @@ -253,7 +455,7 @@ private void ProcessInstallHelper(string[] pkgNames, bool pkgPrerelease, string[
force: false,
trustRepository: TrustRepository,
noClobber: NoClobber,
credential: Credential,
credential: pkgCredential,
asNupkg: false,
includeXML: true,
skipDependencyCheck: SkipDependencyCheck,
Expand Down
Loading