I'd like to automate the creation of new project when they involve mirroring existing one. To do this, I'd like to verify all the project info, then call the Scriptrunner built-in copy project script. Is there anyway to invoke the script externally, through REST or some other means?
There are a few options.
You can call the built-in script directly via rest from within your jira instance as if you were calling it remotely: https://scriptrunner.adaptavist.com/5.5.7/jira/builtin-scripts.html#_executing_built_in_scripts_remotely
But you can also implement the canned script within your own scripts. Either in your own custom Rest Endpoint or from a workflow post function.
You just have to import the com.onresolve.scriptrunner.canned.jira.admin.CopyProject class and supply all the inputs.
Here is a custom REST API I've actually implemented and have allowed some non-admins users to run (the script runs as admin).
It's called by just providing a json like this:
{"sourceKey":"ABC", targetKey: "NEW", "targetName":"New Project", "projectLead":"leadUsername"}
The REST API script:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.properties.APKeys
import com.onresolve.scriptrunner.canned.jira.admin.CopyProject
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonSlurper
import groovy.transform.BaseScript
import groovy.transform.Field
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
@BaseScript CustomEndpointDelegate delegate
@Field projectManager = ComponentAccessor.projectManager
@Field userManager = ComponentAccessor.userManager
@Field baseUrl = ComponentAccessor.applicationProperties.getString(APKeys.JIRA_BASEURL)
@Field defaultProjectTemplate = "MFGS"
@Field adminUserName = 'admin'
copyProject(httpMethod: "POST", groups: ["jira-administrators", "support-project-creators"]) { MultivaluedMap queryParams, String body ->
def bodyObject = new JsonSlurper().parseText(body)
if (!body) {
return Response.serverError().entity([success: false, output: ["No data supplied: targetKey and targetName are required"]]).build()
}
def sourceKey = bodyObject.sourceKey
def targetKey = bodyObject.targetKey
def targetName = bodyObject.targetName
def projectLead = bodyObject.projectLead
if (!sourceKey) {
sourceKey = defaultSupportProjectKey
}
def inputValidationResults = validateInputs(sourceKey, targetKey, targetName, projectLead)
if (inputValidationResults.errors) {
return Response.serverError().entity([success: false, output: inputValidationResults.errors]).build()
} else {
def projectCopyResults = copyProject(sourceKey, targetKey, targetName, projectLead)
if (projectCopyResults.success) {
return Response.created(new URI(projectCopyResults.link)).entity(projectCopyResults).build()
} else {
return Response.serverError().entity(projectCopyResults).build()
}
}
}
/**
* Validate the input parameters
* @param sourceKey
* @param targetKey
* @param targetName
* @param projectLead
* @return map with errors array. The array will be empty if the validation rules are all passed
*/
def validateInputs(sourceKey, targetKey, targetName, projectLead) {
def errors = []
if (!targetKey || !targetName) {
errors << "Missing parameter: targetKey and targetName are required"
}
if(!(targetKey ==~ /[A-Z][A-Z0-9]+/)){
errors << "Invalid parameter: targetKey ($targetKey) must start with a letter and contain only uppercase letters or numbers."
}
def sourceProject = projectManager.getProjectObjByKeyIgnoreCase(sourceKey)
if (!sourceProject) {
errors << "Invalid parameter: sourceKey ($sourceKey) provided is not a valid Jira project."
}
def targetProject = projectManager.getProjectObjByKeyIgnoreCase(targetKey)
if (targetProject) {
errors << "Invalid parameter: targetKey ($targetKey) provided already exists. Choose a different key"
}
targetProject = projectManager.getProjectObjByName(targetName)
if (targetProject) {
errors << "Invalid parameter: targetName ($targetName) provided is already in use for project with key=$targetProject.key. Choose a different name"
}
if (projectLead) {
def projectLeadUser = ComponentAccessor.userManager.getUserByKey(projectLead)
if (!projectLeadUser || !projectLeadUser.isActive()) {
errors << "Invalid parameter: projectLead ($projectLead) provided is not a valid Jira user"
}
}
return [errors: errors]
}
/**
* Call the scriptRunner Built-in script as the 'admin' with some default parameters to create an empty copy of the project.
* Note that the copyscript handles custom field contexts
* Additionally, we can set the default projectLead
* @param sourceKey The project that will be the source of the copy
* @param targetKey The desired key for the new project
* @param targetName The desired name for the new project
* @param projectLead The username of the desired project lead
* @return
*/
def copyProject(sourceKey, targetKey, targetName, projectLead) {
def copyProject = new CopyProject()
def inputs = [
(CopyProject.FIELD_SOURCE_PROJECT) : sourceKey,
(CopyProject.FIELD_TARGET_PROJECT) : targetKey,
(CopyProject.FIELD_TARGET_PROJECT_NAME) : targetName,
(CopyProject.FIELD_COPY_VERSIONS) : false,
(CopyProject.FIELD_COPY_COMPONENTS) : false,
(CopyProject.FIELD_COPY_ISSUES) : false,
(CopyProject.FIELD_COPY_DASH_AND_FILTERS): false,
]
def errorCollection = copyProject.doValidate(inputs, false)
if (errorCollection.hasAnyErrors()) {
log.warn("Couldn't create project: $errorCollection")
return [success: false, output: errorCollection]
} else {
ComponentAccessor.getJiraAuthenticationContext().setLoggedInUser(userManager.getUserByName(adminUserName))
def output = copyProject.doScript(inputs)
def newProject = projectManager.getProjectObjByKeyIgnoreCase(targetKey)
if (!newProject) {
return [success: false, output: output]
}
if (newProject && projectLead) {
projectManager.updateProject(newProject, targetName, newProject.description ?: "", projectLead, newProject.url ?: "", newProject.assigneeType)
newProject = projectManager.getProjectObjByKeyIgnoreCase(targetKey)
}
return [success: true, output: "Project [$targetKey] created as a copy of [$sourceKey] with project lead '$newProject.projectLead.name'", link: "$baseUrl/projects/$targetKey"]
}
}
The validateInputs may seem redundant because you still have to call the copyProject.doValidate() method. But in my case, those same input parameters were being used by several endpoints and it was cleaner to validate them upfront and provide some simple hints to the users.
You can also see this other question/answer https://community.atlassian.com/t5/Jira-questions/How-to-create-projects-in-post-function-with-API-from/qaq-p/1118891 that shows how you might implement this in a post-function.
Hi,
Check this, please - https://community.atlassian.com/t5/Answers-Developer-Questions/How-to-Call-Canned-Builtin-Script-Runner-Scripts-from-a-Script/qaq-p/466583
This is rather old conversation, but can do the trick.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.