I am trying to create a process with Jira and confluence, using Scriptrunner Cloud.
I am trying to do the following :
I want to create a confluence page automatically with posfunction scriptrunner on Jira issue creation, and want to link this page and issue automatically too.
I already automatized creation with some code, but this code does not link page and issues automatically.
I am using following code :
def spaceKey = 'LTSE'
def result = get("/rest/api/3/issue/${issueKey}")
.header('Content-Type', 'application/json')
.asObject(Map)
if (result.status != 200) {
return "Error retrieving issue ${result}"
}
def title = "${result.body.key} - ${result.body.fields.summary}"
def rootContent = "<h2>Description</h2><p>${result.body.fields.description}</p>"
def parent = createConfluencePage(title, spaceKey, rootContent, null)
result.body.fields.subtasks.forEach { subtask ->
def subtaskResult = get("/rest/api/3/issue/${subtask.id}")
.header('Content-Type', 'application/json')
.asObject(Map)
if (subtaskResult.status != 200) {
logger.error("Error retrieving issue ${subtaskResult}")
}
def subtitle = "${subtask.key} - ${subtask.fields.summary}"
def pageContent = "<h2>Description</h2><p>${subtaskResult.body.fields.description}</p>"
createConfluencePage(subtitle, spaceKey, pageContent, parent)
}
String createConfluencePage(String pageTitle, String spaceKey, String pageContent, String parentPage) {
def params = [
type : "page",
title: pageTitle,
space: [
key: spaceKey
],
body : [
storage: [
value : pageContent.toString(),
representation: "storage"
]
]
]
if (parentPage != null) {
params["ancestors"] = [parentPage].collect { [id: parentPage.toString()] }
}
def pageResult = post('/wiki/rest/api/content')
.header('Content-Type', 'application/json')
.body(params)
.asObject(Map).body
if (pageResult.statusCode) {
logger.error("Failed to create a new page. Confluence responded with error code: {}", pageResult.statusCode)
} else {
logger.info("Successfully created a new space with id: {}", pageResult)
}
pageResult.id
}
parent
Is there anything I can do, to automatize linking?
please have in mind that I use Cloud Jira and confluence
Is it possible to add content to the Confluence page using Scriptrunner? If so, just add the link of the JIRA issue to the Confluence page. This will automatically be converted to a JIRA issue macro on the page and will create a link with the JIRA issue itself. See:
https://www.atlassian.com/blog/confluence/link-jira-issues-to-confluence-pages-automatically
This only works if you have created application links between JIRA and Confluence:
https://confluence.atlassian.com/adminjiracloud/using-applinks-to-link-to-other-applications-779295829.html
Related
I'm creating a file in Acumatica by calling an action from the API, so that I can retrieve the file in my application.
Is it possible to delete the file via API after I'm done with it? I'd rather not have it cluttering up my Acumatica database.
Failing this, is there a recommended cleanup approach for these files?
Found examples of how to delete a file from within Acumatica, as well as how to save a new version of an existing file! The below implementation saves a new version but has the deletion method commented out. Because I built this into my report generation process, I'm not later deleting the report via API, but it would be easy to translate a deletion into an action callable by the API.
private IEnumerable ExportReport(PXAdapter adapter, string reportID, Dictionary<String, String> parameters)
{
//Press save if the SO is not completed
if (Base.Document.Current.Completed == false)
{
Base.Save.Press();
}
PX.SM.FileInfo file = null;
using (Report report = PXReportTools.LoadReport(reportID, null))
{
if (report == null)
{
throw new Exception("Unable to access Acumatica report writer for specified report : " + reportID);
}
PXReportTools.InitReportParameters(report, parameters, PXSettingProvider.Instance.Default);
ReportNode reportNode = ReportProcessor.ProcessReport(report);
IRenderFilter renderFilter = ReportProcessor.GetRenderer(ReportProcessor.FilterPdf);
//Generate the PDF
byte[] data = PX.Reports.Mail.Message.GenerateReport(reportNode, ReportProcessor.FilterPdf).First();
file = new PX.SM.FileInfo(reportNode.ExportFileName + ".pdf", null, data);
//Save the PDF to the SO
UploadFileMaintenance graph = new UploadFileMaintenance();
//Check to see if a file with this name already exists
Guid[] files = PXNoteAttribute.GetFileNotes(Base.Document.Cache, Base.Document.Current);
foreach (Guid fileID in files)
{
FileInfo existingFile = graph.GetFileWithNoData(fileID);
if (existingFile.Name == reportNode.ExportFileName + ".pdf")
{
//If we later decide we want to delete previous versions instead of saving them, this can be changed to
//UploadFileMaintenance.DeleteFile(existingFile.UID);
//But in the meantime, for history purposes, set the UID of the new file to that of the existing file so we can save it as a new version.
file.UID = existingFile.UID;
}
}
//Save the file with the setting to create a new version if one already exists based on the UID
graph.SaveFile(file, FileExistsAction.CreateVersion);
//Save the note attribute so we can find it again.
PXNoteAttribute.AttachFile(Base.Document.Cache, Base.Document.Current, file);
}
//Return the info on the file
return adapter.Get();
}
The response from Acumatica:
S-b (Screen-base) API allows clean way of downloading report generated as file. C-b (Contract-base) simply does not have this feature added. I suggest you provided feedback here: feedback.acumatica.com (EDIT: Done! https://feedback.acumatica.com/ideas/ACU-I-1852)
I think couple of workaround are:
1) use s-b using login from c-b to generate report and get as file (see example below), or
2) create another method to delete the file once required report file is downloaded. For that, you will need to pass back FileID or something to identify for deletion.
example of #1
using (DefaultSoapClient sc = new DefaultSoapClient("DefaultSoap1"))
{
string sharedCookie;
using (new OperationContextScope(sc.InnerChannel))
{
sc.Login("admin", "123", "Company", null, null);
var responseMessageProperty = (HttpResponseMessageProperty)
OperationContext.Current.IncomingMessageProperties[HttpResponseMessageProperty.Name];
sharedCookie = responseMessageProperty.Headers.Get("Set-Cookie");
}
try
{
Screen scr = new Screen(); // add reference to report e.g. http://localhost/Demo2018R2/Soap/SO641010.asmx
scr.CookieContainer = new System.Net.CookieContainer();
scr.CookieContainer.SetCookies(new Uri(scr.Url), sharedCookie);
var schema = scr.GetSchema();
var commands = new Command[]
{
new Value { LinkedCommand = schema.Parameters.OrderType, Value = "SO" },
new Value { LinkedCommand = schema.Parameters.OrderNumber, Value = "SO004425" },
schema.ReportResults.PdfContent
};
var data = scr.Submit(commands);
if(data != null && data.Length > 0)
{
System.IO.File.WriteAllBytes(#"c:\Temp\SalesOrder.pdf",
Convert.FromBase64String(data[0].ReportResults.PdfContent.Value));
}
}
finally
{
sc.Logout();
}
}
Hope this helps. Also, it would be great if you update the stackover post based on these suggestions.
Thanks
Nayan Mansinha
Lead - Developer Support | Acumatica
Went through the java docs of getAttribute. Couldn't understand the point mentioned as :
Finally, the following commonly mis-capitalized attribute/property
names are evaluated as expected: "class" "readonly"
Could someone confirm if webElement.getAttribute("class") shall return the class name of the element or not?
Edit : On trying this myself
System.out.println("element " + webElement.getAttribute("class"));
I am getting
org.openqa.selenium.NoSuchElementException
Note : The element does exist on the screen as I can perform actions successfully on the element :
webElement.click(); //runs successfully
Code:
WebElement webElement = <findElement using some locator strategy>;
System.out.println("element " + webElement.getAttribute("class"));
So the answer to the problem was answered on GitHub in the issues list of appium/java-client by #SergeyTikhomirov. Simple solution to this is accessing the className property as following :
webElement.getAttribute("className")); //instead of 'class' as mentioned in the doc
Method core implementation here : AndroidElement
According to this answer, yes you are doing it right. Your org.openqa.selenium.NoSuchElementException is thrown because selenium can't find the element itself.
The sidenote you have posted, about webElement.click() actually working, is unfortunately not included in the code you have posted. Since it is not a part of the actual question, I leave this answer without adressing it.
public String getStringAttribute(final String attr)
throws UiObjectNotFoundException, NoAttributeFoundException {
String res;
if (attr.equals("name")) {
res = getContentDesc();
if (res.equals("")) {
res = getText();
}
} else if (attr.equals("contentDescription")) {
res = getContentDesc();
} else if (attr.equals("text")) {
res = getText();
} else if (attr.equals("className")) {
res = getClassName();
} else if (attr.equals("resourceId")) {
res = getResourceId();
} else {
throw new NoAttributeFoundException(attr);
}
return res;
}
i try to connect to webpage using Groovy,
i tried this, it works when the URL is www.google.fr
String.metaClass.browse {
def handler = [(~/^Mac OS.*/) : { "open $it".execute() }, (~/^Windows.*/) : { "cmd /C start $it".execute() },
(~/.*/) : {
//--- assume Unix or Linux
def browsers = [ 'firefox'.'chrome' ]
//--- find a browser we know the location of
def browser = browsers.find {
"which $it".execute().waitFor() == 0
}
//--- and run it if one found
if( browser )
"$browser $it".execute()
}
]
def k = handler.find { k, v -> k.matcher( System.properties.'os.name' ).matches() }
k?.value( delegate )
}
www.google.fr".browse.()
if i put URL which download a file it throw Compilation error. Thank you for your help.
If you want to add it as a method to the String class, you can do:
String.metaClass.browse = { ->
java.awt.Desktop.desktop.browse(new URI(delegate))
}
Then calling
"http://www.google.com".browse()
Will open your default browser
Given the following object structure:
{
key1: "...",
key2: "...",
data: "..."
}
Is there any way to get this object from a CouchDB by quering both key1 and key2 without setting up two different views (one for each key) like:
select * from ... where key1=123 or key2=123
Kind regards,
Artjom
edit:
Here is a better description of the problem:
The object described above is a serialized game state. A game has exactly one creator user (key1) and his opponent (key2). For a given user I would like to get all games where he is involved (both as creator and opponent).
Emit both keys (or only one if equal):
function(doc) {
if (doc.hasOwnProperty('key1')) {
emit(doc.key1, 1);
}
if (doc.hasOwnProperty('key2') && doc.key1 !== doc.key2) {
emit(doc.key2, 1);
}
}
Query with (properly url-encoded):
?include_docs=true&key=123
or with multiple values:
?include_docs=true&keys=[123,567,...]
UPDATE: updated to query multiple values with a single query.
You could create a CouchDB view which produces output such as:
["key1", 111],
["key1", 123],
["key2", 111],
["key2", 123],
etc.
It is very simple to write a map view in javascript:
function(doc) {
emit(["key1", doc["key1"]], null);
emit(["key2", doc["key2"]], null);
}
When querying, you can query using multiple keys:
{"keys": [["key1", 123], ["key2", 123]]}
You can send that JSON as the data in a POST to the view. Or preferably use an API for your programming language. The results of this query will be each row in the view that matches either key. So, every document which matches on both key1 and key2 will return two rows in the view results.
I also was struggling with simular question, how to use
"select * from ... where key1=123 or key2=123".
The following view would allow you to lookup customer documents by the LastName or FirstName fields:
function(doc) {
if (doc.Type == "customer") {
emit(doc.LastName, {FirstName: doc.FirstName, Address: doc.Address});
emit(doc.FirstName, {LastName: doc.LastName, Address: doc.Address});
}
}
I am using this for a web service that queries all my docs and returns every doc that matches both the existence of a node and the query. In this example I am using the node 'detail' for the search. If you would like to search a different node, you need to specify.
This is my first Stack Overflow post, so I hope I can help someone out :)
***Python Code
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import httplib, json
from tornado.options import define,options
define("port", default=8000, help="run on the given port", type=int)
class MainHandler(tornado.web.RequestHandler):
def get(self):
db_host = 'YOUR_COUCHDB_SERVER'
db_port = 5984
db_name = 'YOUR_COUCHDB_DATABASE'
node = self.get_argument('node',None)
query = self.get_argument('query',None)
cleared = None
cleared = 1 if node else self.write('You have not supplied an object node.<br>')
cleared = 2 if query else self.write('You have not supplied a query string.<br>')
if cleared is 2:
uri = ''.join(['/', db_name, '/', '_design/keysearch/_view/' + node + '/?startkey="' + query + '"&endkey="' + query + '\u9999"'])
connection = httplib.HTTPConnection(db_host, db_port)
headers = {"Accept": "application/json"}
connection.request("GET", uri, None, headers)
response = connection.getresponse()
self.write(json.dumps(json.loads(response.read()), sort_keys=True, indent=4))
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r"/", MainHandler)
]
settings = dict(
debug = True
)
tornado.web.Application.__init__(self, handlers, **settings)
def main():
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(Application())
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == '__main__':
main()
***CouchDB Design View
{
"_id": "_design/keysearch",
"language": "javascript",
"views": {
"detail": {
"map": "function(doc) { var docs = doc['detail'].match(/[A-Za-z0-9]+/g); if(docs) { for(var each in docs) { emit(docs[each],doc); } } }"
}
}
}
I'm using CouchApp to build an easy web application that allows to upload and manage pictures. The actual image file is stored as attachment to the doc like show below.
{
"_id":"09fe82d75a26f9aa5e722d6b220180d2",
"_rev":"2-5797b822c83b9d41545139caa592f611",
"data":"some additional fields with info about the image",
"_attachments":
{
"foo.jpg":
{
"stub":true,
"content_type":"image/jpeg",
"length":23721
}
}
}
But for integrating the image in html i need the url to the attachment. How do i get this url?
I'm using evently and mustache for generating the web pages. Below is the data.js for reading the data:
function(data) {
return {
items : data.rows.map(function(r) {
return {
id : r.value._id,
rev : r.value._rev,
title : r.value.description,
url : "how am i supposed to do this?"
};
})
};
};
The URL to the attachment would be http://domain/database/09fe82d75a26f9aa5e722d6b220180d2/foo.jpg
If your filenames are dynamic, you would have to iterate the _attachments object and fetch the keys on your way - that's where your filename would be.
for(var filename in r.value._attachments){break;}
// ...
url : '<database>/' + r.value._id +'/'+filename;