I am attempting to use structFindKey to create an "org" struct from a query.
I am starting with a query that looks like this.
from this I am trying to build a struct that represents the actual org structure that I would like to look something like this:
I am starting with my request.hierarchyStruct that looks like this:
here is the code so far
for(row in getCorpUserHierarchy){
insertIntoHierachy(row);
}
function insertIntoHierachy(thisRow){
var thisKey = thisRow.parentGroupId;
var newChild = {
"level" = thisRow.ThisLevel
, "levelName" = thisRow.levelName
, "groupName" = thisRow.groupName
, "members" = []
};
keyResult = structFindKey(request.hierarchyStruct, thisKey, "one");
if(arrayLen(keyResult) > 0){
writeDump(keyResult);
newPath = 'request.hierarchyStruct' & keyResult[1].path;
foundKey = structGet(newPath);
foundKey[thisRow.groupId] = newChild;
}
}
I am able to "find the key" which dumps the key result:
But when the first row "Jasmines Region" finds and try to add the "newChild" to it I get an error
I have tried a variety of combinations with the path including
var newPath = keyResult[1].path;
var fullPath = 'request.hierarchyStruct'
var pathArray = listToArray(newPath,'.');
for(i in pathArray){
fullPath = fullpath & "." & i ;
}
I don't know if it matters a lot but I am using the lastest version of LUCEE not adobe's coldfusion.
This is the first time using structFindKey and it's path can anyone shed any light on this???
You may have stumbled upon a bug in Lucee. Your code seems to work with Adobe ColdFusion. I created a gist on TryCF showing this.
<cfscript>
hierarchyStruct = {};
hierarchyStruct.0 = {
"groupName" = "top level"
, "level" = "1"
, "levelName" = "region"
};
writeDump(hierarchyStruct);
keyResult = structFindKey(hierarchyStruct, "0", "one");
writeDump(keyResult);
newPath = 'hierarchyStruct' & keyResult[1].path;
writeDump(newPath);
foundKey = structGet(newPath);
writeDump(foundKey);
</cfscript>
That gist is using Adobe ColdFusion 11 and it will run. Change the engine to Lucee and it will error.
You can get around this error by changing the name of the request.hierarchyStruct.0 structure. Note that it is failing on that structure being named 0.
For example, I created another gist changing the name of that structure to a0 and it works using Lucee.
<cfscript>
hierarchyStruct = {};
hierarchyStruct.a0 = {
"groupName" = "top level"
, "level" = "1"
, "levelName" = "region"
};
writeDump(hierarchyStruct);
keyResult = structFindKey(hierarchyStruct, "a0", "one");
writeDump(keyResult);
newPath = 'hierarchyStruct' & keyResult[1].path;
writeDump(newPath);
foundKey = structGet(newPath);
writeDump(foundKey);
</cfscript>
Related
I currently try to automate nested SumoLogic forder creation as part of my custom module. I have to use this resource. I need to create a folder path similar to:
parent_folder_path = "SRE/Test/Troubleshooting"
and due to the fact that this variable will change between environments I cannot hardcode creation of the underlying resources. The problematic part is that all shown folders (SRE, Test, Troubleshooting) need to be created in a sequence because the latter needs id of the former (eg. Test folder needs id of already created SRE folder) to be created.
The end result at which I am aiming is automatically generated code as below:
resource "sumologic_folder" "SRE" {
provider = sumologic
name = "SRE"
description = ""
parent_id = "0000000000XXXXX"
}
resource "sumologic_folder" "Test" {
provider = sumologic
name = "Test"
description = ""
parent_id = sumologic_folder.SRE.id
}
resource "sumologic_folder" "Troubleshooting" {
provider = sumologic
name = "Troubleshooting"
description = ""
parent_id = sumologic_folder.Test.id
}
I tried an approach which uses templatefile() and local_file:
parent_directories.tftpl
%{~ for index, path_part in parent_folder_path ~}
%{~ if index == 0 ~}
resource "sumologic_folder" "${replace(path_part, " ", "_")}" {
provider = sumologic
name = "${path_part}"
description = ""
parent_id = "${root_folder_id}"
}
%{~ else }
resource "sumologic_folder" "${replace(path_part, " ", "_")}" {
provider = sumologic
name = "${path_part}"
description = ""
parent_id = sumologic_folder.${replace(parent_folder_path[index - 1], " ", "_")}.id
}
%{~ endif ~}
%{~ endfor ~}
main.tf
resource "local_file" "parent_directories" {
content = templatefile("${path.module}/parent_directories.tftpl", { parent_folder_path = split("/", var.parent_folder_path), root_folder_id = var.root_folder_id })
filename = "${path.module}/parent_directories.tf"
}
and the file was correctly generated during terraform apply run but I was not able to include it in the scope of the run dynamically.
Does anyone know how to handle such usecase?
Thanks in advance for all help.
Best Regards,
Rafal.
I understand what you are trying to achieve - you want to create multiple resources of the same type, but relying on each one of them created before (previous one on the list), at the same time not knowing how many there would be (more folders in path). I am afraid it is not how Terraform works. You would create a cycle between the list or map of the same resources.
That said, however, I can offer you the ugly solution. If you can limit to some number of subdirectories, let's say up to five or ten levels, you can do that in the code that will create three folders if there are three dirs in the path, and four if there are four, and so on. You just stop creating resources if this level is empty.
Let's say you have a sumo module:
variable "parent_path" {}
variable "name" {}
data "sumologic_folder" "parent" {
path = var.parent_path
}
resource "sumologic_folder" "folder" {
provider = sumologic
name = var.name
description = ""
parent_id = data.sumologic_folder.parent.id
}
output "path" {
value = "${var.path}/${var.name}"
}
And then you can split the path to the list of folders and create as many resources as there are folders in the path, for example: AA/BB/CC/DD = 4 sumofolders.
locals {
desired_path = "SRE/Test/Troubleshooting" # example - 3 folders
regex = regexall("[^//]+", local.desired_path)
path0 = "/"
}
module "sumo" {
source = "./sumo"
name = local.regex[0]
parent_path = local.path0 # var.parent_path
}
module "sumo_child_1" {
source = "./sumo"
count = try(local.regex[1], null) == null ? 0 : 1
name = try(local.regex[1], "none")
parent_path = module.sumo.path
}
module "sumo_child_2" {
source = "./sumo"
count = try(local.regex[2], null) == null ? 0 : 1
name = try(local.regex[2], "none")
parent_path = module.sumo_child_1.path
}
module "sumo_child_3" { # this is NOT going to be even created in our example
source = "./sumo"
count = try(local.regex[3], null) == null ? 0 : 1
name = try(local.regex[3], "none")
parent_path = module.sumo_child_2.path
}
# and so on... if there are no more folders in the path, the resources won't be created anyway.
Now let me say that again, this is a very ugly solution... but it works. Cheers.
I am using Terraform to Invoke a lambda function, and need to pass an input JSON which includes a list of string values.
data "aws_lambda_invocation" "invo6" {
function_name = "my_function"
input = <<JSON
{
"pairs":[
{
"principal":"arn:aws:iam::12345678901:role/myRole",
"databases":[
"my_db_apple", "my_db_banana", "my_db_orange"
]
}
]
}
JSON
}
Instead of hard-coding these database names, I want to pull in from a map that already exists elsewhere in my tf files.
variable "gluedb_map" {
type = map(map(string))
default = {
"apple" = {
description = "my apple db"
catalog = ""
location_uri = "s3://mybucket/"
params = ""
}
"banana" = {
description = "my banana db"
catalog = ""
location_uri = "s3://anotherpath/"
params = ""
}
I tried swapping out the 'databases' code for this :
input = <<JSON
{
"pairs":[
{
"principal":"arn:aws:iam::12345678901:role/myRole",
${jsonencode("databases": [for each in var.gluedb_map : "my_db_${each}"], )}
}
]
}
JSON
but i then get error :
A comma is required to separate each function argument from the next.
Can anyone spot where I'm going wrong ?
Thanks
If you're just interested in accessing the keys of the map then you can use the keys function to return a list of keys. You can then combine that with formatlist to interpolate each list item with a string.
I'd also recommend using a HCL map for the wider data structure and then encoding to JSON rather than trying to JSON encode a section of it and having to mangle things to get it in a suitable shape.
A fully worked example then looks something like this:
variable "gluedb_map" {
type = map(map(string))
default = {
"apple" = {
description = "my apple db"
catalog = ""
location_uri = "s3://mybucket/"
params = ""
}
"banana" = {
description = "my banana db"
catalog = ""
location_uri = "s3://anotherpath/"
params = ""
}
}
}
output "json" {
value = jsonencode({
pairs: [
{
principal = "arn:aws:iam::12345678901:role/myRole"
databases = formatlist("my_db_%s", keys(var.gluedb_map))
}
]
})
}
Applying this will output the following:
json = {"pairs":[{"databases":["my_db_apple","my_db_banana"],"principal":"arn:aws:iam::12345678901:role/myRole"}]}
You can try to use keys, formatlist and join to get:
${jsonencode("databases": [join("," , formatlist("my_db_%s", keys(var.gluedb_map)) )}
When I am doing the search, the results always contain only five records (in ascending order). I need to get a list of all files in the folder. What am I doing wrong?
FileSearch fileSearch = new FileSearch();
FileSearchBasic fileSearchBasic = new FileSearchBasic();
// Specify the folder in which the search is to be done.
SearchMultiSelectField folderFilter = new SearchMultiSelectField
{
#operator = SearchMultiSelectFieldOperator.anyOf,
operatorSpecified = true
};
RecordRef[] folder = new RecordRef[1];
folder[0] = new RecordRef
{
// Internal id of the folder where pospay files are stored.
internalId = ns.DataCollection["netSuite:PositivePayFolderInternalId"]
};
folderFilter.searchValue = folder;
fileSearchBasic.folder = folderFilter;
fileSearch.basic = fileSearchBasic;
var result = NSBase.Client.Service.search(fileSearch);
var recordList = result.recordList; // this has ony five results, why?
I found the issue, 'PageSize' was set to 5 in the service client's constructor.
In my user interface (angularjs) I create new row. Each row have file upload button. I want to upload all files together with metadata and save each row in one call. The complex object which I post to Nodejs API is somewhat like below
var activity = {
"Id" : 1,
"Name" : "Test",
"Steps" : [
{
"StepId":1,
"FileUrl": {fileObject} // this property if bound with the file upload directive 'ng-file-upload' by Daniel Farid
"Description" : "Save this file"
},
{
"StepId":2,
"FileUrl": {fileObject} // this property if bound with the file upload directive 'ng-file-upload' by Daniel Farid
"Description" : "Save this file2"
}
]
}
This JSON will be posted to Node js API. On Nodejs side I am using multer to save the uploaded files to server. I get all the files in API using multer's .any() method, but I get the posted object without Steps[x].FileUrl property.
The file object that has the information about the field name in which this file was added. Below is the info I see in debugger.
Array[2]
length:2
[0]:Object
destination:"C:\DeleteThis\"
encoding:"7bit"
fieldname:"Steps[0][FileUrl]"
filename:"ed13d2a61cb38c43f1f46a221855a896"
mimetype:"image/png"
originalname:"deploy.png"
path:"C:\DeleteThis\ed13d2a61cb38c43f1f46a221855a896"
size:2347
[1]:Object
Now what I want to do it, since My complex object that is posted does not have Steps[0].FileUrl property, I want to iterate for each file (i.e. req.files) and use fieldname to create this property and assign the originalName as value to it.
How I am trying to do it
var deployment = req.body;
if(req.files){
var app = _config.getApplicationConfig(req.body.ApplicationId);
req.files.forEach(function(f){
//Move file to the deployment folder.
_utils.createDirIfNotExist(app.packageDir);
var newPath = _utils.DetermineFileName(f.originalname, app.packageDir);
_fs.renameSync(f.path, path.join(app.packageDir,newPath));
var newFileName = path.basename(newPath);
//set the file url to corresponding field
var evalExp = "deployment." + f.fieldname; //I get evalExpression as "deployment.Steps[0][FileUrl]"
eval(evalExp); //Here it fails saying FileUrl is not defined
evalExp = "deployment." + f.fieldname + "= \"" + newFileName.toString() + "\"";
eval(evalExp);
});
}
Does anyone know how can as assign the property to an object at run time?
I have found solution to this as below
I have wrote a function that converts the [] notation to . notation ie. myobj[myprop] to myobj.myprop
var convertToDotNotation = function (keyPath) {
var bracketSyntaxRegex = new RegExp(/\[([^0-9])\w+\]/g); //matches the javascript property defined in [] syntax but not an array
var matches = keyPath.match(bracketSyntaxRegex)
if(matches && matches.length > 0){
matches.forEach(function(p){
//replace '[' with '.' and ']' with emptyspace
var dotSyntax = p.replace("[",".").replace("]","");
keyPath = keyPath.replace(p,dotSyntax);
});
}
return keyPath;
}
This will give me the '.' notation which can dynamically create the property and set the value
var newFileName = "MyFile.pdf";
var evalExp = "deployment[0].[FileUrl]" ;
var temp = convertToDotNotation(evalExp);
eval(temp + "= \"" + newFileName + "\"");
Hope it helps someone.
I have the following code that when I run autoformat in ReSharper doesn't not get changed.
I thought Chop if long would cause a chop to occur if the right margin is exceeded.
If I turn on Chop always I get this.
This works, but I would rather not chop short statements like the first, which is what I assume Chop if long means.
Ideas?
Here are my ReSharper settings.
Turning on wrap long lines makes things even worse.
UPDATE1:
Here is the email I sent to JetBrains support.
The believe the central issues I'm facing is I understand the "chop always" setting, but I do not understand "chop if long" or "simple wrap". I have not found any documentation on what these settings mean, so I'm going off what I believe should be happening.
I am setting the "Wrap object collection and initializer".
Chop Always:
cdata.GetByIdData = new Category {
Id = "123",
Name = "category"
};
vdata.GetByIdData = new Vendor {
Id = "456",
Name = "vendor"
};
adata.GetByIdData.Add(new Account {
Id = "789",
Name = "account",
Balance = 5000
});
svc.ExecuteRequest(new AccountTransactionService.Add {
Kind = AccountTransaction.KIND_DEBIT,
Source = "789",
Destination = "dst",
Date = new DateTime(2011, 1, 1),
Categories = new List<AccountTransactionService.CreateCategory> {
new AccountTransactionService.CreateCategory {
Id = "123",
Amount = 200.50m
}
}
});
Chop If Long:
cdata.GetByIdData = new Category { Id = "123", Name = "category" };
vdata.GetByIdData = new Vendor { Id = "456", Name = "vendor" };
adata.GetByIdData.Add(new Account { Id = "789", Name = "account", Balance = 5000 });
svc.ExecuteRequest(new AccountTransactionService.Add { Kind = AccountTransaction.KIND_DEBIT, Source = "789", Destination = "dst", Date = new DateTime(2011, 1, 1), Categories = new List<AccountTransactionService.CreateCategory> { new AccountTransactionService.CreateCategory { Id = "123", Amount = 200.50m } } });
I would expect Chop If Long to look like this given a margin of 80:
cdata.GetByIdData = new Category { Id = "123", Name = "category" };
vdata.GetByIdData = new Vendor { Id = "456", Name = "vendor" };
adata.GetByIdData.Add(new Account { Id = "789", Name = "account", Balance = 5000 });
svc.ExecuteRequest(new AccountTransactionService.Add {
Kind = AccountTransaction.KIND_DEBIT,
Source = "789",
Destination = "dst",
Date = new DateTime(2011, 1, 1),
Categories = new List<AccountTransactionService.CreateCategory> {
new AccountTransactionService.CreateCategory {
Id = "123",
Amount = 200.50m
}
}
});
"Chop if long" works only when you turn on "Wrap long lines" option, so you should turn it on. I guess that http://youtrack.jetbrains.com/issue/RSRP-291146 prevented you from getting the formatting you wanted with "Wrap long lines" turned on. Well, it should be fixed in ReSharper 7.1 EAP - try and write us if you still have problems.
Change "Wrap object collection and initialisers" to "Simple Wrap", that should style your code in the way you want it to.
Unfortunately, I can't look up what Chop if long is supposed to do as the Resharper community site is blocked from work for some strange reason.
There is an issue logged with JetBrains that is depicting the same behavior you describe...
http://youtrack.jetbrains.com/issue/RSRP-291146