diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 082b64f..37f9402 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -4,7 +4,8 @@
{
"name": "PowerShell",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
- "image": "mcr.microsoft.com/powershell:lts-debian-11",
+ "image": "mcr.microsoft.com/powershell:lts-ubuntu-22.04",
+
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {},
"ghcr.io/devcontainers/features/powershell:1": {},
diff --git a/SfHelper.psm1 b/SfHelper.psm1
index f3e72cc..4d6908e 100644
--- a/SfHelper.psm1
+++ b/SfHelper.psm1
@@ -9,11 +9,12 @@ $START = Get-ChildItem -Path $MODULE_PATH -Filter start.ps1 -Recurse
if($START | Test-Path){ . $($START | Get-Item).FullName }
#Get public and private function definition files.
+$Include = @( Get-ChildItem -Path $MODULE_PATH\include\*.ps1 -Recurse -ErrorAction SilentlyContinue )
$Public = @( Get-ChildItem -Path $MODULE_PATH\public\*.ps1 -Recurse -ErrorAction SilentlyContinue )
$Private = @( Get-ChildItem -Path $MODULE_PATH\private\*.ps1 -Recurse -ErrorAction SilentlyContinue )
#Dot source the files
-Foreach($import in @($Public + $Private))
+Foreach($import in @($Include + $Public + $Private))
{
Try
{
diff --git a/Test/Test.psm1 b/Test/Test.psm1
index 14c5c6a..6fc3d0b 100644
--- a/Test/Test.psm1
+++ b/Test/Test.psm1
@@ -4,11 +4,12 @@ Write-Information -Message ("Loading {0} ..." -f ($PSCommandPath | Split-Path -L
$MODULE_PATH = $PSScriptRoot
#Get public and private function definition files.
+$Include = @( Get-ChildItem -Path $MODULE_PATH\include\*.ps1 -Recurse -ErrorAction SilentlyContinue )
$Public = @( Get-ChildItem -Path $MODULE_PATH\public\*.ps1 -Recurse -ErrorAction SilentlyContinue )
$Private = @( Get-ChildItem -Path $MODULE_PATH\private\*.ps1 -Recurse -ErrorAction SilentlyContinue )
#Dot source the files
-Foreach($import in @($Public + $Private))
+Foreach($import in @($Include + $Public + $Private))
{
Try
{
diff --git a/Test/include/config.mock.ps1 b/Test/include/config.mock.ps1
new file mode 100644
index 0000000..53536fe
--- /dev/null
+++ b/Test/include/config.mock.ps1
@@ -0,0 +1,28 @@
+
+
+function Mock_Config{
+ param(
+ [Parameter(Position=0)][string] $key = "config",
+ [Parameter(Position=1)][object] $Config
+ )
+
+ $MOCK_CONFIG_PATH = "test_config_path"
+
+ # Remove mock config path if exists
+ if(Test-Path $MOCK_CONFIG_PATH){
+ Remove-Item -Path $MOCK_CONFIG_PATH -ErrorAction SilentlyContinue -Recurse -Force
+ }
+
+ # create mock config path
+ New-Item -Path $MOCK_CONFIG_PATH -ItemType Directory -Force
+
+ # if $config is not null save it to a file
+ if($null -ne $Config){
+ $configfile = Join-Path -Path $MOCK_CONFIG_PATH -ChildPath "$key.json"
+ $Config | ConvertTo-Json -Depth 10 | Set-Content $configfile
+ }
+
+ # Mock invoke call
+ MockCallToString "Invoke-GetConfigRootPath" -OutString $MOCK_CONFIG_PATH
+
+}
\ No newline at end of file
diff --git a/Test/include/database.mock.ps1 b/Test/include/database.mock.ps1
new file mode 100644
index 0000000..3a0e927
--- /dev/null
+++ b/Test/include/database.mock.ps1
@@ -0,0 +1,9 @@
+function Mock_Database([switch]$ResetDatabase){
+
+ MockCallToString "Invoke-GetDatabaseStorePath" -OutString "test_database_path"
+
+ if($ResetDatabase){
+ Reset-DatabaseStore
+ }
+
+}
\ No newline at end of file
diff --git a/Test/private/InvokeCommandMock.ps1 b/Test/private/InvokeCommandMock.ps1
index ce78ff0..9762ab4 100644
--- a/Test/private/InvokeCommandMock.ps1
+++ b/Test/private/InvokeCommandMock.ps1
@@ -1,6 +1,6 @@
# Managing dependencies
-$MODULE_INVOKATION_TAG = "project-migration-module"
-$MODULE_INVOKATION_TAG_MOCK = "project-migration-module_Mock"
+$MODULE_INVOKATION_TAG = "SfHelperModule"
+$MODULE_INVOKATION_TAG_MOCK = "SfHelperModule-Mock"
$ROOT = $PSScriptRoot | Split-Path -Parent
$MOCK_PATH = $ROOT | Join-Path -ChildPath 'private' -AdditionalChildPath 'mocks'
@@ -136,11 +136,28 @@ function Save-InvokeAsMockFile{
$result = Invoke-Expression -Command $Command
- $result | ConvertTo-Json -Depth 100 | Out-File -FilePath $filePath
+ $json = $result | ConvertTo-Json -Depth 100
+
+ $json | Out-File -FilePath $filePath
Write-Host $FileName
} Export-ModuleMember -Function Save-InvokeAsMockFile
+function Save-InvokeAsMockFileJson{
+ param(
+ [Parameter(Mandatory=$true)] [string]$Command,
+ [Parameter(Mandatory=$true)] [string]$FileName
+ )
+
+ $filePath = Get-MockFileFullPath -fileName $fileName
+
+ $result = Invoke-Expression -Command $Command
+
+ $result | Out-File -FilePath $filePath
+
+ Write-Host $FileName
+} Export-ModuleMember -Function Save-InvokeAsMockFileJson
+
function Assert-MockFileNotfound{
param(
[Parameter(Mandatory=$true,Position=0)] [string]$FileName
diff --git a/Test/private/mocks/sf-data-query-account.json b/Test/private/mocks/sf-data-query-account.json
new file mode 100644
index 0000000..9c83d05
--- /dev/null
+++ b/Test/private/mocks/sf-data-query-account.json
@@ -0,0 +1,31 @@
+{
+ "status": 0,
+ "result": {
+ "records": [
+ {
+ "attributes": {
+ "type": "Account",
+ "url": "/services/data/v63.0/sobjects/Account/0010V00002KIWkaQAH"
+ },
+ "Id": "0010V00002KIWkaQAH",
+ "Name": "Contoso",
+ "OwnerId": "0053o000008Skv3AAC",
+ "Industry": "Retail",
+ "Account_Owner__c": "Oana Dinca [Change]",
+ "Account_Segment__c": "Enterprise",
+ "Account_Owner_Role__c": "EMEA - Enterprise Sales Manager - South EMEA",
+ "Account_Tier__c": "Tier 1",
+ "Potential_Seats__c": 998,
+ "Country_Name__c": "Spain",
+ "Current_Seats__c": 600,
+ "Current_ARR_10__c": 116928,
+ "Salesforce_Record_URL__c": "https://github.my.salesforce.com/0010V00002KIWkaQAH",
+ "Potential_Seats_Manual__c": 1500,
+ "Website": "www.contos.es",
+ "PhotoUrl": "/services/images/photo/0010V00002KIWkaQAH"
+ }
+ ],
+ "totalSize": 1,
+ "done": true
+ }
+}
diff --git a/Test/public/Get-SfObjectIdFromUrl.test.ps1 b/Test/public/Get-SfObjectIdFromUrl.test.ps1
index 565c5e1..dca5d81 100644
--- a/Test/public/Get-SfObjectIdFromUrl.test.ps1
+++ b/Test/public/Get-SfObjectIdFromUrl.test.ps1
@@ -1,4 +1,4 @@
-function Test_GetSfAccount{
+function Test_GetSfAccountFromUrl{
. $PSScriptRoot/../../private/auxiliarfunctions.ps1
diff --git a/Test/public/sfDataQuery.test.ps1 b/Test/public/sfDataQuery.test.ps1
new file mode 100644
index 0000000..54dd671
--- /dev/null
+++ b/Test/public/sfDataQuery.test.ps1
@@ -0,0 +1,37 @@
+function Test_GetSfAccount{
+
+ Reset-InvokeCommandMock
+ Mock_Database -ResetDatabase
+ $mockAttrib = @{attributes = @("Potential_Seats_Manual__c","Website","PhotoUrl")}
+ Mock_Config -Config $mockAttrib
+
+ $dbstore = Invoke-MyCommand -Command GetDatabaseStorePath
+ Assert-AreEqual -Expected "test_database_path" -Presented $dbstore
+
+ $attrib = "Id,Name,OwnerId,Industry,Account_Owner__c,Account_Segment__c,Account_Owner_Role__c,Account_Tier__c,Potential_Seats__c,Country_Name__c,Current_Seats__c,Current_ARR_10__c,Salesforce_Record_URL__c,Potential_Seats_Manual__c,Website,PhotoUrl"
+ $type = "Account"
+ $id = "0010V00002KIWkaQAH"
+
+ $command = 'sf data query --query "SELECT {attributes} FROM {type} WHERE Id=''{id}''" -r=json'
+ $command = $command -replace "{attributes}", $attrib
+ $command = $command -replace "{type}", $type
+ $command = $command -replace "{id}", $id
+ MockCall -Command $command -filename "sf-data-query-account.json"
+
+ # Act with out cache
+ $result = get-sfAccount https://github.lightning.force.com/lightning/r/Account/0010V00002KIWkaQAH/view
+ Assert-AreEqual -Expected $Id -Presented $result.Id
+ $dbfiles = Get-ChildItem $dbstore
+ Assert-Count -Expected 1 -Presented $dbfiles
+ Assert-IsTrue -Condition ($dbfiles[0].Name -like "sfDataQuery*-$type-$id-*.json")
+
+ # Remove sf data
+ Reset-InvokeCommandMock
+ Mock_Database
+ Mock_Config -Config $mockAttrib
+
+ # Act with cache
+ $result = Get-SfAccount https://github.lightning.force.com/lightning/r/Account/0010V00002KIWkaQAH/view
+ Assert-AreEqual -Expected $Id -Presented $result.Id
+
+}
\ No newline at end of file
diff --git a/include/config.ps1 b/include/config.ps1
new file mode 100644
index 0000000..19d2d30
--- /dev/null
+++ b/include/config.ps1
@@ -0,0 +1,93 @@
+# Configuration management module
+
+Set-MyInvokeCommandAlias -Alias GetConfigRootPath -Command "Invoke-GetConfigRootPath"
+
+$moduleName = Get-ModuleName
+$CONFIG_ROOT = [System.Environment]::GetFolderPath('UserProfile') | Join-Path -ChildPath ".helpers" -AdditionalChildPath $moduleName, "config"
+
+# Create the config root if it does not exist
+if(-Not (Test-Path $CONFIG_ROOT)){
+ New-Item -Path $CONFIG_ROOT -ItemType Directory
+}
+
+function Invoke-GetConfigRootPath {
+ [CmdletBinding()]
+ param()
+
+ $configRoot = $CONFIG_ROOT
+ return $configRoot
+} Export-ModuleMember -Function Invoke-GetConfigRootPath
+
+function GetConfigFile {
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory = $true, Position = 0)][string]$Key
+ )
+
+ $configRoot = Invoke-MyCommand -Command GetConfigRootPath
+ $path = Join-Path -Path $configRoot -ChildPath "$Key.json"
+ return $path
+}
+
+function Test-Configuration {
+ [CmdletBinding()]
+ param(
+ [Parameter(Position = 0)][string]$Key = "config"
+ )
+
+ $path = GetConfigFile -Key $Key
+
+ return Test-Path $path
+}
+
+function Get-Configuration {
+ [CmdletBinding()]
+ param(
+ [Parameter(Position = 0)][string]$Key = "config"
+ )
+
+ $path = GetConfigFile -Key $Key
+
+ if(-Not (Test-Configuration -Key $Key)){
+ return $null
+ }
+
+ try{
+ $ret = Get-Content $path | ConvertFrom-Json -ErrorAction Stop
+ return $ret
+ }
+ catch{
+ Write-Warning "Error reading configuration ($Key) file: $($path). $($_.Exception.Message)"
+ return $null
+ }
+}
+
+function Save-Configuration {
+ [CmdletBinding()]
+ param(
+ [Parameter(Position = 0)][string]$Key = "config",
+ [Parameter(Mandatory = $true, Position = 1)][Object]$Config
+ )
+
+ $path = GetConfigFile -Key $Key
+
+ try {
+ $Config | ConvertTo-Json -Depth 10 | Set-Content $path -ErrorAction Stop
+ }
+ catch {
+ Write-Warning "Error saving configuration ($Key) to file: $($path). $($_.Exception.Message)"
+ return $false
+ }
+
+ return $true
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/private/dependencies/databaseV2.ps1 b/private/dependencies/databaseV2.ps1
new file mode 100644
index 0000000..305ae37
--- /dev/null
+++ b/private/dependencies/databaseV2.ps1
@@ -0,0 +1,113 @@
+# Database driver to store the cache
+
+# Invoke to allow mockig the store path on testing
+Set-MyInvokeCommandAlias -Alias GetDatabaseStorePath -Command "Invoke-GetDatabaseStorePath"
+
+$moduleName = Get-ModuleName
+$DATABASE_ROOT = [System.Environment]::GetFolderPath('UserProfile') | Join-Path -ChildPath ".helpers" -AdditionalChildPath $moduleName, "databaseCache"
+
+# Create the database root if it does not exist
+if(-Not (Test-Path $DATABASE_ROOT)){
+ New-Item -Path $DATABASE_ROOT -ItemType Directory
+}
+
+function Reset-DatabaseStore{
+ [CmdletBinding()]
+ param()
+
+ $databaseRoot = Invoke-MyCommand -Command GetDatabaseStorePath
+
+ Remove-Item -Path $databaseRoot -Recurse -Force -ErrorAction SilentlyContinue
+
+ New-Item -Path $databaseRoot -ItemType Directory
+
+} Export-ModuleMember -Function Reset-DatabaseStore
+
+function Get-DatabaseStore{
+ [CmdletBinding()]
+ param()
+
+ $databaseRoot = Invoke-MyCommand -Command GetDatabaseStorePath
+
+ return $databaseRoot
+
+} Export-ModuleMember -Function Get-DatabaseStore
+
+function Get-Database{
+ [CmdletBinding()]
+ param(
+ [Parameter(Position = 0)][string]$Key
+ )
+
+ if(-Not (Test-Database $Key)){
+ return $null
+ }
+
+ $path = GetDatabaseFile $Key
+
+ $ret = Get-Content $path | ConvertFrom-Json -Depth 10 -AsHashtable
+
+ return $ret
+}
+
+function Reset-Database{
+ [CmdletBinding()]
+ param(
+ [Parameter(Position = 0)][string]$Key
+ )
+ $path = GetDatabaseFile -Key $Key
+ Remove-Item -Path $path -Force -ErrorAction SilentlyContinue
+ return
+}
+
+function Save-Database{
+ [CmdletBinding()]
+ param(
+ [Parameter(Position = 0)][string]$Key,
+ [Parameter(Position = 2)][Object]$Database
+ )
+
+ $path = GetDatabaseFile -Key $Key
+
+ $Database | ConvertTo-Json -Depth 10 | Set-Content $path
+}
+
+function Test-Database{
+ [CmdletBinding()]
+ param(
+ [Parameter(Position = 0)][string]$Key
+ )
+
+ $path = GetDatabaseFile -Key $Key
+
+ # Key file not exists
+ if(-Not (Test-Path $path)){
+ return $false
+ }
+
+ # TODO: Return $false if cache has expired
+
+ return $true
+}
+
+function GetDatabaseFile{
+ [CmdletBinding()]
+ param(
+ [Parameter(Position = 0)][string]$Key
+ )
+
+ $databaseRoot = Invoke-MyCommand -Command GetDatabaseStorePath
+
+ $path = $databaseRoot | Join-Path -ChildPath "$Key.json"
+
+ return $path
+}
+
+function Invoke-GetDatabaseStorePath{
+ [CmdletBinding()]
+ param()
+
+ $databaseRoot = $DATABASE_ROOT
+
+ return $databaseRoot
+} Export-ModuleMember -Function Invoke-GetDatabaseStorePath
\ No newline at end of file
diff --git a/private/start/START.ps1 b/private/start/START.ps1
index ca671d7..8a7366f 100644
--- a/private/start/START.ps1
+++ b/private/start/START.ps1
@@ -9,5 +9,11 @@ if (! $LOADED_EARLYLOADED){
# Load Invoke helper functions
. $(($PSScriptRoot | Join-Path -ChildPath SetMyInvokeCommandAlias.ps1 | Get-Item).FullName)
-}
+ function Get-ModuleName{
+ $local = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent
+ $moduleName = (Get-ChildItem -Path $local -Filter *.psd1 | Select-Object -First 1).BaseName
+
+ return $moduleName
+ }
+}
diff --git a/private/start/SetMyInvokeCommandAlias.ps1 b/private/start/SetMyInvokeCommandAlias.ps1
index 65fc3e1..57cfcbc 100644
--- a/private/start/SetMyInvokeCommandAlias.ps1
+++ b/private/start/SetMyInvokeCommandAlias.ps1
@@ -4,7 +4,7 @@
if (!$SET_MY_INVOKECOMMANDALIAS_LOADED){
$SET_MY_INVOKECOMMANDALIAS_LOADED = $true
- $MODULE_INVOKATION_TAG = "sfhelper-module"
+ $MODULE_INVOKATION_TAG = "SfHelperModule"
function Set-MyInvokeCommandAlias{
[CmdletBinding(SupportsShouldProcess)]
diff --git a/public/getsfaccount.ps1 b/public/getsfaccount.ps1
index 481c5e5..7cce9b5 100644
--- a/public/getsfaccount.ps1
+++ b/public/getsfaccount.ps1
@@ -59,10 +59,11 @@ function Get-SfAccount{
}
## Add attributes from file
- if (Test-Path $PROFILE_ATTRIBUTES_FILE_PATH) {
- $attributesFromFile = Get-Content $PROFILE_ATTRIBUTES_FILE_PATH
- "addring attributes from file $attributesFromFile" | Write-Verbose
- $attributes += $attributesFromFile | Select-Object -Unique
+ if (Test-Configuration ) {
+ $config = Get-Configuration
+ $attributesFromConfig = $config.attributes
+ "adding attributes from config $($attributesFromConfig -join ',' )" | Write-Verbose
+ $attributes += $attributesFromConfig | Select-Object -Unique
}
# Get object
diff --git a/public/sfDataQuery.ps1 b/public/sfDataQuery.ps1
index 50eaa92..7cf0b07 100644
--- a/public/sfDataQuery.ps1
+++ b/public/sfDataQuery.ps1
@@ -8,6 +8,12 @@ function Get-SfDataQuery{
[Parameter(Mandatory)][string[]]$Attributes
)
+ # Testcache first
+ $cacheKey = getcacheKey -Type $Type -Id $Id -Attributes $Attributes
+ if(Test-Database -Key $cacheKey){
+ return Get-Database -Key $cacheKey
+ }
+
$params = @{
id = $Id
type = $Type
@@ -35,8 +41,31 @@ function Get-SfDataQuery{
$ret | Add-Member -MemberType NoteProperty -Name "QueryDate" -Value (Get-Date)
+ Save-Database -Key $cacheKey -Database $ret
+
return $ret
}
+function getcacheKey{
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory)][string]$Type,
+ [Parameter(Mandatory)][string]$Id,
+ [Parameter(Mandatory)][string[]]$Attributes
+ )
+
+ # Add hash of attributes to key
+ $attribString = $Attributes -join ","
+ "Attributes : $attribString" | Write-Verbose
+ $attributesHash = $attribString.GetHashCode()
+ "AttributesHash : $attributesHash" | Write-Verbose
+
+ $cacheKey = "sfDataQuery-$Type-$Id-$attributesHash"
+
+ "CacheKey : $cacheKey" | Write-Verbose
+
+ return $cacheKey
+}
+