Skip to content

Commit

Permalink
Merge pull request #158 from ctreminiom/feature/157
Browse files Browse the repository at this point in the history
Feature/157
  • Loading branch information
ctreminiom authored Oct 28, 2022
2 parents 495d9d2 + 6462a5a commit b104580
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 0 deletions.
80 changes: 80 additions & 0 deletions jira/internal/jql_impl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package internal

import (
"context"
"fmt"
model "github.com/ctreminiom/go-atlassian/pkg/infra/models"
"github.com/ctreminiom/go-atlassian/service"
"github.com/ctreminiom/go-atlassian/service/jira"
"net/http"
"net/url"
"strings"
)

func NewJQLService(client service.Client, version string) (*JQLService, error) {

if version == "" {
return nil, model.ErrNoVersionProvided
}

return &JQLService{
internalClient: &internalJQLServiceImpl{c: client, version: version},
}, nil
}

type JQLService struct {
internalClient jira.JQLConnector
}

// Parse parses and validates JQL queries.
//
// Validation is performed in context of the current user.
//
// POST /rest/api/{2-3}/jql/parse
//
// https://docs.go-atlassian.io/jira-software-cloud/jql#parse-jql-query
func (j *JQLService) Parse(ctx context.Context, validationType string, JqlQueries []string) (*model.ParsedQueryPageScheme, *model.ResponseScheme, error) {
return j.internalClient.Parse(ctx, validationType, JqlQueries)
}

type internalJQLServiceImpl struct {
c service.Client
version string
}

func (i *internalJQLServiceImpl) Parse(ctx context.Context, validationType string, JqlQueries []string) (*model.ParsedQueryPageScheme, *model.ResponseScheme, error) {

var endpoint strings.Builder
endpoint.WriteString(fmt.Sprintf("/rest/api/%v/jql/parse", i.version))

if validationType != "" {
params := url.Values{}
params.Add("validation", validationType)

endpoint.WriteString(fmt.Sprintf("?%v", params.Encode()))
}

payload := struct {
Queries []string `json:"queries,omitempty"`
}{
Queries: JqlQueries,
}

reader, err := i.c.TransformStructToReader(&payload)
if err != nil {
return nil, nil, err
}

request, err := i.c.NewRequest(ctx, http.MethodPost, endpoint.String(), reader)
if err != nil {
return nil, nil, err
}

page := new(model.ParsedQueryPageScheme)
response, err := i.c.Call(request, page)
if err != nil {
return nil, response, err
}

return page, response, nil
}
189 changes: 189 additions & 0 deletions jira/internal/jql_impl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package internal

import (
"bytes"
"context"
"errors"
model "github.com/ctreminiom/go-atlassian/pkg/infra/models"
"github.com/ctreminiom/go-atlassian/service"
"github.com/ctreminiom/go-atlassian/service/mocks"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
)

func Test_internalJQLServiceImpl_Parse(t *testing.T) {

payloadMocked := &struct {
Queries []string "json:\"queries,omitempty\""
}{Queries: []string{"summary ~ test AND (labels in (urgent, blocker) OR lastCommentedBy = currentUser()) AND status CHANGED AFTER startOfMonth(-1M) ORDER BY updated DESC", "invalid query", "summary = test", "summary in test", "project = INVALID", "universe = 42"}}

type fields struct {
c service.Client
version string
}

type args struct {
ctx context.Context
validationType string
JqlQueries []string
}

testCases := []struct {
name string
fields fields
args args
on func(*fields)
wantErr bool
Err error
}{
{
name: "when the api version is v3",
fields: fields{version: "3"},
args: args{
ctx: context.TODO(),
validationType: "strict",
JqlQueries: []string{
"summary ~ test AND (labels in (urgent, blocker) OR lastCommentedBy = currentUser()) AND status CHANGED AFTER startOfMonth(-1M) ORDER BY updated DESC",
"invalid query",
"summary = test",
"summary in test",
"project = INVALID",
"universe = 42"},
},
on: func(fields *fields) {

client := mocks.NewClient(t)

client.On("TransformStructToReader",
payloadMocked).
Return(bytes.NewReader([]byte{}), nil)

client.On("NewRequest",
context.Background(),
http.MethodPost,
"/rest/api/3/jql/parse?validation=strict",
bytes.NewReader([]byte{})).
Return(&http.Request{}, nil)

client.On("Call",
&http.Request{},
&model.ParsedQueryPageScheme{}).
Return(&model.ResponseScheme{}, nil)

fields.c = client

},
wantErr: false,
Err: nil,
},

{
name: "when the api version is v2",
fields: fields{version: "2"},
args: args{
ctx: context.TODO(),
validationType: "strict",
JqlQueries: []string{
"summary ~ test AND (labels in (urgent, blocker) OR lastCommentedBy = currentUser()) AND status CHANGED AFTER startOfMonth(-1M) ORDER BY updated DESC",
"invalid query",
"summary = test",
"summary in test",
"project = INVALID",
"universe = 42"},
},
on: func(fields *fields) {

client := mocks.NewClient(t)

client.On("TransformStructToReader",
payloadMocked).
Return(bytes.NewReader([]byte{}), nil)

client.On("NewRequest",
context.Background(),
http.MethodPost,
"/rest/api/2/jql/parse?validation=strict",
bytes.NewReader([]byte{})).
Return(&http.Request{}, nil)

client.On("Call",
&http.Request{},
&model.ParsedQueryPageScheme{}).
Return(&model.ResponseScheme{}, nil)

fields.c = client

},
wantErr: false,
Err: nil,
},

{
name: "when the http request cannot be created",
fields: fields{version: "3"},
args: args{
ctx: context.TODO(),
validationType: "strict",
JqlQueries: []string{
"summary ~ test AND (labels in (urgent, blocker) OR lastCommentedBy = currentUser()) AND status CHANGED AFTER startOfMonth(-1M) ORDER BY updated DESC",
"invalid query",
"summary = test",
"summary in test",
"project = INVALID",
"universe = 42"},
},
on: func(fields *fields) {

client := mocks.NewClient(t)

client.On("TransformStructToReader",
payloadMocked).
Return(bytes.NewReader([]byte{}), nil)

client.On("NewRequest",
context.Background(),
http.MethodPost,
"/rest/api/3/jql/parse?validation=strict",
bytes.NewReader([]byte{})).
Return(&http.Request{}, errors.New("error, unable to create the http request"))

fields.c = client

},
wantErr: true,
Err: errors.New("error, unable to create the http request"),
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {

if testCase.on != nil {
testCase.on(&testCase.fields)
}

fieldService, err := NewJQLService(testCase.fields.c, testCase.fields.version)
assert.NoError(t, err)

gotResult, gotResponse, err := fieldService.Parse(testCase.args.ctx, testCase.args.validationType,
testCase.args.JqlQueries)

if testCase.wantErr {

if err != nil {
t.Logf("error returned: %v", err.Error())
}

assert.EqualError(t, err, testCase.Err.Error())

} else {

assert.NoError(t, err)
assert.NotEqual(t, gotResponse, nil)
assert.NotEqual(t, gotResult, nil)
}

})
}
}
7 changes: 7 additions & 0 deletions jira/v2/api_client_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ func New(httpClient common.HttpClient, site string) (*Client, error) {
return nil, err
}

jql, err := internal.NewJQLService(client, "2")
if err != nil {
return nil, err
}

client.Permission = permission
client.MySelf = mySelf
client.Auth = internal.NewAuthenticationService(client)
Expand All @@ -347,6 +352,7 @@ func New(httpClient common.HttpClient, site string) (*Client, error) {
client.Task = task
client.User = user
client.Workflow = workflow
client.JQL = jql

return client, nil
}
Expand All @@ -368,6 +374,7 @@ type Client struct {
Server *internal.ServerService
User *internal.UserService
Workflow *internal.WorkflowService
JQL *internal.JQLService
}

func (c *Client) NewFormRequest(ctx context.Context, method, apiEndpoint, contentType string, payload io.Reader) (*http.Request, error) {
Expand Down
7 changes: 7 additions & 0 deletions jira/v3/api_client_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ func New(httpClient common.HttpClient, site string) (*Client, error) {
return nil, err
}

jql, err := internal.NewJQLService(client, "3")
if err != nil {
return nil, err
}

client.Permission = permission
client.MySelf = mySelf
client.Auth = internal.NewAuthenticationService(client)
Expand All @@ -347,6 +352,7 @@ func New(httpClient common.HttpClient, site string) (*Client, error) {
client.Server = server
client.User = user
client.Workflow = workflow
client.JQL = jql

return client, nil
}
Expand All @@ -368,6 +374,7 @@ type Client struct {
Server *internal.ServerService
User *internal.UserService
Workflow *internal.WorkflowService
JQL *internal.JQLService
}

func (c *Client) NewFormRequest(ctx context.Context, method, apiEndpoint, contentType string, payload io.Reader) (*http.Request, error) {
Expand Down
40 changes: 40 additions & 0 deletions pkg/infra/models/jira_jql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package models

type ParsedQueryPageScheme struct {
Queries []*ParseQueryScheme `json:"queries"`
}

type ParseQueryScheme struct {
Query string `json:"query"`
Structure struct {
Where struct {
} `json:"where"`
OrderBy *QueryStructureOrderScheme `json:"orderBy"`
} `json:"structure"`
Errors []string `json:"errors"`
}

type QueryStructureScheme struct {
OrderBy *QueryStructureOrderScheme `json:"orderBy"`
}

type QueryStructureOrderScheme struct {
Fields []*QueryStructureOrderFieldScheme `json:"fields"`
}

type QueryStructureOrderFieldScheme struct {
Field *QueryStructureOrderFieldNodeScheme `json:"field"`
Direction string `json:"direction"`
}

type QueryStructureOrderFieldNodeScheme struct {
Name string `json:"name"`
Property []*QueryPropertyScheme `json:"property"`
}

type QueryPropertyScheme struct {
Entity string `json:"entity"`
Key string `json:"key"`
Path string `json:"path"`
Type string `json:"type"`
}
18 changes: 18 additions & 0 deletions service/jira/jql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package jira

import (
"context"
"github.com/ctreminiom/go-atlassian/pkg/infra/models"
)

type JQLConnector interface {

// Parse parses and validates JQL queries.
//
// Validation is performed in context of the current user.
//
// POST /rest/api/{2-3}/jql/parse
//
// https://docs.go-atlassian.io/jira-software-cloud/jql#parse-jql-query
Parse(ctx context.Context, validationType string, JqlQueries []string) (*models.ParsedQueryPageScheme, *models.ResponseScheme, error)
}

0 comments on commit b104580

Please sign in to comment.