I have a Feature name such as: "This / is / the / name / of my feature ". Rally throws me an error when I try to mention this name as a query string. Is there a way to get around this?
Where do you get the error? I tested this query in WS API, a custom grid and a Ruby script using a feature named "feat/ure"
(Name = feat/ure)
and the query worked in all 3 cases.
Here is the Ruby code that query on a feature with forward slash in the name and assigns a new story to it:
require 'rally_api'
#Setup custom app information
headers = RallyAPI::CustomHttpHeader.new()
headers.name = "My Utility"
headers.vendor = "Nick M RallyLab"
headers.version = "1.0"
# Connection to Rally
config = {:base_url => "https://rally1.rallydev.com/slm"}
config[:username] = "user#co.com"
config[:password] = "secret"
config[:workspace] = "W"
config[:project] = "P"
config[:headers] = headers #from RallyAPI::CustomHttpHeader.new()
#rally = RallyAPI::RallyRestJson.new(config)
obj = {}
obj["Name"] = "new story efd3"
new_s = #rally.create("hierarchicalrequirement", obj)
query = RallyAPI::RallyQuery.new()
query.type = "portfolioitem"
query.fetch = "Name,FormattedID"
query.workspace = {"_ref" => "https://rally1.rallydev.com/slm/webservice/v2.0/workspace/111" }
query.project = {"_ref" => "https://rally1.rallydev.com/slm/webservice/v2.0/project/222" }
query.query_string = "(Name = \"feat/ure\")"
result = #rally.find(query)
feature = result.first
puts feature
field_updates={"PortfolioItem" => feature}
new_s.update(field_updates)
You can also utilize the html replacements for those characters.
Related
I'm very new to Python coding and have run into an issue while trying to upgrade some code. I'm working with an app that pulls data via an API from stored data from a scan.
here is the code as it sits working
def _collect_one_host_scan_info(self, host_id, sid, scan_info):
"""
The method to collect all the vulnerabilities of one host and generate the event data.
"""
count = 0
host_uri = self.endpoint + '/' + str(sid) + '/hosts/' + str(host_id)
result = self.client.request(host_uri).get("content")
# if there is exception in request, return None
if result is None:
_LOGGER.info("There is exception in request, return None")
return None
else:
host_info = result.get("info", {})
host_end_time = host_info.get("host_end", "")
if self.ckpt.is_new_host_scan(host_end_time,
self.config.get("start_date")):
self.source = self.url + self.endpoint + '/' + str(
sid) + '/hosts/' + str(host_id)
for vuln in result.get("vulnerabilities", []):
vuln["sid"] = sid
vuln["host_id"] = host_id
#get the port info
plugin_id = vuln.get("plugin_id", "")
port_info = []
if plugin_id:
plugin_uri = "{}/plugins/{}".format(host_uri,
plugin_id)
plugin_outputs = self.client.request(plugin_uri).get(
"content", {}).get("outputs")
ports = []
for output in plugin_outputs:
ports.extend(output.get("ports", {}).keys())
for port in ports:
port_elem = {}
port_items = re.split(r"\s*/\s*", port)
port_elem["port"] = int(port_items[0])
if port_items[1]:
port_elem["transport"] = port_items[1]
if port_items[2]:
port_elem["protocol"] = port_items[2]
port_info.append(port_elem)
vuln = dict(vuln, **scan_info)
vuln = dict(vuln, **host_info)
if port_info:
vuln["ports"] = port_info
entry = NessusObject(
vuln.get("timestamp"), self.sourcetype, self.source,
vuln)
self._print_stream(entry)
count += 1
return count
The data that is being pulled from looks like this
"outputs": [
{
"ports": {
"445 / tcp / cifs": [
{
"hostname": "computer.domain.com"
}
]
},
"has_attachment": 0,
"custom_description": null,
"plugin_output": "\nPath : c:\\program files (x86)\\folder\\bin\\fax.exe\nUsed by services : RFDB\nFile write allowed for groups : Domain Users\nFull control of directory allowed for groups : Domain Users\n\nPath : c:\\program files (x86)\\folder\\bin\\faxrpc.exe\nUsed by services : RFRPC\nFile write allowed for groups : Domain Users\nFull control of directory allowed for groups : Domain Users\n\nPath : c:\\program files (x86)\\folder\\bin\\faxserv.exe\nUsed by services : RFSERVER\nFile write allowed for groups : Domain Users\nFull control of directory allowed for groups : Domain Users\n`,
"hosts": null,
"severity": 3
}
with the working code the return is
ports{}.port 445
ports{}.protocol tcp
ports{}.transport cifs
What I really would like is to grab the "plugin_output" data with the "port" data
I'm currently just trying to replace the "port" data with "plugin_output" data
#get the output info
plugin_id = vuln.get("plugin_id", "")
output_info = []
if plugin_id:
plugin_uri = "{}/plugins/{}".format(host_uri,
plugin_id)
plugin_outputs = self.client.request(plugin_uri).get(
"content", {}).get("outputs")
outputs = []
for output in plugin_outputs:
outputs.extend(output.get("plugin_output", "").keys())
for plugin in plugin_outputs:
plugin_elem = {}
plugin_items = re.split(r"nPath\s*", plugin)
plugin_elem["location1"] = plugin_items[0]
if plugin_items[1]:
plugin_elem["location2"] = plugin_items[1]
if plugin_items[2]:
plugin_elem["location3"] = plugin_items[2]
output_info.append(plugin_elem)
vuln = dict(vuln, **scan_info)
vuln = dict(vuln, **host_info)
if output_info:
vuln["plugin_output"] = output_info
entry = NessusObject(
vuln.get("timestamp"), self.sourcetype, self.source,
vuln)
self._print_stream(entry)
count += 1
what I've done as you can see if just replace the "ports" data with "plugin_output" data and the error received is
AttributeError: 'unicode' object has no attribute key
Well after further efforts I was able to figure out what I needed to do with the code. It was much easier than I thought it would be but sometime when learning a new language its hard to envision what is needed. Code posted below.
def _collect_one_host_scan_info(self, host_id, sid, scan_info):
"""
The method to collect all the vulnerabilities of one host and generate
the event data.
"""
count = 0
host_uri = self.endpoint + '/' + str(sid) + '/hosts/' + str(host_id)
result = self.client.request(host_uri).get("content")
# if there is exception in request, return None
if result is None:
_LOGGER.info("There is exception in request, return None")
return None
else:
host_info = result.get("info", {})
host_end_time = host_info.get("host_end", "")
if self.ckpt.is_new_host_scan(host_end_time,
self.config.get("start_date")):
self.source = self.url + self.endpoint + '/' + str(
sid) + '/hosts/' + str(host_id)
for vuln in result.get("vulnerabilities", []):
vuln["sid"] = sid
vuln["host_id"] = host_id
plugin_id = vuln.get("plugin_id", "")
# get plugin_output data
plugin_output_info = []
if plugin_id:
plugin_uri = "{}/plugins/{}".format(host_uri,
plugin_id)
plugin_outputs = self.client.request(plugin_uri).get(
"content", {}).get("outputs", [])
data_output = []
for output in plugin_outputs:
items = output.get("plugin_output", 'no value')
item = str(items)
#clean = re.sub('[^a-zA-Z0-9-()_*.(:\\)]', ' ', item)
plugin_output_info.append(item)
# get the port info
port_info = []
if plugin_id:
plugin_uri = "{}/plugins/{}".format(host_uri,
plugin_id)
plugin_outputs = self.client.request(plugin_uri).get(
"content", {}).get("outputs", [])
ports = []
for output in plugin_outputs:
ports.extend(output.get("ports", {}).keys())
for port in ports:
port_elem = {}
port_items = re.split(r"\s*/\s*", port)
port_elem["port"] = int(port_items[0])
if port_items[1]:
port_elem["transport"] = port_items[1]
if port_items[2]:
port_elem["protocol"] = port_items[2]
port_info.append(port_elem)
vuln = dict(vuln, **scan_info)
vuln = dict(vuln, **host_info)
if port_info:
vuln["ports"] = port_info
if plugin_output_info:
vuln["plugin_output"] = plugin_output_info
entry = NessusObject(
vuln.get("timestamp"), self.sourcetype, self.source,
vuln)
self._print_stream(entry)
count += 1
return count
I am using Docusign to add a signature my PDF documents in c#.
I have some html file, I add to end of html div with text "SignHere" that Docusign will recognize the zone for signature, but the problem that after converting html to pdf and send Docusign, I see that "SignHere" option in all pages, not the last one.
What I am wrong wrong here?
My code, after converting html to pdf file:
if (System.IO.File.Exists(PdfPath))
{
byte[] fileBytes = System.IO.File.ReadAllBytes(PdfPath);
EnvelopeDefinition envDef = new EnvelopeDefinition();
envDef.EmailSubject = envDefEmailSubject;
envDef.EventNotification = new EventNotification();
envDef.EventNotification.Url = envDefEventNotificationUrl;
envDef.EventNotification.LoggingEnabled = "true";
envDef.EventNotification.IncludeDocuments = "true";
envDef.EventNotification.RequireAcknowledgment = "true";
envDef.EventNotification.IncludeCertificateWithSoap = "false";
envDef.EventNotification.RequireAcknowledgment = "true";
envDef.EventNotification.UseSoapInterface = "false";
envDef.EventNotification.EnvelopeEvents = new List<EnvelopeEvent>();
EnvelopeEvent envelopeEventSent = new EnvelopeEvent();
envelopeEventSent.EnvelopeEventStatusCode = "sent";
envDef.EventNotification.EnvelopeEvents.Add(envelopeEventSent);
EnvelopeEvent envelopeEventDelivered = new EnvelopeEvent();
envelopeEventDelivered.EnvelopeEventStatusCode = "delivered";
envDef.EventNotification.EnvelopeEvents.Add(envelopeEventDelivered);
EnvelopeEvent envelopeEventSentCompleted = new EnvelopeEvent();
envelopeEventSentCompleted.EnvelopeEventStatusCode = "completed";
envDef.EventNotification.EnvelopeEvents.Add(envelopeEventSentCompleted);
Document doc = new Document();
doc.DocumentBase64 = System.Convert.ToBase64String(fileBytes);
doc.Name = docName;
doc.DocumentId = docDocumentId;
envDef.Documents = new List<Document>();
envDef.Documents.Add(doc);
Signer signer = new Signer();
signer.Email = Email;
signer.Name = signerName + LeadName;
signer.RecipientId = signerRecipientId;
signer.Tabs = new Tabs();
//Custom Field For LeadId and PdfName
envDef.CustomFields = new CustomFields();
envDef.CustomFields.TextCustomFields = new List<TextCustomField>();
TextCustomField textCustomFieldLeadId = new TextCustomField();
textCustomFieldLeadId.Name = "LeadId";
textCustomFieldLeadId.Value = LeadId;
textCustomFieldLeadId.Required = "false";
textCustomFieldLeadId.Name = "false";
envDef.CustomFields.TextCustomFields.Add(textCustomFieldLeadId);
TextCustomField textCustomFieldSignedPdfName = new TextCustomField();
textCustomFieldSignedPdfName.Name = "SignedPdfName";
textCustomFieldSignedPdfName.Value = SignedPdfName;
textCustomFieldSignedPdfName.Required = "false";
textCustomFieldSignedPdfName.Name = "false";
envDef.CustomFields.TextCustomFields.Add(textCustomFieldSignedPdfName);
if (SignHereExist)
{
signer.Tabs.SignHereTabs = new List<SignHere>();
SignHere signHere = new SignHere();
signHere.RecipientId = signHereRecipientId;
signHere.AnchorXOffset = signHereAnchorXOffset;
signHere.AnchorYOffset = signHereAnchorYOffset;
signHere.AnchorIgnoreIfNotPresent = signHereAnchorIgnoreIfNotPresent;
signHere.AnchorUnits = "inches";
signHere.AnchorString = signHereAnchorString;
signer.Tabs.SignHereTabs.Add(signHere);
envDef.Recipients = new Recipients();
envDef.Recipients.Signers = new List<Signer>();
envDef.Recipients.Signers.Add(signer);
envDef.Status = "sent";
ApiClient apiClient = new ApiClient("https://demo.docusign.net/restapi");
DocuSign.eSign.Client.Configuration cfi = new DocuSign.eSign.Client.Configuration(apiClient);
string authHeader = "{\"Username\":\"" + x+ "\", \"Password\":\"" + x+ "\", \"IntegratorKey\":\"" + x+ "\"}";
cfi.AddDefaultHeader("X-DocuSign-Authentication", authHeader);
EnvelopesApi envelopesApi = new EnvelopesApi(cfi);
EnvelopeSummary envelopeSummary = envelopesApi.CreateEnvelope(accountID, envDef);
}
You are using Docusign Auto-Place (Anchor Tagging) in your request.
signHere.AnchorString = signHereAnchorString;
This will trigger a scan on the text in the document. If the scan finds the text specified in the variable signHereAnchorString anywhere in the document, it automatically places the "SignHere" option next to the text. That is the reason you are seeing "SignHere" option on all pages
You have couple of options if you want to place the Tag only on the last page
Option 1 - Using Anchor Tags: (See documentation here)
Modify your document to contain a unique string where you want to place the Signature tag. In this case, you could add the text "SignHereLastPage" in white font color (so that it isn't visible in the document) to where you want to place the Signature tag on the Document. Use "SignHereLastPage" as the anchor string.
You will just need to change one line in your code
signHere.AnchorString = "SignHereLastPage";
Option 2 - Fixed (or Absolute) Positioning (See documentation here)
You can use Absolute position of Tags and specify where you want to place the signature Tag. See Api recipe here
signer.Tabs.SignHereTabs = new List<SignHere>();
SignHere signHere = new SignHere();
signHere.DocumentId =docDocumentId;
signHere.PageNumber = "1"; // Specify the last Page number here.
signHere.RecipientId = signHereRecipientId;
signHere.XPosition = "100"; //You can adjust this based on your document
signHere.YPosition = "100"; //You can adjust this based on your document
signer.Tabs.SignHereTabs.Add(signHere);
So I have this query using Rally:
query_result = stuff.slm.find(:hierarchical_requirement, :project => project, :workspace => stuff.workspace, :project_scope_up => false, :project_scope_down => true){ equal :name, parent_name.strip }
and then I do,
parent = query_result.results.first
and I am curious to know what kind of object is assigned to the parent. I am not on the exceptions list in Rally so unable to run the script. But I am writing an SSO integration for this script and having some problems. I feel my problem would be solved if I get to inspect the "parent" because the function is returning this "parent". If anyone has any information on this , please share. Thanks!
A story (HierarchicalRequirement) object is assigned. Using Rally Ruby REST toolkit
The code below prints:
my_story: abc, FormattedID: US86, _ref:https://rally1.rallydev.com/slm/webservice/v2.0/hierarchicalrequirement/12891971030
.
#rally = RallyAPI::RallyRestJson.new(config)
some_name = "abc"
query = RallyAPI::RallyQuery.new()
query.type = :story
query.fetch = "FormattedID"
query.query_string = "(Name = \"#{some_name}\")"
results = #rally.find(query)
my_story = results.first
puts "my_story: #{my_story}, FormattedID: #{my_story["FormattedID"]}, _ref:#{my_story["_ref"]}"
If in your code you try to find a user story based on FormattedID, here is an example. It does not really matter if it is a parent or not, since a parent of a story is a story, and I used "story" instead of "parent" in my script for clarity. A FormattedID is entered as a command line argument in this example. Notice I use story = result.first, and not story=result. Here is a screenshot of the output:
require 'rally_api'
#Setup custom app information
headers = RallyAPI::CustomHttpHeader.new()
headers.name = "find story"
headers.vendor = "Nick M RallyLab"
headers.version = "1.0"
# Connection to Rally
config = {:base_url => "https://rally1.rallydev.com/slm"}
config[:username] = "user#co.com"
config[:password] = "secret"
config[:workspace] = "X"
config[:project] = "Y"
config[:version] = "v2.0"
config[:headers] = headers #from RallyAPI::CustomHttpHeader.new()
unless ARGV.length == 1
puts "enter one argument, e.g. US123, with no spaces"
exit
end
story = nil
story_id = ARGV[0]
puts "find story by FormattedID: #{story_id}"
#rally = RallyAPI::RallyRestJson.new(config)
query = RallyAPI::RallyQuery.new()
query.type = :story
query.fetch = "Name,FormattedID,ScheduleState"
query.workspace = {"_ref" => "https://rally1.rallydev.com/slm/webservice/v2.0/workspace/1111.js" }
query.project = {"_ref" => "https://rally1.rallydev.com/slm/webservice/v2.0/project/2222.js" }
query.project_scope_up = true
query.project_scope_down = true
query.query_string = "(FormattedID = \"#{story_id}\")"
result = #rally.find(query)
if(result.length > 0)
puts "found the story"
story = result.first
puts "FormattedID: #{story["FormattedID"]}, Name: #{story["Name"]}, ScheduleState: #{story["ScheduleState"]}"
else
puts "did not find a story with FormattedID #{story_id}"
end
puts story.class
puts story.inspect
I am trying to match Project name in my query and also trying to print the name of the project associated with each feature record. I know there are plenty of answers but I couldn't find anything that could help me. I am trying to do something like this:
pi_query.type = "portfolioitem"
pi_query.fetch="Name,FormattedID,Owner,c_ScopingTeam,c_AspirationalRelease,c_AssignedProgram,Tags"
#To be configured as per requirement
pi_query.project_scope_up = false
pi_query.project_scope_down = false
pi_query.order = "FormattedID Asc"
pi_query.query_string = "(Project.Name = \"Uni - Serviceability\")"
pi_results = #rally.find(pi_query)
I am trying to match the project name but it simply doesn't work, I also tried printing the name of the project, i tried Project.Name, Project.Values or simply Project. But it doesn't work. I am guessing it is because of my query type which is "portfolioItem" and I can't change my type because I am getting all other attribute values correctly.
Thanks.
Make sure to fetch Project, e.g: feature_query.fetch = "Name,FormattedID,Project"
and this should work:
feature_query.query_string = "(Project.Name = \"My Project\")"
Here is an example where a feature is found by project name.
require 'rally_api'
#Setup custom app information
headers = RallyAPI::CustomHttpHeader.new()
headers.name = "create story in one project, add it to a feature from another project"
headers.vendor = "Nick M RallyLab"
headers.version = "1.0"
# Connection to Rally
config = {:base_url => "https://rally1.rallydev.com/slm"}
config[:username] = "user#co.com"
config[:password] = "secret"
config[:workspace] = "W"
config[:project] = "Product1"
config[:headers] = headers #from RallyAPI::CustomHttpHeader.new()
#rally = RallyAPI::RallyRestJson.new(config)
obj = {}
obj["Name"] = "new story xyz123"
new_s = #rally.create("hierarchicalrequirement", obj)
query = RallyAPI::RallyQuery.new()
query.type = "portfolioitem"
query.fetch = "Name,FormattedID,Project"
query.workspace = {"_ref" => "https://rally1.rallydev.com/slm/webservice/v2.0/workspace/12352608129" }
query.query_string = "(Project.Name = \"Team Group 1\")"
result = #rally.find(query)
feature = result.first
puts feature
field_updates={"PortfolioItem" => feature}
new_s.update(field_updates)
I’m doing a tool to create Test folders (test suites) and test cases automatically from a xml file that a Jenkins job provides me.
Probably is a silly thing but I cannot find the solution, the thing is when the test folder name contains a white space, I’m not able to query it to see if already exists. I’ve trid to escape the whitespace and also encode it as url but nothing happens.
test_suite_name = ts_line["name"]
if test_suite_name.match(/\s/)
#test_suite_name_nows = test_suite_name.gsub(/ /,"\\ \\")
test_suite_name_nows = URI::encode(test_suite_name)
end
#==================== Querying the test suite in Rally ==========================
test_suite_query = RallyAPI::RallyQuery.new({:type => :testfolder, :query_string => "(Name = #{test_suite_name_nows})"})
Do you know which should be the format for being able to query test folder names as “Test folder1”??
I always get:
/usr/local/rvm/gems/ruby-2.0.0-p195/gems/rally_api-0.9.14/lib/rally_api/rally_json_connection.rb:153:in `send_request': (StandardError)
Error on request - https://rally1.rallydev.com/slm/webservice/1.42/testfolder.js -
{:errors=>["Could not parse: Cannot parse expression \"Version tests\" as a query"]
Thanks a lot.
Here is the format:
query.query_string = "(Name = \"My Test Folder 1\")"
and a full example:
require 'rally_api'
#Setup custom app information
headers = RallyAPI::CustomHttpHeader.new()
headers.name = "find test folder"
headers.vendor = "NickM RallyLab"
headers.version = "1.0"
# Connection to Rally
config = {:base_url => "https://rally1.rallydev.com/slm"}
config[:username] = "user#co.com"
config[:password] = "1984"
config[:workspace] = "W"
config[:project] = "P"
config[:headers] = headers #from RallyAPI::CustomHttpHeader.new()
#rally = RallyAPI::RallyRestJson.new(config)
query = RallyAPI::RallyQuery.new()
query.type = :test_folder
query.fetch = "Name,FormattedID"
query.workspace = {"_ref" => "https://rally1.rallydev.com/slm/webservice/1.43/workspace/12345.js" } #use yoru OID
query.project = {"_ref" => "https://rally1.rallydev.com/slm/webservice/1.43/project/67890.js" } #user yoru OID
query.page_size = 200 #optional - default is 200
query.limit = 1000 #optional - default is 99999
query.project_scope_up = false
query.project_scope_down = true
query.order = "Name Asc"
query.query_string = "(Name = \"My Test Folder 1\")"
results = #rally.find(query)
results.each do |t|
puts "Name: #{t["Name"]}, FormattedID: #{t["FormattedID"]}"
t.read
#.............
end