diff --git a/README.md b/README.md index 240fbb8..6dce10c 100644 --- a/README.md +++ b/README.md @@ -61,11 +61,12 @@ Examples for Project Custom Resource can be found at [here](https://github.com/s * Following are the immutable fields that cannot be updated: * ProjectTemplateKey * ProjectTypeKey - * leadAccountId + * LeadAccountId * CategoryId * NotificationScheme * PermissionScheme - * issueSecurityScheme + * IssueSecurityScheme + * OpenAccess You can read more about these fields on [Jira Service Desk api docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-projects/#api-rest-api-3-project-post). diff --git a/api/v1alpha1/project_types.go b/api/v1alpha1/project_types.go index 021aa67..6e20503 100644 --- a/api/v1alpha1/project_types.go +++ b/api/v1alpha1/project_types.go @@ -87,6 +87,10 @@ type ProjectSpec struct { // The ID of the project's category // +optional CategoryId int `json:"categoryId,omitempty"` + + // The Open Access status, which dictates who can access the project. If set to true all customers can access the project. If false, only customers added to project can access the project. + // +optional, if not provided default behaviour is False + OpenAccess bool `json:"openAccess,omitempty"` } // ProjectStatus defines the observed state of Project @@ -160,6 +164,9 @@ func (project *Project) IsValidUpdate(existingProject Project) (bool, error) { if project.Spec.IssueSecurityScheme != existingProject.Spec.IssueSecurityScheme { return false, fmt.Errorf("%s %s", "IssueSecurityScheme", errorImmutableFieldMsg) } + if project.Spec.OpenAccess != existingProject.Spec.OpenAccess { + return false, fmt.Errorf("%s %s", "OpenAccess", errorImmutableFieldMsg) + } return true, nil } diff --git a/config/crd/bases/jiraservicedesk.stakater.com_projects.yaml b/config/crd/bases/jiraservicedesk.stakater.com_projects.yaml index 3bc582a..be45f42 100644 --- a/config/crd/bases/jiraservicedesk.stakater.com_projects.yaml +++ b/config/crd/bases/jiraservicedesk.stakater.com_projects.yaml @@ -69,6 +69,11 @@ spec: notificationScheme: description: The ID of the notification scheme for the project type: integer + openAccess: + description: The Open Access status, which dictates who can access the + project. If set to true all customers can access the project. If false, + only customers added to project can access the project. + type: boolean permissionScheme: description: The ID of the permission scheme for the project type: integer diff --git a/controllers/project_controller.go b/controllers/project_controller.go index c2c9302..0ff7713 100644 --- a/controllers/project_controller.go +++ b/controllers/project_controller.go @@ -137,6 +137,16 @@ func (r *ProjectReconciler) handleCreate(req ctrl.Request, instance *jiraservice } log.Info("Successfully created Jira Service Desk Project: " + instance.Spec.Name) + + if !instance.Spec.OpenAccess { + err = r.JiraServiceDeskClient.UpdateProjectAccessPermissions(instance.Spec.OpenAccess, project.Key) + if err != nil { + return reconcilerUtil.ManageError(r.Client, instance, err, false) + } + + log.Info("Successfully updated the Access Permissions to customer") + } + instance.Status.ID = projectId return reconcilerUtil.ManageSuccess(r.Client, instance) } @@ -180,6 +190,7 @@ func (r *ProjectReconciler) handleUpdate(req ctrl.Request, existingProject jiras if ok, err := instance.IsValidUpdate(existingProjectInstance); !ok { return reconcilerUtil.ManageError(r.Client, instance, err, false) } + updatedProject := r.JiraServiceDeskClient.GetProjectForUpdateRequest(existingProject, instance) err := r.JiraServiceDeskClient.UpdateProject(updatedProject, existingProject.Id) if err != nil { diff --git a/examples/project/classic-project.yaml b/examples/project/classic-project.yaml index 4962cb2..0c2913b 100644 --- a/examples/project/classic-project.yaml +++ b/examples/project/classic-project.yaml @@ -11,4 +11,3 @@ spec: assigneeType: PROJECT_LEAD leadAccountId: 5ebfbc3ead226b0ba46c3590 url: https://stakater.com - diff --git a/mock/data.go b/mock/data.go index aa2ea98..685d693 100644 --- a/mock/data.go +++ b/mock/data.go @@ -13,8 +13,8 @@ var ProjectIDInt, _ = strconv.Atoi(ProjectID) var InvalidPermissionScheme = "4000" var GetProjectFailedErrorMsg = "Rest request to get Project failed with status: 404" -var CreateProjectFailedErrorMsg = "Rest request to create Project failed with status 400 and response: " -var UpdateProjectFailedErrorMsg = "Rest request to update Project failed with status 404 and response: " +var CreateProjectFailedErrorMsg = "Rest request to create Project failed with status: 400 and response: " +var UpdateProjectFailedErrorMsg = "Rest request to update Project failed with status: 404 and response: " var DeleteProjectFailedErrorMsg = "Rest request to delete Project failed with status: 404" var CreateProjectInputJSON = map[string]string{ diff --git a/pkg/jiraservicedesk/client/client.go b/pkg/jiraservicedesk/client/client.go index dc21e2f..c5dedc4 100644 --- a/pkg/jiraservicedesk/client/client.go +++ b/pkg/jiraservicedesk/client/client.go @@ -24,6 +24,7 @@ type Client interface { UpdateProject(updatedProject Project, id string) error ProjectEqual(oldProject Project, newProject Project) bool GetProjectForUpdateRequest(existingProject Project, newProject *jiraservicedeskv1alpha1.Project) Project + UpdateProjectAccessPermissions(status bool, key string) error GetCustomerById(customerAccountId string) (Customer, error) CreateCustomer(customer Customer) (string, error) AddCustomerToProject(customerAccountId string, projectKey string) error diff --git a/pkg/jiraservicedesk/client/client_project.go b/pkg/jiraservicedesk/client/client_project.go index 5462a3c..947ef12 100644 --- a/pkg/jiraservicedesk/client/client_project.go +++ b/pkg/jiraservicedesk/client/client_project.go @@ -12,6 +12,8 @@ import ( const ( // Endpoints EndpointApiVersion3Project = "/rest/api/3/project" + ServiceDeskV1ApiPath = "/rest/servicedesk/1/servicedesk/" + RequestSecurityPath = "/settings/requestsecurity" // Project Template Types ClassicProjectTemplateKey = "com.atlassian.servicedesk:itil-v2-service-desk-project" @@ -59,6 +61,13 @@ type ProjectCreateResponse struct { Key string `json:"key"` } +type CustomerAccessRequestBody struct { + autocompleteEnabled bool + manageEnabled bool + serviceDeskOpenAccess bool + serviceDeskPublicSignup bool +} + func (c *jiraServiceDeskClient) GetProjectById(id string) (Project, error) { var project Project @@ -103,7 +112,7 @@ func (c *jiraServiceDeskClient) CreateProject(project Project) (string, error) { responseData, _ := ioutil.ReadAll(response.Body) if response.StatusCode < 200 || response.StatusCode > 299 { - err := errors.New("Rest request to create Project failed with status " + strconv.Itoa(response.StatusCode) + + err := errors.New("Rest request to create Project failed with status: " + strconv.Itoa(response.StatusCode) + " and response: " + string(responseData)) return "", err } @@ -133,7 +142,7 @@ func (c *jiraServiceDeskClient) UpdateProject(updatedProject Project, id string) responseData, _ := ioutil.ReadAll(response.Body) if response.StatusCode < 200 || response.StatusCode > 299 { - err := errors.New("Rest request to update Project failed with status " + strconv.Itoa(response.StatusCode) + + err := errors.New("Rest request to update Project failed with status: " + strconv.Itoa(response.StatusCode) + " and response: " + string(responseData)) return err } @@ -152,13 +161,41 @@ func (c *jiraServiceDeskClient) DeleteProject(id string) error { return err } - if response.StatusCode != 204 { + if response.StatusCode < 200 || response.StatusCode > 299 { return errors.New("Rest request to delete Project failed with status: " + strconv.Itoa(response.StatusCode)) } return err } +func (c *jiraServiceDeskClient) UpdateProjectAccessPermissions(status bool, key string) error { + body := CustomerAccessRequestBody{ + autocompleteEnabled: false, + manageEnabled: false, + serviceDeskOpenAccess: status, + serviceDeskPublicSignup: status, + } + + request, err := c.newRequest("POST", ServiceDeskV1ApiPath+key+RequestSecurityPath, body, false) + if err != nil { + return err + } + + response, err := c.do(request) + if err != nil { + return err + } + + defer response.Body.Close() + + if response.StatusCode < 200 || response.StatusCode > 299 { + err := errors.New("Rest request to update project permissions failed with status: " + strconv.Itoa(response.StatusCode)) + return err + } + + return err +} + func (c *jiraServiceDeskClient) ProjectEqual(oldProject Project, newProject Project) bool { // The fields AvatarId, IssueSecurityScheme, NotificationScheme, PermissionScheme, CategoryId are not retrieved // through get project REST API call so they cannot be used in project comparison