Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [CI-14390]: Added new enhancements to plugins/jira #26

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 141 additions & 23 deletions plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ type Args struct {
// Deployment environment (optional)
EnvironmentName string `envconfig:"PLUGIN_ENVIRONMENT_NAME"`

EnvironmentId string `envconfig:"PLUGIN_ENVIRONMENT_ID"`

EnvironmentType string `envconfig:"PLUGIN_ENVIRONMENT_TYPE"`

vanisrikanithi marked this conversation as resolved.
Show resolved Hide resolved
// Link to deployment (optional)
Link string `envconfig:"PLUGIN_LINK"`

Expand All @@ -67,47 +71,60 @@ type Args struct {

// connect hostname (required)
ConnectHostname string `envconfig:"PLUGIN_CONNECT_HOSTNAME"`
IssueKeys []string `envconfig:"PLUGIN_ISSUEKEYS"`
}

// Exec executes the plugin.
func Exec(ctx context.Context, args Args) error {
var (
environ = toEnvironment(args)
issue = extractIssue(args)
environ = toEnvironment(args)
environmentID = toEnvironmentId(args)
environmentType = toEnvironmentType(args)
issue string
issues []string
state = toState(args)
version = toVersion(args)
deeplink = toLink(args)
)

// ExtractInstanceName extracts the instance name from the provided URL if any
instanceName := ExtractInstanceName(args.Instance)

logger := logrus.
WithField("client_id", args.ClientID).
WithField("cloud_id", args.CloudID).
WithField("project_id", args.Project).
WithField("instance", args.Instance).
WithField("instance", instanceName).
WithField("pipeline", args.Name).
WithField("environment", environ).
WithField("state", state).
WithField("version", version)
WithField("environment Type", environmentType).
WithField("environment ID", environmentID)

if issue == "" {
logger.Debugln("cannot find issue number")
return errors.New("failed to extract issue number")
if len(args.IssueKeys) > 0 {
issues = args.IssueKeys
} else {
issue = extractIssue(args)
if issue == "" {
logger.Debugln("cannot find issue number")
return errors.New("failed to extract issue number")
}
logger = logger.WithField("issue", issue)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's not duplicate how we handle both the array (issues) and the single issue (issue [old logic]) separately, and keep the logic simple:

// check if PLUGIN_ISSUEKEYS is provided
if len(args.IssueKeys) > 0 {
issues = args.IssueKeys
} else {
// fallback to extracting from commit if no issue keys are passed
issue = extractIssue(args)
if issue == "" {
logger.Debugln("cannot find issue number")
return errors.New("failed to extract issue number")
}
issues = []string{issue} // add the single issue here for consistency
}

and modify the deployment payload accordingly.

Copy link
Author

@vanisrikanithi vanisrikanithi Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I have done this way, but observed one issue that if PLUGIN_ISSUEKEYS are not provided then Issue keys will be populated by old way(extractIssue) right. That means deployment payload contains always both Issuekeys and associations like below. With this payload when i tested restapi (postman) I was getting error in the response as below ("message": "issueKeys and associations are mutually exclusive.) Is this expected.? Also it could be existing behaviour. It won't cause any issue?
Request chunk:
"issueKeys": [
"SCRUM-1"
],
"associations": [
{
"associationType": "issueIdOrKeys",
"values": [
"SCRUM-1"
]
}
],

Response chunk:
"errors": [
{
"message": "issueKeys and associations are mutually exclusive. Either only specify issueKeys or pass issueKeys as an associationType.",
"key": "deploymentdata.issueKeysOrAssociationsOrNone.invalid"
},


commitMessage := args.Commit.Message
if len(commitMessage) > 255 {
logger.Warnln("Commit message exceeds 255 characters; truncating to fit.")
commitMessage = commitMessage[:252] + "..."
}
if len(commitMessage) > 255 {
logger.Warnln("Commit message exceeds 255 characters; truncating to fit.")
commitMessage = commitMessage[:252] + "..."
}

logger = logger.WithField("issue", issue)
logger.Debugln("successfully extraced issue number")

deploymentPayload := DeploymentPayload{
Deployments: []*Deployment{
{
Deploymentsequencenumber: args.Build.Number,
Updatesequencenumber: args.Build.Number,
IssueKeys: issues,
Associations: []Association{
{
Associationtype: "issueIdOrKeys",
Expand All @@ -125,13 +142,23 @@ func Exec(ctx context.Context, args Args) error {
URL: deeplink,
},
Environment: Environment{
ID: environ,
ID: environmentID,
Displayname: environ,
Type: environ,
Type: environmentType,
},
},
},
}
if len(args.IssueKeys) > 0 {
deploymentPayload.Deployments[0].Associations = nil
}
/*fmt.Println("formatted deploymentPayload JSON data")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this comment please. Please make sure those debug/temp/commented out code doesn't appear in final PR

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

jsonData, err := json.MarshalIndent(deploymentPayload, "", " ")
if err != nil {
fmt.Println("Error marshaling to JSON:", err)
}
fmt.Println(string(jsonData))
*/
buildPayload := BuildPayload{
Builds: []*Build{
{
Expand All @@ -141,13 +168,47 @@ func Exec(ctx context.Context, args Args) error {
URL: deeplink,
LastUpdated: time.Now(),
PipelineID: args.Name,
IssueKeys: []string{issue},
IssueKeys: issues,
State: state,
UpdateSequenceNumber: args.Build.Number,
References: []struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a check to make sure the branch and URI values exist before adding them to the References so it'll only be added when the necessary values are available

Commit struct {
ID string `json:"id"`
RepositoryURI string `json:"repositoryUri"`
} `json:"commit"`
Ref struct {
Name string `json:"name"`
URI string `json:"uri"`
} `json:"ref"`
}{
{
Commit: struct {
ID string `json:"id"`
RepositoryURI string `json:"repositoryUri"`
}{
ID: args.Commit.Rev,
RepositoryURI: args.Commit.Link,
},
Ref: struct {
Name string `json:"name"`
URI string `json:"uri"`
}{
Name: args.Commit.Branch,
URI: fmt.Sprintf("%s/refs/%s", args.Commit.Link, args.Commit.Branch),
},
},
},
},
},
}

// Print the full data of buildPayload
// Marshaling the Build struct into JSON format with indentation
/*jsonData1, err1 := json.MarshalIndent(buildPayload, "", " ")
if err != nil {
fmt.Println("Error marshaling to JSON:", err1)
}
//Printing the formatted JSON data
fmt.Println(string(jsonData1))*/
// validation of arguments
if (args.ClientID == "" && args.ClientSecret == "") && (args.ConnnectKey == "") {
logger.Debugln("client id and secret are empty. specify the client id and secret or specify connect key")
Expand All @@ -156,7 +217,7 @@ func Exec(ctx context.Context, args Args) error {
// create tokens and deployments
if args.ClientID != "" && args.ClientSecret != "" {
// get cloud id
cloudID, err := getCloudID(args.Instance, args.CloudID)
cloudID, err := getCloudID(instanceName, args.CloudID)
if err != nil {
logger.Debugln("cannot get cloud id")
return err
Expand Down Expand Up @@ -187,15 +248,15 @@ func Exec(ctx context.Context, args Args) error {
}
if args.EnvironmentName != "" {
logger.Infoln("creating deployment")
deploymentErr := createConnectDeployment(deploymentPayload, args.Instance, args.Level, jwtToken)
deploymentErr := createConnectDeployment(deploymentPayload, instanceName, args.Level, jwtToken)
if deploymentErr != nil {
logger.WithError(deploymentErr).
Errorln("cannot create deployment")
return deploymentErr
}
} else {
logger.Infoln("creating build")
buildErr := createConnectBuild(buildPayload, args.Instance, args.Level, jwtToken)
buildErr := createConnectBuild(buildPayload, instanceName, args.Level, jwtToken)
if buildErr != nil {
logger.WithError(buildErr).
Errorln("cannot create build")
Expand All @@ -204,15 +265,25 @@ func Exec(ctx context.Context, args Args) error {
}
}
// only create card if the state is successful
ticketLink := fmt.Sprintf("https://%s.atlassian.net/browse/%s", args.Instance, issue)

var ticketLinks []string
if len(issues) > 0 && len(args.IssueKeys) > 0 {
for _, issue_key := range issues {
ticketLink := fmt.Sprintf("https://%s.atlassian.net/browse/%s", args.Instance, issue_key)
ticketLinks = append(ticketLinks, ticketLink)
}
} else {
ticketLink := fmt.Sprintf("https://%s.atlassian.net/browse/%s", args.Instance, issue)
ticketLinks = append(ticketLinks, ticketLink)
}
cardData := Card{
Pipeline: args.Name,
Instance: args.Instance,
Instance: instanceName,
Project: args.Project,
State: state,
Version: version,
Environment: environ,
URL: ticketLink,
URL: ticketLinks,
}
if err := args.writeCard(cardData); err != nil {
fmt.Printf("Could not create adaptive card. %s\n", err)
Expand All @@ -221,6 +292,24 @@ func Exec(ctx context.Context, args Args) error {
return nil
}

// TBD(TobeDeleted): Commented as it is not working as expected.
/*
func toBranchReference(args Args) []References {
return []References{
{
Commit: Commit{
ID: args.Commit.Rev,
RepositoryURI: args.Commit.Link,
},
Ref: Ref{
Name: args.Commit.Branch, // Branch name
URI: fmt.Sprintf("%s/refs/%s", args.Commit.Link, args.Commit.Branch), // Branch URI
},
},
}
}
*/

// makes an API call to create a token.
func getOauthToken(args Args) (string, error) {
payload := map[string]string{
Expand All @@ -239,6 +328,16 @@ func getOauthToken(args Args) (string, error) {
return "", err
}
req.Header.Set("Content-Type", "application/json")
/*
// Dump the entire HTTP request
requestDump, err := httputil.DumpRequest(req, true)
if err != nil {
fmt.Println("Error dumping request:", err)
} else {
fmt.Println(string(requestDump))
}
*/

res, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
Expand All @@ -257,6 +356,7 @@ func getOauthToken(args Args) (string, error) {
if err != nil {
return "", err
}
// fmt.Println(output["access_token"].(string))
return output["access_token"].(string), nil
}

Expand Down Expand Up @@ -323,6 +423,16 @@ func createConnectDeployment(payload DeploymentPayload, cloudID, debug, jwtToken
req.Header.Set("Authorization", "Bearer "+jwtToken)
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)

/* jsonData2, err2 := json.MarshalIndent(res, "", " ")
if err != nil {
fmt.Println("Error marshaling to JSON:", err2)
}
//Printing the formatted JSON data
fmt.Println("formatted Build Payload JSON data")
fmt.Println(string(jsonData2))*/

if err != nil {
return err
}
Expand Down Expand Up @@ -357,6 +467,14 @@ func createConnectBuild(payload BuildPayload, cloudID, debug, jwtToken string) e
if err != nil {
return err
}
/* jsonData3, err3 := json.MarshalIndent(res, "", " ")
if err != nil {
fmt.Println("Error marshaling to JSON:", err3)
}
//Printing the formatted JSON data
fmt.Println("formatted Build Payload JSON data")
fmt.Println(string(jsonData3))*/
defer res.Body.Close()
switch debug {
case "debug", "trace", "DEBUG", "TRACE":
Expand Down
40 changes: 21 additions & 19 deletions plugin/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,24 @@ type (

// Deployment provides the Deployment details.
Deployment struct {
Deploymentsequencenumber int `json:"deploymentSequenceNumber"`
Updatesequencenumber int `json:"updateSequenceNumber"`
Associations []Association `json:"associations"`
Displayname string `json:"displayName"`
URL string `json:"url"`
Description string `json:"description"`
Lastupdated time.Time `json:"lastUpdated"`
State string `json:"state"`
Pipeline JiraPipeline `json:"pipeline"`
Environment Environment `json:"environment"`
Deploymentsequencenumber int `json:"deploymentSequenceNumber"`
//IssueKeys []string `json:"issueKeys"`
IssueKeys []string `json:"issueKeys,omitempty"`
Updatesequencenumber int `json:"updateSequenceNumber"`
Associations []Association `json:"associations"`
Displayname string `json:"displayName"`
URL string `json:"url"`
Description string `json:"description"`
Lastupdated time.Time `json:"lastUpdated"`
State string `json:"state"`
Pipeline JiraPipeline `json:"pipeline"`
Environment Environment `json:"environment"`
}

// Association provides the association details.
Association struct {
Associationtype string `json:"associationType"`
Values []string `json:"values"`
Associationtype string `json:"associationType,omitempty"`
Values []string `json:"values,omitempty"`
}

// Environment provides the environment details.
Expand All @@ -86,12 +88,12 @@ type (

// struct for adaptive card
Card struct {
Pipeline string `json:"pipeline"`
Instance string `json:"instance"`
Project string `json:"project"`
State string `json:"state"`
Version string `json:"version"`
Environment string `json:"environment"`
URL string `json:"url"`
Pipeline string `json:"pipeline"`
Instance string `json:"instance"`
Project string `json:"project"`
State string `json:"state"`
Version string `json:"version"`
Environment string `json:"environment"`
URL []string `json:"url"`
}
)
Loading