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

Handle get project by key #4

Merged
merged 1 commit into from
Aug 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions api/v1alpha1/project_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ type ProjectSpec struct {

// ProjectStatus defines the observed state of Project
type ProjectStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Jira service desk project ID
ID string `json:"id"`
}

// +kubebuilder:object:root=true
Expand Down
6 changes: 6 additions & 0 deletions config/crd/bases/jiraservicedesk.stakater.com_projects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ spec:
type: object
status:
description: ProjectStatus defines the observed state of Project
properties:
id:
description: Jira service desk project ID
type: string
required:
- id
type: object
type: object
version: v1alpha1
Expand Down
62 changes: 43 additions & 19 deletions controllers/project_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,25 @@ func (r *ProjectReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
}

// Check if the Project already exists
// project, err := r.JiraServiceDeskClient.GetProjectByKey(instance.Spec.Key)
// if err != nil {
// return ctrl.Result{}, err
// }
// // Project already exists
// // TODO: This should be project != nil
// if err != nil {
// updatedProject := r.JiraServiceDeskClient.GetProjectFromProjectSpec(instance.Spec)
// if !r.JiraServiceDeskClient.ProjectEqual(project, updatedProject) {
// return r.handleUpdate(req, instance)
// } else {
// log.Info("Skipping update. No changes found")
// return ctrl.Result{}, nil
// }
// }
// TODO: Think of use cases and add a default return ctrl.Result{}, nil
return r.handleCreate(req, instance, log)
if len(instance.Status.ID) > 0 {
project, err := r.JiraServiceDeskClient.GetProjectById(instance.Status.ID)
if err != nil {
return ctrl.Result{}, err
}
// Project already exists
if len(project.Id) > 0 {
updatedProject := r.JiraServiceDeskClient.GetProjectFromProjectSpec(instance.Spec)
// Compare retrieved project with current spec
if !r.JiraServiceDeskClient.ProjectEqual(project, updatedProject) {
// Update if there are changes in the declared spec
return r.handleUpdate(req, instance)
} else {
log.Info("Skipping update. No changes found")
return ctrl.Result{}, nil
}
}
}
return r.handleCreate(req, instance)
}

func (r *ProjectReconciler) SetupWithManager(mgr ctrl.Manager) error {
Expand All @@ -92,13 +94,22 @@ func (r *ProjectReconciler) SetupWithManager(mgr ctrl.Manager) error {
Complete(r)
}

func (r *ProjectReconciler) handleCreate(req ctrl.Request, instance *jiraservicedeskv1alpha1.Project, log logr.Logger) (ctrl.Result, error) {
func (r *ProjectReconciler) handleCreate(req ctrl.Request, instance *jiraservicedeskv1alpha1.Project) (ctrl.Result, error) {
log := r.Log.WithValues("project", req.NamespacedName)

log.Info("Creating Jira Service Desk Project: " + instance.Spec.Name)

project := r.JiraServiceDeskClient.GetProjectFromProjectSpec(instance.Spec)
err := r.JiraServiceDeskClient.CreateProject(project)
projectId, err := r.JiraServiceDeskClient.CreateProject(project)
if err != nil {
return ctrl.Result{}, err
}

instance.Status.ID = projectId

err = r.Status().Update(context.Background(), instance)
if err != nil {
log.Error(err, "Failed to update status of Project")
return ctrl.Result{}, err
}

Expand All @@ -108,9 +119,22 @@ func (r *ProjectReconciler) handleCreate(req ctrl.Request, instance *jiraservice
}

func (r *ProjectReconciler) handleDelete(req ctrl.Request, instance *jiraservicedeskv1alpha1.Project) (ctrl.Result, error) {
log := r.Log.WithValues("project", req.NamespacedName)

log.Info("Deleting Jira Service Desk Project: " + instance.Spec.Name)

if instance == nil {
// Instance not found, nothing to do
return ctrl.Result{}, nil
}

return ctrl.Result{}, nil
}

func (r *ProjectReconciler) handleUpdate(req ctrl.Request, instance *jiraservicedeskv1alpha1.Project) (ctrl.Result, error) {
log := r.Log.WithValues("project", req.NamespacedName)

log.Info("Updating Jira Service Desk Project: " + instance.Spec.Name)

return ctrl.Result{RequeueAfter: defaultRequeueTime}, nil
}
File renamed without changes.
14 changes: 14 additions & 0 deletions examples/project/next-gen-project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: jiraservicedesk.stakater.com/v1alpha1
kind: Project
metadata:
name: stakater
spec:
name: stakater
key: STK
projectTypeKey: service_desk
projectTemplateKey: com.atlassian.servicedesk:next-gen-it-service-desk
description: "Sample project for jira-service-desk-operator"
assigneeType: PROJECT_LEAD
leadAccountId: 5ebfbc3ead226b0ba46c3590
url: https://stakater.com

8 changes: 2 additions & 6 deletions jiraservicedesk/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,11 @@ import (

var Log = logf.Log.WithName("jiraServiceDeskClient")

const (
EndpointApiVersion3Project = "/rest/api/3/project"
)

type Client interface {
// Methods for Project
GetProjectByKey(key string) (Project, error)
GetProjectById(id string) (Project, error)
GetProjectFromProjectSpec(spec jiraservicedeskv1alpha1.ProjectSpec) Project
CreateProject(project Project) error
CreateProject(project Project) (string, error)
UpdateProject(updatedProject Project) (Project, error)
ProjectEqual(oldProject Project, newProject Project) bool
}
Expand Down
113 changes: 80 additions & 33 deletions jiraservicedesk/client/client_project.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
package client

import (
"encoding/json"
"errors"
"io/ioutil"
"strconv"

jiraservicedeskv1alpha1 "github.com/stakater/jira-service-desk-operator/api/v1alpha1"
)

const (
// Endpoints
EndpointApiVersion3Project = "/rest/api/3/project"

// Project Template Types
ClassicProjectTemplateKey = "com.atlassian.servicedesk:itil-v2-service-desk-project"
NextGenProjectTemplateKey = "com.atlassian.servicedesk:next-gen-it-service-desk"
)

type Project struct {
Id string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Expand All @@ -25,65 +35,102 @@ type Project struct {
CategoryId int `json:"categoryId,omitempty"`
}

func NewProject(name string) Project {
return Project{
Name: name,
}
type ProjectGetResponse struct {
Self string `json:"self,omitempty"`
Id string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Key string `json:"key,omitempty"`
Description string `json:"description,omitempty"`
Lead ProjectLead `json:"lead,omitempty"`
ProjectTypeKey string `json:"projectTypeKey,omitempty"`
Style string `json:"style,omitempty"`
AssigneeType string `json:"assigneeType,omitempty"`
URL string `json:"url,omitempty"`
}

func (c *jiraServiceDeskClient) GetProjectByKey(name string) (Project, error) {
return NewProject("test"), nil
type ProjectLead struct {
Self string `json:"self,omitempty"`
AccountId string `json:"accountId,omitempty"`
}

type ProjectCreateResponse struct {
Self string `json:"self"`
Id int `json:"id"`
Key string `json:"key"`
}

func (c *jiraServiceDeskClient) GetProjectById(id string) (Project, error) {
var project Project

request, err := c.newRequest("GET", EndpointApiVersion3Project+"/"+id, nil)
if err != nil {
return project, err
}

response, err := c.do(request)
if err != nil {
return project, err
}
defer response.Body.Close()

var responseObject ProjectGetResponse
err = json.NewDecoder(response.Body).Decode(&responseObject)
if err != nil {
return project, err
}

project = projectGetResponseToProjectMapper(responseObject)
return project, err
}

func (c *jiraServiceDeskClient) CreateProject(project Project) error {
func (c *jiraServiceDeskClient) CreateProject(project Project) (string, error) {
request, err := c.newRequest("POST", EndpointApiVersion3Project, project)
if err != nil {
return err
return "", err
}

response, err := c.do(request)
if err != nil {
return err
return "", err
}

defer response.Body.Close()
responseData, _ := ioutil.ReadAll(response.Body)

if response.StatusCode < 200 || response.StatusCode > 299 {
data, _ := ioutil.ReadAll(response.Body)
err := errors.New("Rest request to create Project failed with status " + strconv.Itoa(response.StatusCode) +
" and response: " + string(data))
return err
" and response: " + string(responseData))
return "", err
}

return err
var responseObject ProjectCreateResponse
err = json.Unmarshal(responseData, &responseObject)
if err != nil {
return "", err
}
projectId := strconv.Itoa(responseObject.Id)

return projectId, err
}

func (c *jiraServiceDeskClient) UpdateProject(updatedProject Project) (Project, error) {
return NewProject("test"), nil
return Project{Name: updatedProject.Name}, nil
}

func (c *jiraServiceDeskClient) ProjectEqual(oldProject Project, newProject Project) bool {
return false
// The fields AvatarId, IssueSecurityScheme, NotificationScheme, PermissionScheme, CategoryId are not retrieved
// through get project REST API call so they cannot be used in project comparison
return oldProject.Id == newProject.Id &&
oldProject.Name == newProject.Name &&
oldProject.Key == newProject.Key &&
oldProject.ProjectTypeKey == newProject.ProjectTypeKey &&
oldProject.ProjectTemplateKey == newProject.ProjectTemplateKey &&
oldProject.Description == newProject.Description &&
oldProject.AssigneeType == newProject.AssigneeType &&
oldProject.LeadAccountId == newProject.LeadAccountId &&
oldProject.URL == newProject.URL
}

func (c *jiraServiceDeskClient) GetProjectFromProjectSpec(spec jiraservicedeskv1alpha1.ProjectSpec) Project {
return projectSpecToProjectMapper(spec)
}

func projectSpecToProjectMapper(spec jiraservicedeskv1alpha1.ProjectSpec) Project {
return Project{
Name: spec.Name,
Key: spec.Key,
ProjectTypeKey: spec.ProjectTypeKey,
ProjectTemplateKey: spec.ProjectTemplateKey,
Description: spec.Description,
AssigneeType: spec.AssigneeType,
LeadAccountId: spec.LeadAccountId,
URL: spec.URL,
AvatarId: spec.AvatarId,
IssueSecurityScheme: spec.IssueSecurityScheme,
PermissionScheme: spec.IssueSecurityScheme,
NotificationScheme: spec.NotificationScheme,
CategoryId: spec.CategoryId,
}
}
46 changes: 46 additions & 0 deletions jiraservicedesk/client/client_project_mappers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package client

import (
jiraservicedeskv1alpha1 "github.com/stakater/jira-service-desk-operator/api/v1alpha1"
)

func projectSpecToProjectMapper(spec jiraservicedeskv1alpha1.ProjectSpec) Project {
return Project{
Name: spec.Name,
Key: spec.Key,
ProjectTypeKey: spec.ProjectTypeKey,
ProjectTemplateKey: spec.ProjectTemplateKey,
Description: spec.Description,
AssigneeType: spec.AssigneeType,
LeadAccountId: spec.LeadAccountId,
URL: spec.URL,
AvatarId: spec.AvatarId,
IssueSecurityScheme: spec.IssueSecurityScheme,
PermissionScheme: spec.IssueSecurityScheme,
NotificationScheme: spec.NotificationScheme,
CategoryId: spec.CategoryId,
}
}

func projectGetResponseToProjectMapper(response ProjectGetResponse) Project {
var projectTemplateKey string
if len(response.Style) > 0 {
if response.Style == "classic" {
projectTemplateKey = ClassicProjectTemplateKey
} else if response.Style == "next-gen" {
projectTemplateKey = NextGenProjectTemplateKey
}
}

return Project{
Id: response.Id,
Name: response.Name,
Key: response.Key,
ProjectTypeKey: response.ProjectTypeKey,
ProjectTemplateKey: projectTemplateKey,
Description: response.Description,
AssigneeType: response.AssigneeType,
LeadAccountId: response.Lead.AccountId,
URL: response.URL,
}
}