From 5f4193817b690214e10ee4ae5985065178243b0b Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Thu, 25 Jul 2024 15:27:04 +0100 Subject: [PATCH 01/18] feat: add action schema --- application/jira/v1/config/definition.json | 24 + application/jira/v1/config/setup.json | 57 + application/jira/v1/config/tasks.json | 1181 ++++++++++++++++++++ 3 files changed, 1262 insertions(+) create mode 100644 application/jira/v1/config/definition.json create mode 100644 application/jira/v1/config/setup.json create mode 100644 application/jira/v1/config/tasks.json diff --git a/application/jira/v1/config/definition.json b/application/jira/v1/config/definition.json new file mode 100644 index 00000000..74124e5e --- /dev/null +++ b/application/jira/v1/config/definition.json @@ -0,0 +1,24 @@ +{ + "availableTasks": [ + "TASK_LIST_BOARDS", + "TASK_LIST_ISSUES", + "TASK_LIST_SPRINTS", + "TASK_GET_ISSUE", + "TASK_GET_SPRINT", + "TASK_CREATE_ISSUE", + "TASK_UPDATE_ISSUE", + "TASK_MOVE_ISSUE_TO_EPIC" + ], + "documentationUrl": "https://www.instill.tech/docs/component/application/jira", + "icon": "assets/Jira.svg", + "id": "jira", + "public": true, + "title": "Jira", + "description": "Do anything available on Jira", + "tombstone": false, + "type": "COMPONENT_TYPE_APPLICATION", + "uid": "3b27f50d-a754-4b9d-8141-95aaec647cc5", + "version": "0.1.0", + "sourceUrl": "https://github.com/instill-ai/component/blob/main/application/jira/v0", + "releaseStage": "RELEASE_STAGE_ALPHA" +} diff --git a/application/jira/v1/config/setup.json b/application/jira/v1/config/setup.json new file mode 100644 index 00000000..e9b18f77 --- /dev/null +++ b/application/jira/v1/config/setup.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "token": { + "description": "Fill in your Jira API token. You can generate one from your Jira account \"settings > security > API tokens\".", + "instillUpstreamTypes": [ + "reference" + ], + "instillAcceptFormats": [ + "string" + ], + "instillSecret": true, + "instillUIOrder": 0, + "title": "Token", + "type": "string" + }, + "email": { + "description": "Fill in your Jira email address.", + "instillUpstreamTypes": [ + "value", + "reference" + ], + "instillAcceptFormats": [ + "string" + ], + "instillUIOrder": 1, + "title": "Email", + "type": "string" + }, + "base-url": { + "description": "Fill in your Jira base URL. For example, if your Jira URL is \"https://mycompany.atlassian.net/...\", then your base URL is https://mycompany.atlassian.net.", + "instillUpstreamTypes": [ + "value", + "reference" + ], + "instillAcceptFormats": [ + "string" + ], + "instillUIOrder": 1, + "title": "Base URL", + "type": "string" + } + }, + "required": [ + "token", + "email", + "base-url" + ], + "instillEditOnNodeFields": [ + "token", + "email", + "base-url" + ], + "title": "Jira Connection", + "type": "object" +} diff --git a/application/jira/v1/config/tasks.json b/application/jira/v1/config/tasks.json new file mode 100644 index 00000000..68045064 --- /dev/null +++ b/application/jira/v1/config/tasks.json @@ -0,0 +1,1181 @@ +{ + "$defs": { + "common-query-params": { + "start-at": { + "default": 0, + "description": "The starting index of the returned boards. Base index: 0. Default is 0", + "instillUIOrder": 3, + "title": "Start At", + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "integer" + }, + "max-results": { + "default": 50, + "description": "The maximum number of boards to return. Default is 50", + "instillUIOrder": 4, + "title": "Max Results", + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "integer" + }, + "update-history": { + "description": "Whether the action taken is added to the user's Recent history, as shown under `Your Work` in Jira.", + "title": "Update History", + "instillUIOrder": 5, + "instillFormat": "boolean", + "instillAcceptFormats": [ + "boolean" + ], + "instillUpstreamTypes": [ + "value" + ], + "type": "boolean" + } + }, + "issue": { + "properties": { + "id": { + "description": "The ID of the issue", + "instillUIOrder": 0, + "title": "ID", + "instillFormat": "string", + "type": "string" + }, + "key": { + "title": "Key", + "description": "The key of the issue, e.g. `JRA-1330`", + "instillShortDescription": "The key of the issue", + "instillUIOrder": 1, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "self": { + "description": "The URL of the issue", + "instillUIOrder": 2, + "instillFormat": "string", + "title": "Self", + "type": "string" + }, + "fields": { + "description": "The fields of the issue. All navigable and Agile fields are returned", + "instillUIOrder": 3, + "instillFormat": "semi-structured/json", + "title": "Fields", + "type": "object", + "required": [] + }, + "issue-type": { + "description": "The type of the issue, can be: `Task`, `Epic`", + "instillUIOrder": 4, + "instillFormat": "string", + "title": "Issue Type", + "type": "string" + }, + "summary": { + "description": "The summary of the issue", + "instillUIOrder": 5, + "instillFormat": "string", + "title": "Summary", + "type": "string" + }, + "description": { + "description": "The description of the issue", + "instillUIOrder": 6, + "instillFormat": "string", + "title": "Description", + "type": "string" + }, + "status": { + "description": "The status of the issue, can be: `To Do`, `In Progress`, `Done`", + "instillUIOrder": 7, + "instillFormat": "string", + "title": "Status", + "type": "string" + } + }, + "required": [ + "id", + "key", + "self", + "fields" + ], + "title": "Issue", + "type": "object" + }, + "sprint": { + "properties": { + "id": { + "title": "ID", + "description": "The ID of the sprint", + "type": "integer", + "instillUIOrder": 0, + "instillFormat": "integer" + }, + "self": { + "title": "Self", + "description": "The URL of the sprint", + "type": "string", + "instillUIOrder": 1, + "instillFormat": "string" + }, + "state": { + "title": "State", + "description": "The state of the sprint, can be: `active`, `closed`, `future`", + "type": "string", + "instillUIOrder": 2, + "instillFormat": "string" + }, + "name": { + "title": "Name", + "description": "The name of the sprint", + "type": "string", + "instillUIOrder": 3, + "instillFormat": "string" + }, + "start-date": { + "title": "Start Date", + "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "type": "string", + "instillUIOrder": 4, + "instillFormat": "string" + }, + "end-date": { + "title": "End Date", + "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "type": "string", + "instillUIOrder": 5, + "instillFormat": "string" + }, + "complete-date": { + "title": "Complete Date", + "description": "The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "type": "string", + "instillUIOrder": 6, + "instillFormat": "string" + }, + "origin-board-id": { + "title": "Origin Board ID", + "description": "The ID of the origin board", + "type": "integer", + "instillUIOrder": 7, + "instillFormat": "integer" + }, + "goal": { + "title": "Goal", + "description": "The Goal of the sprint", + "type": "string", + "instillUIOrder": 8, + "instillFormat": "string" + } + }, + "required": [ + "id", + "self" + ], + "title": "Sprint", + "type": "object" + } + }, + "TASK_LIST_BOARDS": { + "instillShortDescription": "List all boards in Jira", + "input": { + "description": "List all boards in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [], + "properties": { + "project-key-or-id": { + "default": "", + "title": "Project Key or ID", + "description": "This filters results to boards that are relevant to a project. Relevance meaning that the JQL filter defined in board contains a reference to a project.", + "instillShortDescription": "The project key or ID, e.g. `INS`. Default is empty", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "board-type": { + "default": "simple", + "description": "The type of board, can be: scrum, kanban, simple. Default is simple", + "instillUIOrder": 1, + "enum": [ + "scrum", + "kanban", + "simple" + ], + "title": "Board Type", + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "name": { + "default": "", + "description": "Name filters results to boards that match or partially match the specified name. Default is empty", + "instillUIOrder": 2, + "title": "Name", + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "start-at": { + "$ref": "#/$defs/common-query-params/start-at", + "instillUIOrder": 3 + }, + "max-results": { + "$ref": "#/$defs/common-query-params/max-results", + "instillUIOrder": 4 + } + }, + "required": [], + "title": "Input", + "type": "object" + }, + "output": { + "description": "List all boards in Jira", + "instillUIOrder": 0, + "properties": { + "boards": { + "description": "A array of boards in Jira", + "instillUIOrder": 1, + "title": "Boards", + "type": "array", + "items": { + "properties": { + "id": { + "description": "The ID of the board", + "instillUIOrder": 0, + "title": "ID", + "instillFormat": "integer", + "type": "integer" + }, + "name": { + "description": "The name of the board", + "instillUIOrder": 1, + "title": "Name", + "instillFormat": "string", + "type": "string" + }, + "type": { + "description": "The type of the board", + "instillUIOrder": 2, + "title": "Type", + "instillFormat": "string", + "type": "string" + }, + "self": { + "description": "The URL of the board", + "instillUIOrder": 3, + "title": "Self", + "instillFormat": "string", + "type": "string" + } + }, + "type": "object", + "required": [ + "id", + "name", + "type", + "self" + ] + } + }, + "start-at": { + "description": "The starting index of the returned boards. Base index: 0", + "instillUIOrder": 2, + "title": "Start At", + "instillFormat": "integer", + "type": "integer" + }, + "max-results": { + "description": "The maximum number of boards", + "instillUIOrder": 3, + "title": "Max Results", + "instillFormat": "integer", + "type": "integer" + }, + "total": { + "description": "The total number of boards", + "instillUIOrder": 4, + "title": "Total", + "instillFormat": "integer", + "type": "integer" + }, + "is-last": { + "description": "Whether the last board is reached", + "instillUIOrder": 5, + "title": "Is Last", + "instillFormat": "boolean", + "type": "boolean" + } + }, + "required": [ + "start-at", + "max-results", + "total", + "is-last" + ], + "title": "Output", + "type": "object" + } + }, + "TASK_LIST_ISSUES": { + "description": "List issues in Jira", + "instillShortDescription": "List issues in Jira", + "input": { + "description": "List issues in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "board-name", + "range" + ], + "properties": { + "board-name": { + "title": "Board Name", + "description": "The name of the board", + "instillShortDescription": "The name of the board", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "range": { + "title": "Range", + "description": "Choose the range of issues to return. Default is `all`", + "instillUIOrder": 1, + "additionalProperties": true, + "instillFormat": "object", + "type": "object", + "required": [ + "range" + ], + "oneOf": [ + { + "properties": { + "range": { + "const": "All", + "type": "string" + } + }, + "required": [ + "range" + ], + "instillEditOnNodeFields": [ + "range" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "range": { + "const": "Standard Issues", + "type": "string" + } + }, + "required": [ + "range" + ], + "instillEditOnNodeFields": [ + "range" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "range": { + "const": "Epics only", + "type": "string" + } + }, + "required": [ + "range" + ], + "instillEditOnNodeFields": [ + "range" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "range": { + "const": "In backlog only", + "type": "string" + } + }, + "required": [ + "range" + ], + "instillEditOnNodeFields": [ + "range" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "range": { + "const": "Issues of an epic", + "type": "string" + }, + "epic-key": { + "title": "Epic Key", + "description": "The Key of the epic, e.g. `JRA-1330`", + "instillShortDescription": "The Key of the epic", + "instillUIOrder": 10, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "required": [ + "range", + "epic-key" + ], + "instillEditOnNodeFields": [ + "range", + "epic-key" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "range": { + "const": "Issues of a sprint", + "type": "string" + }, + "sprint-name": { + "title": "Sprint Name", + "description": "The name of the sprint", + "instillShortDescription": "The Name of the sprint", + "instillUIOrder": 10, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "required": [ + "range", + "sprint-name" + ], + "instillEditOnNodeFields": [ + "range", + "sprint-name" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "range": { + "const": "Issues without epic assigned", + "type": "string" + } + }, + "required": [ + "range" + ], + "instillEditOnNodeFields": [ + "range" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "range": { + "const": "JQL query", + "type": "string" + }, + "jql": { + "title": "JQL", + "description": "The JQL query. For example, `type = \"Task\" AND status = \"Done\"`. For more information, see Advanced searching", + "instillShortDescription": "The JQL query", + "instillUIOrder": 10, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "required": [ + "range", + "jql" + ], + "instillEditOnNodeFields": [ + "range", + "jql" + ], + "instillFormat": "object", + "type": "object" + } + ] + }, + "start-at": { + "$ref": "#/$defs/common-query-params/start-at", + "instillUIOrder": 3 + }, + "max-results": { + "$ref": "#/$defs/common-query-params/max-results", + "instillUIOrder": 4 + } + }, + "required": [ + "board-name" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Get issues in Jira", + "instillUIOrder": 0, + "properties": { + "issues": { + "description": "A array of issues in Jira", + "instillUIOrder": 1, + "title": "Issues", + "type": "array", + "items": { + "$ref": "#/$defs/issue" + } + }, + "start-at": { + "description": "The starting index of the returned boards. Base index: 0", + "instillUIOrder": 2, + "title": "Start At", + "instillFormat": "integer", + "type": "integer" + }, + "max-results": { + "description": "The maximum number of boards", + "instillUIOrder": 3, + "title": "Max Results", + "instillFormat": "integer", + "type": "integer" + }, + "total": { + "description": "The total number of boards", + "instillUIOrder": 4, + "title": "Total", + "instillFormat": "integer", + "type": "integer" + } + }, + "required": [ + "start-at", + "max-results", + "total" + ], + "title": "Output", + "type": "object" + } + }, + "TASK_LIST_SPRINTS": { + "description": "List sprints in Jira", + "instillShortDescription": "List sprints in Jira", + "input": { + "description": "List sprints in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "board-id" + ], + "properties": { + "board-id": { + "title": "Board ID", + "description": "The ID of the board", + "instillShortDescription": "The ID of the board", + "instillUIOrder": 0, + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "integer" + }, + "start-at": { + "$ref": "#/$defs/common-query-params/start-at", + "instillUIOrder": 1 + }, + "max-results": { + "$ref": "#/$defs/common-query-params/max-results", + "instillUIOrder": 2 + } + }, + "required": [ + "board-id" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Get sprints in Jira", + "instillUIOrder": 0, + "properties": { + "sprints": { + "description": "A array of sprints in Jira", + "instillUIOrder": 1, + "title": "Sprints", + "type": "array", + "items": { + "$ref": "#/$defs/sprint" + } + }, + "start-at": { + "description": "The starting index of the returned boards. Base index: 0", + "instillUIOrder": 2, + "title": "Start At", + "instillFormat": "integer", + "type": "integer" + }, + "max-results": { + "description": "The maximum number of boards", + "instillUIOrder": 3, + "title": "Max Results", + "instillFormat": "integer", + "type": "integer" + }, + "total": { + "description": "The total number of boards", + "instillUIOrder": 4, + "title": "Total", + "instillFormat": "integer", + "type": "integer" + } + }, + "required": [ + "start-at", + "max-results", + "total" + ], + "title": "Output", + "type": "object" + } + }, + "TASK_GET_ISSUE": { + "description": "Get an issue in Jira. The issue will only be returned if the user has permission to view it. Issues returned from this resource include Agile fields, like sprint, closedSprints, flagged, and epic.", + "instillShortDescription": "Get an issue in Jira", + "input": { + "description": "Get an issue in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "issue-key" + ], + "properties": { + "issue-key": { + "title": "Issue Key", + "description": "The key of the issue, e.g. `JRA-1330`", + "instillShortDescription": "The key of the issue", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "update-history": { + "$ref": "#/$defs/common-query-params/update-history", + "instillUIOrder": 1 + } + }, + "required": [ + "issue-key" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Get an issue in Jira", + "instillUIOrder": 0, + "$ref": "#/$defs/issue", + "title": "Output", + "type": "object" + } + }, + "TASK_GET_SPRINT": { + "description": "Get a sprint in Jira. The sprint will only be returned if the user can view the board that the sprint was created on, or view at least one of the issues in the sprint.", + "instillShortDescription": "Get a sprint in Jira", + "input": { + "description": "Get an sprint in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "sprint-id" + ], + "properties": { + "sprint-id": { + "title": "Sprint ID", + "description": "The ID of the sprint. The sprint will only be returned if you can view the board that the sprint was created on, or view at least one of the issues in the sprint.", + "instillShortDescription": "The ID of the sprint", + "instillUIOrder": 0, + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "integer" + } + }, + "required": [ + "sprint-id" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Get an sprint in Jira", + "instillUIOrder": 0, + "$ref": "#/$defs/sprint", + "required": [], + "title": "Output", + "type": "object" + } + }, + "TASK_CREATE_ISSUE": { + "description": "Create an issue in Jira", + "instillShortDescription": "Create an issue in Jira", + "input": { + "description": "Create an issue in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "project-key", + "issue-type", + "status", + "summary", + "description" + ], + "properties": { + "project-key": { + "title": "Project Key", + "description": "The key of the project, e.g. `INS`", + "instillShortDescription": "The key of the project", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "issue-type": { + "title": "Issue Type", + "description": "The type of the issue, can be: `Task`, `Epic`, `Subtask`, etc.", + "instillUIOrder": 1, + "additionalProperties": true, + "instillFormat": "object", + "type": "object", + "required": [ + "issue-type" + ], + "oneOf": [ + { + "properties": { + "issue-type": { + "const": "Epic", + "type": "string" + } + }, + "instillEditOnNodeFields": [ + "issue-type" + ], + "required": [ + "issue-type" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "issue-type": { + "const": "Task", + "type": "string" + } + }, + "instillEditOnNodeFields": [ + "issue-type" + ], + "required": [ + "issue-type" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "issue-type": { + "const": "Subtask", + "type": "string" + }, + "parent-key": { + "title": "Parent Key", + "description": "The key of the parent issue, e.g. `JRA-1330`", + "instillShortDescription": "The key of the parent issue", + "instillUIOrder": 10, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "instillEditOnNodeFields": [ + "issue-type", + "parent-key" + ], + "required": [ + "issue-type", + "parent-key" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "issue-type": { + "const": "Other", + "type": "string" + }, + "custom-issue-type": { + "title": "Issue Type", + "description": "The type of the issue, can be: `Task`, `Epic`, `Subtask`, etc.", + "instillShortDescription": "The type of the issue", + "instillUIOrder": 1, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "instillEditOnNodeFields": [ + "issue-type", + "custom-issue-type" + ], + "required": [ + "issue-type", + "custom-issue-type" + ], + "instillFormat": "object", + "type": "object" + } + ] + }, + "status": { + "title": "Status", + "description": "The status of the issue, can be: `To Do`, `In Progress`, `Done`", + "instillShortDescription": "The status of the issue", + "instillUIOrder": 4, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "summary": { + "title": "Summary", + "description": "The summary of the issue", + "instillShortDescription": "The summary of the issue", + "instillUIOrder": 2, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the issue", + "instillShortDescription": "The description of the issue", + "instillUIOrder": 3, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "assignee": { + "title": "Assignee", + "description": "The assignee of the issue", + "instillShortDescription": "The assignee of the issue", + "instillUIOrder": 5, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "required": [ + "project-key", + "issue-type", + "status" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Create an issue in Jira", + "instillUIOrder": 0, + "$ref": "#/$defs/issue", + "title": "Output", + "type": "object" + } + }, + "TASK_UPDATE_ISSUE": { + "description": "Update an issue in Jira", + "instillShortDescription": "Update an issue in Jira", + "input": { + "description": "Update an issue in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "issue-key", + "update" + ], + "properties": { + "issue-key": { + "$ref": "#/$defs/issue/properties/key", + "title": "Issue Key", + "instillUIOrder": 0 + }, + "update": { + "title": "Update", + "description": "The fields to update", + "instillUIOrder": 1, + "instillFormat": "array:structured/update-field", + "instillAcceptFormats": [ + "array:structured/update-field" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "array", + "items": { + "instillFormat": "structured/update-field", + "properties": { + "action": { + "description": "The action to perform, can be: `add`, `copy`, `edit`, `remove`, `set`", + "enum": [ + "add", + "copy", + "edit", + "remove", + "set" + ], + "instillUIOrder": 3, + "instillFormat": "string", + "type": "string", + "title": "Content" + }, + "field-name": { + "description": "The name of the field to update", + "instillUIOrder": 4, + "instillFormat": "string", + "type": "string", + "title": "Field Name" + }, + "value": { + "description": "The value of the field to update", + "instillUIOrder": 5, + "instillFormat": "string", + "type": "string", + "title": "Value" + } + }, + "required": [ + "action", + "field-name" + ], + "type": "object" + } + } + }, + "required": [ + "issue-key" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Create an issue in Jira", + "instillUIOrder": 0, + "$ref": "#/$defs/issue", + "title": "Output", + "type": "object" + } + }, + "TASK_MOVE_ISSUE_TO_EPIC": { + "description": "Move an issue to an epic in Jira", + "instillShortDescription": "Move an issue to an epic in Jira", + "input": { + "description": "Move an issue to an epic in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "issue-key", + "epic-key" + ], + "properties": { + "issue-key": { + "$ref": "#/$defs/issue/properties/key", + "title": "Issue Key", + "instillUIOrder": 0 + }, + "epic-key": { + "title": "Epic Key", + "description": "The key of the epic, e.g. `JRA-1330`", + "instillShortDescription": "The key of the epic", + "instillUIOrder": 1, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "required": [ + "issue-key", + "epic-key" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Move an issue to an epic in Jira", + "instillUIOrder": 0, + "properties": { + "success": { + "description": "Whether the issue is moved to the epic", + "instillUIOrder": 0, + "title": "Success", + "instillFormat": "boolean", + "type": "boolean" + } + }, + "title": "Output", + "type": "object" + } + } +} From b1dece35fbf89364be652cc5eda152020dae461e Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Fri, 26 Jul 2024 00:12:04 +0100 Subject: [PATCH 02/18] feat: add sprint related task --- application/jira/v1/config/definition.json | 5 +- application/jira/v1/config/tasks.json | 337 +++++++++++++++++++++ 2 files changed, 341 insertions(+), 1 deletion(-) diff --git a/application/jira/v1/config/definition.json b/application/jira/v1/config/definition.json index 74124e5e..e6626872 100644 --- a/application/jira/v1/config/definition.json +++ b/application/jira/v1/config/definition.json @@ -7,7 +7,10 @@ "TASK_GET_SPRINT", "TASK_CREATE_ISSUE", "TASK_UPDATE_ISSUE", - "TASK_MOVE_ISSUE_TO_EPIC" + "TASK_MOVE_ISSUE_TO_EPIC", + "TASK_CREATE_SPRINT", + "TASK_UPDATE_SPRINT", + "TASK_RANK_EPIC" ], "documentationUrl": "https://www.instill.tech/docs/component/application/jira", "icon": "assets/Jira.svg", diff --git a/application/jira/v1/config/tasks.json b/application/jira/v1/config/tasks.json index 68045064..e90cf512 100644 --- a/application/jira/v1/config/tasks.json +++ b/application/jira/v1/config/tasks.json @@ -1177,5 +1177,342 @@ "title": "Output", "type": "object" } + }, + "TASK_CREATE_SPRINT": { + "description": "Create a sprint in Jira", + "instillShortDescription": "Create a sprint in Jira", + "input": { + "description": "Create a sprint in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "board-id", + "name", + "goal", + "start-date", + "end-date" + ], + "properties": { + "board-name": { + "title": "Board Name", + "description": "The name of the board", + "instillShortDescription": "The name of the board", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the sprint", + "instillShortDescription": "The name of the sprint", + "instillUIOrder": 1, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "goal": { + "title": "Goal", + "description": "The goal of the sprint", + "instillShortDescription": "The goal of the sprint", + "instillUIOrder": 2, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "start-date": { + "title": "Start Date", + "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "instillShortDescription": "The start date of the sprint", + "instillUIOrder": 3, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "end-date": { + "title": "End Date", + "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "instillShortDescription": "The end date of the sprint", + "instillUIOrder": 4, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "required": [ + "board-id", + "name", + "goal", + "start-date", + "end-date" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Create a sprint in Jira", + "instillUIOrder": 0, + "$ref": "#/$defs/sprint", + "title": "Output", + "type": "object" + } + }, + "TASK_UPDATE_SPRINT": { + "description": "Update a sprint in Jira", + "instillShortDescription": "Update a sprint in Jira", + "input": { + "description": "Update a sprint in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "sprint-key", + "name", + "goal", + "start-date", + "end-date", + "complete-date" + ], + "properties": { + "sprint-key": { + "title": "Sprint Key", + "description": "The key of the sprint", + "instillShortDescription": "The key of the sprint", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the sprint", + "instillShortDescription": "The name of the sprint", + "instillUIOrder": 1, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "goal": { + "title": "Goal", + "description": "The goal of the sprint", + "instillShortDescription": "The goal of the sprint", + "instillUIOrder": 2, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "start-date": { + "title": "Start Date", + "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "instillShortDescription": "The start date of the sprint", + "instillUIOrder": 3, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "end-date": { + "title": "End Date", + "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "instillShortDescription": "The end date of the sprint", + "instillUIOrder": 4, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "complete-date": { + "title": "Complete Date", + "description": "The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "instillShortDescription": "The complete date of the sprint", + "instillUIOrder": 5, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "required": [ + "sprint-key" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Create a sprint in Jira", + "instillUIOrder": 0, + "$ref": "#/$defs/sprint", + "title": "Output", + "type": "object" + } + }, + "TASK_RANK_EPIC": { + "description": "Rank an epic in Jira", + "instillShortDescription": "Rank an epic in Jira", + "input": { + "description": "Rank an epic in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "epic-key", + "rank" + ], + "properties": { + "epic-key": { + "title": "Epic Key", + "description": "The key of the epic, e.g. `JRA-1330`", + "instillShortDescription": "The key of the epic", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "after": { + "title": "Rank After Epic", + "description": "The key of the epic to rank after", + "instillShortDescription": "The key of the epic to rank after", + "instillUIOrder": 1, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "before": { + "title": "Rank Before Epic", + "description": "The key of the epic to rank before", + "instillShortDescription": "The key of the epic to rank before", + "instillUIOrder": 2, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "rank": { + "title": "Rank", + "description": "The custom rank field ID of the epic", + "instillShortDescription": "The custom rank field ID of the epic", + "instillUIOrder": 3, + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "integer" + } + }, + "required": [ + "epic-key", + "rank" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Rank an epic in Jira", + "instillUIOrder": 0, + "properties": { + "success": { + "description": "Whether the epic is ranked", + "instillUIOrder": 0, + "title": "Success", + "instillFormat": "boolean", + "type": "boolean" + } + }, + "title": "Output", + "type": "object" + } } } From dd664de00e69e8d98455d76f6101313b33c419f1 Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Wed, 31 Jul 2024 00:05:36 +0100 Subject: [PATCH 03/18] chore: merge task of jira --- application/jira/v1/config/definition.json | 1 - application/jira/v1/config/tasks.json | 191 ++++++++++----------- 2 files changed, 88 insertions(+), 104 deletions(-) diff --git a/application/jira/v1/config/definition.json b/application/jira/v1/config/definition.json index e6626872..af4252cc 100644 --- a/application/jira/v1/config/definition.json +++ b/application/jira/v1/config/definition.json @@ -7,7 +7,6 @@ "TASK_GET_SPRINT", "TASK_CREATE_ISSUE", "TASK_UPDATE_ISSUE", - "TASK_MOVE_ISSUE_TO_EPIC", "TASK_CREATE_SPRINT", "TASK_UPDATE_SPRINT", "TASK_RANK_EPIC" diff --git a/application/jira/v1/config/tasks.json b/application/jira/v1/config/tasks.json index e90cf512..147fbc4a 100644 --- a/application/jira/v1/config/tasks.json +++ b/application/jira/v1/config/tasks.json @@ -1056,56 +1056,97 @@ }, "update": { "title": "Update", - "description": "The fields to update", + "description": "Update an issue in Jira", "instillUIOrder": 1, - "instillFormat": "array:structured/update-field", - "instillAcceptFormats": [ - "array:structured/update-field" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "array", - "items": { - "instillFormat": "structured/update-field", - "properties": { - "action": { - "description": "The action to perform, can be: `add`, `copy`, `edit`, `remove`, `set`", - "enum": [ - "add", - "copy", - "edit", - "remove", - "set" - ], - "instillUIOrder": 3, - "instillFormat": "string", - "type": "string", - "title": "Content" - }, - "field-name": { - "description": "The name of the field to update", - "instillUIOrder": 4, - "instillFormat": "string", - "type": "string", - "title": "Field Name" - }, - "value": { - "description": "The value of the field to update", - "instillUIOrder": 5, - "instillFormat": "string", - "type": "string", - "title": "Value" + "additionalProperties": true, + "instillFormat": "object", + "type": "object", + "oneOf": [ + { + "properties": { + "update": { + "const": "Custom Update", + "type": "string" + }, + "update-fields": { + "title": "Update Fields", + "description": "The fields to update", + "instillFormat": "array:structured/update-field", + "instillAcceptFormats": [ + "array:structured/update-field" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "array", + "items": { + "instillFormat": "structured/update-field", + "properties": { + "action": { + "description": "The action to perform, can be: `add`, `copy`, `edit`, `remove`, `set`", + "enum": [ + "add", + "copy", + "edit", + "remove", + "set" + ], + "instillUIOrder": 3, + "instillFormat": "string", + "type": "string", + "title": "Action" + }, + "field-name": { + "description": "The name of the field to update", + "instillUIOrder": 4, + "instillFormat": "string", + "type": "string", + "title": "Field Name" + }, + "value": { + "description": "The value of the field to update", + "instillUIOrder": 5, + "instillFormat": "string", + "type": "string", + "title": "Value" + } + }, + "required": [ + "action", + "field-name" + ], + "type": "object" + } + } } }, - "required": [ - "action", - "field-name" - ], - "type": "object" - } + { + "properties": { + "update": { + "const": "Move Issue to Epic", + "type": "string" + }, + "epic-key": { + "title": "Epic Key", + "description": "The key of the epic, e.g. `JRA-1330`", + "instillShortDescription": "The key of the epic", + "instillUIOrder": 11, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + } + } + ] } }, "required": [ @@ -1122,62 +1163,6 @@ "type": "object" } }, - "TASK_MOVE_ISSUE_TO_EPIC": { - "description": "Move an issue to an epic in Jira", - "instillShortDescription": "Move an issue to an epic in Jira", - "input": { - "description": "Move an issue to an epic in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "issue-key", - "epic-key" - ], - "properties": { - "issue-key": { - "$ref": "#/$defs/issue/properties/key", - "title": "Issue Key", - "instillUIOrder": 0 - }, - "epic-key": { - "title": "Epic Key", - "description": "The key of the epic, e.g. `JRA-1330`", - "instillShortDescription": "The key of the epic", - "instillUIOrder": 1, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - } - }, - "required": [ - "issue-key", - "epic-key" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Move an issue to an epic in Jira", - "instillUIOrder": 0, - "properties": { - "success": { - "description": "Whether the issue is moved to the epic", - "instillUIOrder": 0, - "title": "Success", - "instillFormat": "boolean", - "type": "boolean" - } - }, - "title": "Output", - "type": "object" - } - }, "TASK_CREATE_SPRINT": { "description": "Create a sprint in Jira", "instillShortDescription": "Create a sprint in Jira", From c666326cc14a1c6903fde6ab4f684966422ea207 Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Wed, 31 Jul 2024 10:51:10 +0100 Subject: [PATCH 04/18] chore: move change from v1 to v0 --- application/jira/v0/config/definition.json | 7 +- application/jira/v0/config/tasks.json | 697 ++++++++- application/jira/v1/config/definition.json | 26 - application/jira/v1/config/setup.json | 57 - application/jira/v1/config/tasks.json | 1503 -------------------- 5 files changed, 702 insertions(+), 1588 deletions(-) delete mode 100644 application/jira/v1/config/definition.json delete mode 100644 application/jira/v1/config/setup.json delete mode 100644 application/jira/v1/config/tasks.json diff --git a/application/jira/v0/config/definition.json b/application/jira/v0/config/definition.json index df7c32f4..23b9f026 100644 --- a/application/jira/v0/config/definition.json +++ b/application/jira/v0/config/definition.json @@ -4,7 +4,12 @@ "TASK_LIST_ISSUES", "TASK_LIST_SPRINTS", "TASK_GET_ISSUE", - "TASK_GET_SPRINT" + "TASK_GET_SPRINT", + "TASK_CREATE_ISSUE", + "TASK_UPDATE_ISSUE", + "TASK_CREATE_SPRINT", + "TASK_UPDATE_SPRINT", + "TASK_RANK_EPIC" ], "documentationUrl": "https://www.instill.tech/docs/component/application/jira", "icon": "assets/jira.svg", diff --git a/application/jira/v0/config/tasks.json b/application/jira/v0/config/tasks.json index e2239ea1..147fbc4a 100644 --- a/application/jira/v0/config/tasks.json +++ b/application/jira/v0/config/tasks.json @@ -57,10 +57,19 @@ "type": "string" }, "key": { + "title": "Key", "description": "The key of the issue, e.g. `JRA-1330`", + "instillShortDescription": "The key of the issue", "instillUIOrder": 1, "instillFormat": "string", - "title": "Key", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], "type": "string" }, "self": { @@ -804,5 +813,691 @@ "title": "Output", "type": "object" } + }, + "TASK_CREATE_ISSUE": { + "description": "Create an issue in Jira", + "instillShortDescription": "Create an issue in Jira", + "input": { + "description": "Create an issue in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "project-key", + "issue-type", + "status", + "summary", + "description" + ], + "properties": { + "project-key": { + "title": "Project Key", + "description": "The key of the project, e.g. `INS`", + "instillShortDescription": "The key of the project", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "issue-type": { + "title": "Issue Type", + "description": "The type of the issue, can be: `Task`, `Epic`, `Subtask`, etc.", + "instillUIOrder": 1, + "additionalProperties": true, + "instillFormat": "object", + "type": "object", + "required": [ + "issue-type" + ], + "oneOf": [ + { + "properties": { + "issue-type": { + "const": "Epic", + "type": "string" + } + }, + "instillEditOnNodeFields": [ + "issue-type" + ], + "required": [ + "issue-type" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "issue-type": { + "const": "Task", + "type": "string" + } + }, + "instillEditOnNodeFields": [ + "issue-type" + ], + "required": [ + "issue-type" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "issue-type": { + "const": "Subtask", + "type": "string" + }, + "parent-key": { + "title": "Parent Key", + "description": "The key of the parent issue, e.g. `JRA-1330`", + "instillShortDescription": "The key of the parent issue", + "instillUIOrder": 10, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "instillEditOnNodeFields": [ + "issue-type", + "parent-key" + ], + "required": [ + "issue-type", + "parent-key" + ], + "instillFormat": "object", + "type": "object" + }, + { + "properties": { + "issue-type": { + "const": "Other", + "type": "string" + }, + "custom-issue-type": { + "title": "Issue Type", + "description": "The type of the issue, can be: `Task`, `Epic`, `Subtask`, etc.", + "instillShortDescription": "The type of the issue", + "instillUIOrder": 1, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "instillEditOnNodeFields": [ + "issue-type", + "custom-issue-type" + ], + "required": [ + "issue-type", + "custom-issue-type" + ], + "instillFormat": "object", + "type": "object" + } + ] + }, + "status": { + "title": "Status", + "description": "The status of the issue, can be: `To Do`, `In Progress`, `Done`", + "instillShortDescription": "The status of the issue", + "instillUIOrder": 4, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "summary": { + "title": "Summary", + "description": "The summary of the issue", + "instillShortDescription": "The summary of the issue", + "instillUIOrder": 2, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the issue", + "instillShortDescription": "The description of the issue", + "instillUIOrder": 3, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "assignee": { + "title": "Assignee", + "description": "The assignee of the issue", + "instillShortDescription": "The assignee of the issue", + "instillUIOrder": 5, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "required": [ + "project-key", + "issue-type", + "status" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Create an issue in Jira", + "instillUIOrder": 0, + "$ref": "#/$defs/issue", + "title": "Output", + "type": "object" + } + }, + "TASK_UPDATE_ISSUE": { + "description": "Update an issue in Jira", + "instillShortDescription": "Update an issue in Jira", + "input": { + "description": "Update an issue in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "issue-key", + "update" + ], + "properties": { + "issue-key": { + "$ref": "#/$defs/issue/properties/key", + "title": "Issue Key", + "instillUIOrder": 0 + }, + "update": { + "title": "Update", + "description": "Update an issue in Jira", + "instillUIOrder": 1, + "additionalProperties": true, + "instillFormat": "object", + "type": "object", + "oneOf": [ + { + "properties": { + "update": { + "const": "Custom Update", + "type": "string" + }, + "update-fields": { + "title": "Update Fields", + "description": "The fields to update", + "instillFormat": "array:structured/update-field", + "instillAcceptFormats": [ + "array:structured/update-field" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "array", + "items": { + "instillFormat": "structured/update-field", + "properties": { + "action": { + "description": "The action to perform, can be: `add`, `copy`, `edit`, `remove`, `set`", + "enum": [ + "add", + "copy", + "edit", + "remove", + "set" + ], + "instillUIOrder": 3, + "instillFormat": "string", + "type": "string", + "title": "Action" + }, + "field-name": { + "description": "The name of the field to update", + "instillUIOrder": 4, + "instillFormat": "string", + "type": "string", + "title": "Field Name" + }, + "value": { + "description": "The value of the field to update", + "instillUIOrder": 5, + "instillFormat": "string", + "type": "string", + "title": "Value" + } + }, + "required": [ + "action", + "field-name" + ], + "type": "object" + } + } + } + }, + { + "properties": { + "update": { + "const": "Move Issue to Epic", + "type": "string" + }, + "epic-key": { + "title": "Epic Key", + "description": "The key of the epic, e.g. `JRA-1330`", + "instillShortDescription": "The key of the epic", + "instillUIOrder": 11, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + } + } + ] + } + }, + "required": [ + "issue-key" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Create an issue in Jira", + "instillUIOrder": 0, + "$ref": "#/$defs/issue", + "title": "Output", + "type": "object" + } + }, + "TASK_CREATE_SPRINT": { + "description": "Create a sprint in Jira", + "instillShortDescription": "Create a sprint in Jira", + "input": { + "description": "Create a sprint in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "board-id", + "name", + "goal", + "start-date", + "end-date" + ], + "properties": { + "board-name": { + "title": "Board Name", + "description": "The name of the board", + "instillShortDescription": "The name of the board", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the sprint", + "instillShortDescription": "The name of the sprint", + "instillUIOrder": 1, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "goal": { + "title": "Goal", + "description": "The goal of the sprint", + "instillShortDescription": "The goal of the sprint", + "instillUIOrder": 2, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "start-date": { + "title": "Start Date", + "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "instillShortDescription": "The start date of the sprint", + "instillUIOrder": 3, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "end-date": { + "title": "End Date", + "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "instillShortDescription": "The end date of the sprint", + "instillUIOrder": 4, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "required": [ + "board-id", + "name", + "goal", + "start-date", + "end-date" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Create a sprint in Jira", + "instillUIOrder": 0, + "$ref": "#/$defs/sprint", + "title": "Output", + "type": "object" + } + }, + "TASK_UPDATE_SPRINT": { + "description": "Update a sprint in Jira", + "instillShortDescription": "Update a sprint in Jira", + "input": { + "description": "Update a sprint in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "sprint-key", + "name", + "goal", + "start-date", + "end-date", + "complete-date" + ], + "properties": { + "sprint-key": { + "title": "Sprint Key", + "description": "The key of the sprint", + "instillShortDescription": "The key of the sprint", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "name": { + "title": "Name", + "description": "The name of the sprint", + "instillShortDescription": "The name of the sprint", + "instillUIOrder": 1, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "goal": { + "title": "Goal", + "description": "The goal of the sprint", + "instillShortDescription": "The goal of the sprint", + "instillUIOrder": 2, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "start-date": { + "title": "Start Date", + "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "instillShortDescription": "The start date of the sprint", + "instillUIOrder": 3, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "end-date": { + "title": "End Date", + "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "instillShortDescription": "The end date of the sprint", + "instillUIOrder": 4, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "complete-date": { + "title": "Complete Date", + "description": "The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "instillShortDescription": "The complete date of the sprint", + "instillUIOrder": 5, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + } + }, + "required": [ + "sprint-key" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Create a sprint in Jira", + "instillUIOrder": 0, + "$ref": "#/$defs/sprint", + "title": "Output", + "type": "object" + } + }, + "TASK_RANK_EPIC": { + "description": "Rank an epic in Jira", + "instillShortDescription": "Rank an epic in Jira", + "input": { + "description": "Rank an epic in Jira", + "instillUIOrder": 0, + "instillEditOnNodeFields": [ + "epic-key", + "rank" + ], + "properties": { + "epic-key": { + "title": "Epic Key", + "description": "The key of the epic, e.g. `JRA-1330`", + "instillShortDescription": "The key of the epic", + "instillUIOrder": 0, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "after": { + "title": "Rank After Epic", + "description": "The key of the epic to rank after", + "instillShortDescription": "The key of the epic to rank after", + "instillUIOrder": 1, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "before": { + "title": "Rank Before Epic", + "description": "The key of the epic to rank before", + "instillShortDescription": "The key of the epic to rank before", + "instillUIOrder": 2, + "instillFormat": "string", + "instillAcceptFormats": [ + "string" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "string" + }, + "rank": { + "title": "Rank", + "description": "The custom rank field ID of the epic", + "instillShortDescription": "The custom rank field ID of the epic", + "instillUIOrder": 3, + "instillFormat": "integer", + "instillAcceptFormats": [ + "integer" + ], + "instillUpstreamTypes": [ + "value", + "reference" + ], + "type": "integer" + } + }, + "required": [ + "epic-key", + "rank" + ], + "title": "Input", + "type": "object" + }, + "output": { + "description": "Rank an epic in Jira", + "instillUIOrder": 0, + "properties": { + "success": { + "description": "Whether the epic is ranked", + "instillUIOrder": 0, + "title": "Success", + "instillFormat": "boolean", + "type": "boolean" + } + }, + "title": "Output", + "type": "object" + } } } diff --git a/application/jira/v1/config/definition.json b/application/jira/v1/config/definition.json deleted file mode 100644 index af4252cc..00000000 --- a/application/jira/v1/config/definition.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "availableTasks": [ - "TASK_LIST_BOARDS", - "TASK_LIST_ISSUES", - "TASK_LIST_SPRINTS", - "TASK_GET_ISSUE", - "TASK_GET_SPRINT", - "TASK_CREATE_ISSUE", - "TASK_UPDATE_ISSUE", - "TASK_CREATE_SPRINT", - "TASK_UPDATE_SPRINT", - "TASK_RANK_EPIC" - ], - "documentationUrl": "https://www.instill.tech/docs/component/application/jira", - "icon": "assets/Jira.svg", - "id": "jira", - "public": true, - "title": "Jira", - "description": "Do anything available on Jira", - "tombstone": false, - "type": "COMPONENT_TYPE_APPLICATION", - "uid": "3b27f50d-a754-4b9d-8141-95aaec647cc5", - "version": "0.1.0", - "sourceUrl": "https://github.com/instill-ai/component/blob/main/application/jira/v0", - "releaseStage": "RELEASE_STAGE_ALPHA" -} diff --git a/application/jira/v1/config/setup.json b/application/jira/v1/config/setup.json deleted file mode 100644 index e9b18f77..00000000 --- a/application/jira/v1/config/setup.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "token": { - "description": "Fill in your Jira API token. You can generate one from your Jira account \"settings > security > API tokens\".", - "instillUpstreamTypes": [ - "reference" - ], - "instillAcceptFormats": [ - "string" - ], - "instillSecret": true, - "instillUIOrder": 0, - "title": "Token", - "type": "string" - }, - "email": { - "description": "Fill in your Jira email address.", - "instillUpstreamTypes": [ - "value", - "reference" - ], - "instillAcceptFormats": [ - "string" - ], - "instillUIOrder": 1, - "title": "Email", - "type": "string" - }, - "base-url": { - "description": "Fill in your Jira base URL. For example, if your Jira URL is \"https://mycompany.atlassian.net/...\", then your base URL is https://mycompany.atlassian.net.", - "instillUpstreamTypes": [ - "value", - "reference" - ], - "instillAcceptFormats": [ - "string" - ], - "instillUIOrder": 1, - "title": "Base URL", - "type": "string" - } - }, - "required": [ - "token", - "email", - "base-url" - ], - "instillEditOnNodeFields": [ - "token", - "email", - "base-url" - ], - "title": "Jira Connection", - "type": "object" -} diff --git a/application/jira/v1/config/tasks.json b/application/jira/v1/config/tasks.json deleted file mode 100644 index 147fbc4a..00000000 --- a/application/jira/v1/config/tasks.json +++ /dev/null @@ -1,1503 +0,0 @@ -{ - "$defs": { - "common-query-params": { - "start-at": { - "default": 0, - "description": "The starting index of the returned boards. Base index: 0. Default is 0", - "instillUIOrder": 3, - "title": "Start At", - "instillFormat": "integer", - "instillAcceptFormats": [ - "integer" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "integer" - }, - "max-results": { - "default": 50, - "description": "The maximum number of boards to return. Default is 50", - "instillUIOrder": 4, - "title": "Max Results", - "instillFormat": "integer", - "instillAcceptFormats": [ - "integer" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "integer" - }, - "update-history": { - "description": "Whether the action taken is added to the user's Recent history, as shown under `Your Work` in Jira.", - "title": "Update History", - "instillUIOrder": 5, - "instillFormat": "boolean", - "instillAcceptFormats": [ - "boolean" - ], - "instillUpstreamTypes": [ - "value" - ], - "type": "boolean" - } - }, - "issue": { - "properties": { - "id": { - "description": "The ID of the issue", - "instillUIOrder": 0, - "title": "ID", - "instillFormat": "string", - "type": "string" - }, - "key": { - "title": "Key", - "description": "The key of the issue, e.g. `JRA-1330`", - "instillShortDescription": "The key of the issue", - "instillUIOrder": 1, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "self": { - "description": "The URL of the issue", - "instillUIOrder": 2, - "instillFormat": "string", - "title": "Self", - "type": "string" - }, - "fields": { - "description": "The fields of the issue. All navigable and Agile fields are returned", - "instillUIOrder": 3, - "instillFormat": "semi-structured/json", - "title": "Fields", - "type": "object", - "required": [] - }, - "issue-type": { - "description": "The type of the issue, can be: `Task`, `Epic`", - "instillUIOrder": 4, - "instillFormat": "string", - "title": "Issue Type", - "type": "string" - }, - "summary": { - "description": "The summary of the issue", - "instillUIOrder": 5, - "instillFormat": "string", - "title": "Summary", - "type": "string" - }, - "description": { - "description": "The description of the issue", - "instillUIOrder": 6, - "instillFormat": "string", - "title": "Description", - "type": "string" - }, - "status": { - "description": "The status of the issue, can be: `To Do`, `In Progress`, `Done`", - "instillUIOrder": 7, - "instillFormat": "string", - "title": "Status", - "type": "string" - } - }, - "required": [ - "id", - "key", - "self", - "fields" - ], - "title": "Issue", - "type": "object" - }, - "sprint": { - "properties": { - "id": { - "title": "ID", - "description": "The ID of the sprint", - "type": "integer", - "instillUIOrder": 0, - "instillFormat": "integer" - }, - "self": { - "title": "Self", - "description": "The URL of the sprint", - "type": "string", - "instillUIOrder": 1, - "instillFormat": "string" - }, - "state": { - "title": "State", - "description": "The state of the sprint, can be: `active`, `closed`, `future`", - "type": "string", - "instillUIOrder": 2, - "instillFormat": "string" - }, - "name": { - "title": "Name", - "description": "The name of the sprint", - "type": "string", - "instillUIOrder": 3, - "instillFormat": "string" - }, - "start-date": { - "title": "Start Date", - "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", - "type": "string", - "instillUIOrder": 4, - "instillFormat": "string" - }, - "end-date": { - "title": "End Date", - "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", - "type": "string", - "instillUIOrder": 5, - "instillFormat": "string" - }, - "complete-date": { - "title": "Complete Date", - "description": "The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", - "type": "string", - "instillUIOrder": 6, - "instillFormat": "string" - }, - "origin-board-id": { - "title": "Origin Board ID", - "description": "The ID of the origin board", - "type": "integer", - "instillUIOrder": 7, - "instillFormat": "integer" - }, - "goal": { - "title": "Goal", - "description": "The Goal of the sprint", - "type": "string", - "instillUIOrder": 8, - "instillFormat": "string" - } - }, - "required": [ - "id", - "self" - ], - "title": "Sprint", - "type": "object" - } - }, - "TASK_LIST_BOARDS": { - "instillShortDescription": "List all boards in Jira", - "input": { - "description": "List all boards in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [], - "properties": { - "project-key-or-id": { - "default": "", - "title": "Project Key or ID", - "description": "This filters results to boards that are relevant to a project. Relevance meaning that the JQL filter defined in board contains a reference to a project.", - "instillShortDescription": "The project key or ID, e.g. `INS`. Default is empty", - "instillUIOrder": 0, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "board-type": { - "default": "simple", - "description": "The type of board, can be: scrum, kanban, simple. Default is simple", - "instillUIOrder": 1, - "enum": [ - "scrum", - "kanban", - "simple" - ], - "title": "Board Type", - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "name": { - "default": "", - "description": "Name filters results to boards that match or partially match the specified name. Default is empty", - "instillUIOrder": 2, - "title": "Name", - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "start-at": { - "$ref": "#/$defs/common-query-params/start-at", - "instillUIOrder": 3 - }, - "max-results": { - "$ref": "#/$defs/common-query-params/max-results", - "instillUIOrder": 4 - } - }, - "required": [], - "title": "Input", - "type": "object" - }, - "output": { - "description": "List all boards in Jira", - "instillUIOrder": 0, - "properties": { - "boards": { - "description": "A array of boards in Jira", - "instillUIOrder": 1, - "title": "Boards", - "type": "array", - "items": { - "properties": { - "id": { - "description": "The ID of the board", - "instillUIOrder": 0, - "title": "ID", - "instillFormat": "integer", - "type": "integer" - }, - "name": { - "description": "The name of the board", - "instillUIOrder": 1, - "title": "Name", - "instillFormat": "string", - "type": "string" - }, - "type": { - "description": "The type of the board", - "instillUIOrder": 2, - "title": "Type", - "instillFormat": "string", - "type": "string" - }, - "self": { - "description": "The URL of the board", - "instillUIOrder": 3, - "title": "Self", - "instillFormat": "string", - "type": "string" - } - }, - "type": "object", - "required": [ - "id", - "name", - "type", - "self" - ] - } - }, - "start-at": { - "description": "The starting index of the returned boards. Base index: 0", - "instillUIOrder": 2, - "title": "Start At", - "instillFormat": "integer", - "type": "integer" - }, - "max-results": { - "description": "The maximum number of boards", - "instillUIOrder": 3, - "title": "Max Results", - "instillFormat": "integer", - "type": "integer" - }, - "total": { - "description": "The total number of boards", - "instillUIOrder": 4, - "title": "Total", - "instillFormat": "integer", - "type": "integer" - }, - "is-last": { - "description": "Whether the last board is reached", - "instillUIOrder": 5, - "title": "Is Last", - "instillFormat": "boolean", - "type": "boolean" - } - }, - "required": [ - "start-at", - "max-results", - "total", - "is-last" - ], - "title": "Output", - "type": "object" - } - }, - "TASK_LIST_ISSUES": { - "description": "List issues in Jira", - "instillShortDescription": "List issues in Jira", - "input": { - "description": "List issues in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "board-name", - "range" - ], - "properties": { - "board-name": { - "title": "Board Name", - "description": "The name of the board", - "instillShortDescription": "The name of the board", - "instillUIOrder": 0, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "range": { - "title": "Range", - "description": "Choose the range of issues to return. Default is `all`", - "instillUIOrder": 1, - "additionalProperties": true, - "instillFormat": "object", - "type": "object", - "required": [ - "range" - ], - "oneOf": [ - { - "properties": { - "range": { - "const": "All", - "type": "string" - } - }, - "required": [ - "range" - ], - "instillEditOnNodeFields": [ - "range" - ], - "instillFormat": "object", - "type": "object" - }, - { - "properties": { - "range": { - "const": "Standard Issues", - "type": "string" - } - }, - "required": [ - "range" - ], - "instillEditOnNodeFields": [ - "range" - ], - "instillFormat": "object", - "type": "object" - }, - { - "properties": { - "range": { - "const": "Epics only", - "type": "string" - } - }, - "required": [ - "range" - ], - "instillEditOnNodeFields": [ - "range" - ], - "instillFormat": "object", - "type": "object" - }, - { - "properties": { - "range": { - "const": "In backlog only", - "type": "string" - } - }, - "required": [ - "range" - ], - "instillEditOnNodeFields": [ - "range" - ], - "instillFormat": "object", - "type": "object" - }, - { - "properties": { - "range": { - "const": "Issues of an epic", - "type": "string" - }, - "epic-key": { - "title": "Epic Key", - "description": "The Key of the epic, e.g. `JRA-1330`", - "instillShortDescription": "The Key of the epic", - "instillUIOrder": 10, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - } - }, - "required": [ - "range", - "epic-key" - ], - "instillEditOnNodeFields": [ - "range", - "epic-key" - ], - "instillFormat": "object", - "type": "object" - }, - { - "properties": { - "range": { - "const": "Issues of a sprint", - "type": "string" - }, - "sprint-name": { - "title": "Sprint Name", - "description": "The name of the sprint", - "instillShortDescription": "The Name of the sprint", - "instillUIOrder": 10, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - } - }, - "required": [ - "range", - "sprint-name" - ], - "instillEditOnNodeFields": [ - "range", - "sprint-name" - ], - "instillFormat": "object", - "type": "object" - }, - { - "properties": { - "range": { - "const": "Issues without epic assigned", - "type": "string" - } - }, - "required": [ - "range" - ], - "instillEditOnNodeFields": [ - "range" - ], - "instillFormat": "object", - "type": "object" - }, - { - "properties": { - "range": { - "const": "JQL query", - "type": "string" - }, - "jql": { - "title": "JQL", - "description": "The JQL query. For example, `type = \"Task\" AND status = \"Done\"`. For more information, see Advanced searching", - "instillShortDescription": "The JQL query", - "instillUIOrder": 10, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - } - }, - "required": [ - "range", - "jql" - ], - "instillEditOnNodeFields": [ - "range", - "jql" - ], - "instillFormat": "object", - "type": "object" - } - ] - }, - "start-at": { - "$ref": "#/$defs/common-query-params/start-at", - "instillUIOrder": 3 - }, - "max-results": { - "$ref": "#/$defs/common-query-params/max-results", - "instillUIOrder": 4 - } - }, - "required": [ - "board-name" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Get issues in Jira", - "instillUIOrder": 0, - "properties": { - "issues": { - "description": "A array of issues in Jira", - "instillUIOrder": 1, - "title": "Issues", - "type": "array", - "items": { - "$ref": "#/$defs/issue" - } - }, - "start-at": { - "description": "The starting index of the returned boards. Base index: 0", - "instillUIOrder": 2, - "title": "Start At", - "instillFormat": "integer", - "type": "integer" - }, - "max-results": { - "description": "The maximum number of boards", - "instillUIOrder": 3, - "title": "Max Results", - "instillFormat": "integer", - "type": "integer" - }, - "total": { - "description": "The total number of boards", - "instillUIOrder": 4, - "title": "Total", - "instillFormat": "integer", - "type": "integer" - } - }, - "required": [ - "start-at", - "max-results", - "total" - ], - "title": "Output", - "type": "object" - } - }, - "TASK_LIST_SPRINTS": { - "description": "List sprints in Jira", - "instillShortDescription": "List sprints in Jira", - "input": { - "description": "List sprints in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "board-id" - ], - "properties": { - "board-id": { - "title": "Board ID", - "description": "The ID of the board", - "instillShortDescription": "The ID of the board", - "instillUIOrder": 0, - "instillFormat": "integer", - "instillAcceptFormats": [ - "integer" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "integer" - }, - "start-at": { - "$ref": "#/$defs/common-query-params/start-at", - "instillUIOrder": 1 - }, - "max-results": { - "$ref": "#/$defs/common-query-params/max-results", - "instillUIOrder": 2 - } - }, - "required": [ - "board-id" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Get sprints in Jira", - "instillUIOrder": 0, - "properties": { - "sprints": { - "description": "A array of sprints in Jira", - "instillUIOrder": 1, - "title": "Sprints", - "type": "array", - "items": { - "$ref": "#/$defs/sprint" - } - }, - "start-at": { - "description": "The starting index of the returned boards. Base index: 0", - "instillUIOrder": 2, - "title": "Start At", - "instillFormat": "integer", - "type": "integer" - }, - "max-results": { - "description": "The maximum number of boards", - "instillUIOrder": 3, - "title": "Max Results", - "instillFormat": "integer", - "type": "integer" - }, - "total": { - "description": "The total number of boards", - "instillUIOrder": 4, - "title": "Total", - "instillFormat": "integer", - "type": "integer" - } - }, - "required": [ - "start-at", - "max-results", - "total" - ], - "title": "Output", - "type": "object" - } - }, - "TASK_GET_ISSUE": { - "description": "Get an issue in Jira. The issue will only be returned if the user has permission to view it. Issues returned from this resource include Agile fields, like sprint, closedSprints, flagged, and epic.", - "instillShortDescription": "Get an issue in Jira", - "input": { - "description": "Get an issue in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "issue-key" - ], - "properties": { - "issue-key": { - "title": "Issue Key", - "description": "The key of the issue, e.g. `JRA-1330`", - "instillShortDescription": "The key of the issue", - "instillUIOrder": 0, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "update-history": { - "$ref": "#/$defs/common-query-params/update-history", - "instillUIOrder": 1 - } - }, - "required": [ - "issue-key" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Get an issue in Jira", - "instillUIOrder": 0, - "$ref": "#/$defs/issue", - "title": "Output", - "type": "object" - } - }, - "TASK_GET_SPRINT": { - "description": "Get a sprint in Jira. The sprint will only be returned if the user can view the board that the sprint was created on, or view at least one of the issues in the sprint.", - "instillShortDescription": "Get a sprint in Jira", - "input": { - "description": "Get an sprint in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "sprint-id" - ], - "properties": { - "sprint-id": { - "title": "Sprint ID", - "description": "The ID of the sprint. The sprint will only be returned if you can view the board that the sprint was created on, or view at least one of the issues in the sprint.", - "instillShortDescription": "The ID of the sprint", - "instillUIOrder": 0, - "instillFormat": "integer", - "instillAcceptFormats": [ - "integer" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "integer" - } - }, - "required": [ - "sprint-id" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Get an sprint in Jira", - "instillUIOrder": 0, - "$ref": "#/$defs/sprint", - "required": [], - "title": "Output", - "type": "object" - } - }, - "TASK_CREATE_ISSUE": { - "description": "Create an issue in Jira", - "instillShortDescription": "Create an issue in Jira", - "input": { - "description": "Create an issue in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "project-key", - "issue-type", - "status", - "summary", - "description" - ], - "properties": { - "project-key": { - "title": "Project Key", - "description": "The key of the project, e.g. `INS`", - "instillShortDescription": "The key of the project", - "instillUIOrder": 0, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "issue-type": { - "title": "Issue Type", - "description": "The type of the issue, can be: `Task`, `Epic`, `Subtask`, etc.", - "instillUIOrder": 1, - "additionalProperties": true, - "instillFormat": "object", - "type": "object", - "required": [ - "issue-type" - ], - "oneOf": [ - { - "properties": { - "issue-type": { - "const": "Epic", - "type": "string" - } - }, - "instillEditOnNodeFields": [ - "issue-type" - ], - "required": [ - "issue-type" - ], - "instillFormat": "object", - "type": "object" - }, - { - "properties": { - "issue-type": { - "const": "Task", - "type": "string" - } - }, - "instillEditOnNodeFields": [ - "issue-type" - ], - "required": [ - "issue-type" - ], - "instillFormat": "object", - "type": "object" - }, - { - "properties": { - "issue-type": { - "const": "Subtask", - "type": "string" - }, - "parent-key": { - "title": "Parent Key", - "description": "The key of the parent issue, e.g. `JRA-1330`", - "instillShortDescription": "The key of the parent issue", - "instillUIOrder": 10, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - } - }, - "instillEditOnNodeFields": [ - "issue-type", - "parent-key" - ], - "required": [ - "issue-type", - "parent-key" - ], - "instillFormat": "object", - "type": "object" - }, - { - "properties": { - "issue-type": { - "const": "Other", - "type": "string" - }, - "custom-issue-type": { - "title": "Issue Type", - "description": "The type of the issue, can be: `Task`, `Epic`, `Subtask`, etc.", - "instillShortDescription": "The type of the issue", - "instillUIOrder": 1, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - } - }, - "instillEditOnNodeFields": [ - "issue-type", - "custom-issue-type" - ], - "required": [ - "issue-type", - "custom-issue-type" - ], - "instillFormat": "object", - "type": "object" - } - ] - }, - "status": { - "title": "Status", - "description": "The status of the issue, can be: `To Do`, `In Progress`, `Done`", - "instillShortDescription": "The status of the issue", - "instillUIOrder": 4, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "summary": { - "title": "Summary", - "description": "The summary of the issue", - "instillShortDescription": "The summary of the issue", - "instillUIOrder": 2, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "description": { - "title": "Description", - "description": "The description of the issue", - "instillShortDescription": "The description of the issue", - "instillUIOrder": 3, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "assignee": { - "title": "Assignee", - "description": "The assignee of the issue", - "instillShortDescription": "The assignee of the issue", - "instillUIOrder": 5, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - } - }, - "required": [ - "project-key", - "issue-type", - "status" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Create an issue in Jira", - "instillUIOrder": 0, - "$ref": "#/$defs/issue", - "title": "Output", - "type": "object" - } - }, - "TASK_UPDATE_ISSUE": { - "description": "Update an issue in Jira", - "instillShortDescription": "Update an issue in Jira", - "input": { - "description": "Update an issue in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "issue-key", - "update" - ], - "properties": { - "issue-key": { - "$ref": "#/$defs/issue/properties/key", - "title": "Issue Key", - "instillUIOrder": 0 - }, - "update": { - "title": "Update", - "description": "Update an issue in Jira", - "instillUIOrder": 1, - "additionalProperties": true, - "instillFormat": "object", - "type": "object", - "oneOf": [ - { - "properties": { - "update": { - "const": "Custom Update", - "type": "string" - }, - "update-fields": { - "title": "Update Fields", - "description": "The fields to update", - "instillFormat": "array:structured/update-field", - "instillAcceptFormats": [ - "array:structured/update-field" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "array", - "items": { - "instillFormat": "structured/update-field", - "properties": { - "action": { - "description": "The action to perform, can be: `add`, `copy`, `edit`, `remove`, `set`", - "enum": [ - "add", - "copy", - "edit", - "remove", - "set" - ], - "instillUIOrder": 3, - "instillFormat": "string", - "type": "string", - "title": "Action" - }, - "field-name": { - "description": "The name of the field to update", - "instillUIOrder": 4, - "instillFormat": "string", - "type": "string", - "title": "Field Name" - }, - "value": { - "description": "The value of the field to update", - "instillUIOrder": 5, - "instillFormat": "string", - "type": "string", - "title": "Value" - } - }, - "required": [ - "action", - "field-name" - ], - "type": "object" - } - } - } - }, - { - "properties": { - "update": { - "const": "Move Issue to Epic", - "type": "string" - }, - "epic-key": { - "title": "Epic Key", - "description": "The key of the epic, e.g. `JRA-1330`", - "instillShortDescription": "The key of the epic", - "instillUIOrder": 11, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - } - } - } - ] - } - }, - "required": [ - "issue-key" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Create an issue in Jira", - "instillUIOrder": 0, - "$ref": "#/$defs/issue", - "title": "Output", - "type": "object" - } - }, - "TASK_CREATE_SPRINT": { - "description": "Create a sprint in Jira", - "instillShortDescription": "Create a sprint in Jira", - "input": { - "description": "Create a sprint in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "board-id", - "name", - "goal", - "start-date", - "end-date" - ], - "properties": { - "board-name": { - "title": "Board Name", - "description": "The name of the board", - "instillShortDescription": "The name of the board", - "instillUIOrder": 0, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the sprint", - "instillShortDescription": "The name of the sprint", - "instillUIOrder": 1, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "goal": { - "title": "Goal", - "description": "The goal of the sprint", - "instillShortDescription": "The goal of the sprint", - "instillUIOrder": 2, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "start-date": { - "title": "Start Date", - "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", - "instillShortDescription": "The start date of the sprint", - "instillUIOrder": 3, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "end-date": { - "title": "End Date", - "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", - "instillShortDescription": "The end date of the sprint", - "instillUIOrder": 4, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - } - }, - "required": [ - "board-id", - "name", - "goal", - "start-date", - "end-date" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Create a sprint in Jira", - "instillUIOrder": 0, - "$ref": "#/$defs/sprint", - "title": "Output", - "type": "object" - } - }, - "TASK_UPDATE_SPRINT": { - "description": "Update a sprint in Jira", - "instillShortDescription": "Update a sprint in Jira", - "input": { - "description": "Update a sprint in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "sprint-key", - "name", - "goal", - "start-date", - "end-date", - "complete-date" - ], - "properties": { - "sprint-key": { - "title": "Sprint Key", - "description": "The key of the sprint", - "instillShortDescription": "The key of the sprint", - "instillUIOrder": 0, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the sprint", - "instillShortDescription": "The name of the sprint", - "instillUIOrder": 1, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "goal": { - "title": "Goal", - "description": "The goal of the sprint", - "instillShortDescription": "The goal of the sprint", - "instillUIOrder": 2, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "start-date": { - "title": "Start Date", - "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", - "instillShortDescription": "The start date of the sprint", - "instillUIOrder": 3, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "end-date": { - "title": "End Date", - "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", - "instillShortDescription": "The end date of the sprint", - "instillUIOrder": 4, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "complete-date": { - "title": "Complete Date", - "description": "The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", - "instillShortDescription": "The complete date of the sprint", - "instillUIOrder": 5, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - } - }, - "required": [ - "sprint-key" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Create a sprint in Jira", - "instillUIOrder": 0, - "$ref": "#/$defs/sprint", - "title": "Output", - "type": "object" - } - }, - "TASK_RANK_EPIC": { - "description": "Rank an epic in Jira", - "instillShortDescription": "Rank an epic in Jira", - "input": { - "description": "Rank an epic in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "epic-key", - "rank" - ], - "properties": { - "epic-key": { - "title": "Epic Key", - "description": "The key of the epic, e.g. `JRA-1330`", - "instillShortDescription": "The key of the epic", - "instillUIOrder": 0, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "after": { - "title": "Rank After Epic", - "description": "The key of the epic to rank after", - "instillShortDescription": "The key of the epic to rank after", - "instillUIOrder": 1, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "before": { - "title": "Rank Before Epic", - "description": "The key of the epic to rank before", - "instillShortDescription": "The key of the epic to rank before", - "instillUIOrder": 2, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "rank": { - "title": "Rank", - "description": "The custom rank field ID of the epic", - "instillShortDescription": "The custom rank field ID of the epic", - "instillUIOrder": 3, - "instillFormat": "integer", - "instillAcceptFormats": [ - "integer" - ], - "instillUpstreamTypes": [ - "value", - "reference" - ], - "type": "integer" - } - }, - "required": [ - "epic-key", - "rank" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Rank an epic in Jira", - "instillUIOrder": 0, - "properties": { - "success": { - "description": "Whether the epic is ranked", - "instillUIOrder": 0, - "title": "Success", - "instillFormat": "boolean", - "type": "boolean" - } - }, - "title": "Output", - "type": "object" - } - } -} From b0e459b84bccd420025ccf36a234f3bce2e625a0 Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Wed, 31 Jul 2024 11:08:26 +0100 Subject: [PATCH 05/18] fix: patch missing fields --- application/jira/v0/config/tasks.json | 30 +++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/application/jira/v0/config/tasks.json b/application/jira/v0/config/tasks.json index 147fbc4a..5ba5faf7 100644 --- a/application/jira/v0/config/tasks.json +++ b/application/jira/v0/config/tasks.json @@ -1061,6 +1061,9 @@ "additionalProperties": true, "instillFormat": "object", "type": "object", + "required": [ + "update" + ], "oneOf": [ { "properties": { @@ -1120,7 +1123,17 @@ "type": "object" } } - } + }, + "required": [ + "update", + "update-fields" + ], + "instillEditOnNodeFields": [ + "update", + "update-fields" + ], + "instillFormat": "object", + "type": "object" }, { "properties": { @@ -1144,7 +1157,17 @@ ], "type": "string" } - } + }, + "required": [ + "update", + "epic-key" + ], + "instillEditOnNodeFields": [ + "update", + "epic-key" + ], + "instillFormat": "object", + "type": "object" } ] } @@ -1496,6 +1519,9 @@ "type": "boolean" } }, + "required": [ + "success" + ], "title": "Output", "type": "object" } From 73beae643e4d90d72824bb2b4cf2d51e1847b015 Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Wed, 7 Aug 2024 16:01:02 +0100 Subject: [PATCH 06/18] feat: add create issue mock --- application/jira/v0/component_test.go | 54 ++++++++++ application/jira/v0/issues.go | 99 ++++++++++++++++++ application/jira/v0/main.go | 4 + application/jira/v0/mock_create_task.go | 127 ++++++++++++++++++++++++ application/jira/v0/mock_server.go | 2 + application/jira/v0/sprint.go | 1 - go.mod | 3 + go.sum | 8 ++ 8 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 application/jira/v0/mock_create_task.go diff --git a/application/jira/v0/component_test.go b/application/jira/v0/component_test.go index a592c300..25b0f25a 100644 --- a/application/jira/v0/component_test.go +++ b/application/jira/v0/component_test.go @@ -494,6 +494,60 @@ func TestComponent_ListSprintsTask(t *testing.T) { taskTesting(testcases, taskListSprints, t) } +func TestComponent_CreateIssueTask(t *testing.T) { + + testcases := []TaskCase[CreateIssueInput, CreateIssueOutput]{ + { + _type: "ok", + name: "create issue", + input: CreateIssueInput{ + ProjectKey: "CRI", + IssueType: "Task", + Status: "To Do", + Summary: "Test issue 1", + Description: "Test description 1", + Assignee: "testuser", + }, + wantResp: CreateIssueOutput{ + Issue{ + ID: "30000", + Key: "CRI-1", + Fields: map[string]interface{}{ + "summary": "Test issue 1", + "description": "Test description 1", + "status": map[string]interface{}{ + "name": "To Do", + }, + "issuetype": map[string]interface{}{ + "name": "Task", + }, + "assignee": map[string]interface{}{ + "name": "testuser", + }, + "project": map[string]interface{}{ + "key": "CRI", + }, + }, + Self: "https://test.atlassian.net/rest/agile/1.0/issue/30000", + Summary: "Test issue 1", + Status: "To Do", + Description: "Test description 1", + IssueType: "Task", + }, + }, + }, + { + _type: "nok", + name: "400 - Bad Request", + input: CreateIssueInput{ + ProjectKey: "INVALID", + }, + wantErr: "unsuccessful HTTP response.*", + }, + } + taskTesting(testcases, taskCreateIssue, t) +} + func TestAuth_nok(t *testing.T) { c := qt.New(t) bc := base.Component{Logger: zap.NewNop()} diff --git a/application/jira/v0/issues.go b/application/jira/v0/issues.go index 73c4a328..6dd50a3c 100644 --- a/application/jira/v0/issues.go +++ b/application/jira/v0/issues.go @@ -8,6 +8,7 @@ import ( "github.com/go-resty/resty/v2" "github.com/instill-ai/component/base" + "github.com/instill-ai/component/tools/logger" "github.com/instill-ai/x/errmsg" "google.golang.org/protobuf/types/known/structpb" ) @@ -62,6 +63,7 @@ func extractIssue(issue *Issue) *Issue { } func (jiraClient *Client) getIssueTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { + var opt GetIssueInput if err := base.ConvertFromStructpb(props, &opt); err != nil { return nil, err @@ -128,6 +130,7 @@ type ListIssuesOutput struct { } func (jiraClient *Client) listIssuesTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { + var ( opt ListIssuesInput jql string @@ -279,3 +282,99 @@ func (jiraClient *Client) nextGenIssuesSearch(_ context.Context, opt nextGenSear } return resp, nil } + +type CreateIssueInput struct { + UpdateHistory bool `json:"updateHistory"` + ProjectKey string `json:"projectKey"` + IssueType string `json:"issueType"` + Status string `json:"status"` + Summary string `json:"summary"` + Description string `json:"description"` + Assignee string `json:"assignee"` +} +type CreateIssueRequset struct { + Fields map[string]interface{} `json:"fields"` + Update map[string][]AdditionalFields `json:"update"` +} +type CreateIssueResp struct { + ID string `json:"id"` + Key string `json:"key"` + Self string `json:"self"` + Transition struct { + Status string `json:"status"` + ErrorCollection struct { + ErrorMessages []string `json:"errorMessages"` + Errors map[string]interface{} `json:"errors"` + } `json:"errorCollection"` + } `json:"transition"` +} + +type CreateIssueOutput struct { + Issue +} + +func convertCreateIssueRequest(issue *CreateIssueInput) *CreateIssueRequset { + return &CreateIssueRequset{ + Fields: map[string]interface{}{ + "project": map[string]interface{}{ + "key": issue.ProjectKey, + }, + "issuetype": map[string]interface{}{ + "name": issue.IssueType, + }, + "status": map[string]interface{}{ + "name": issue.Status, + }, + "summary": issue.Summary, + "description": issue.Description, + "assignee": map[string]interface{}{ + "name": issue.Assignee, + }, + }, + } +} + +func (jiraClient *Client) createIssueTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { + var debug logger.Session + defer debug.SessionStart("CreateIssueTask", logger.Develop).SessionEnd() + + var issue CreateIssueInput + if err := base.ConvertFromStructpb(props, &issue); err != nil { + return nil, err + } + + apiEndpoint := "rest/api/2/issue" + req := jiraClient.Client.R().SetResult(&CreateIssueResp{}).SetBody(convertCreateIssueRequest(&issue)) + err := addQueryOptions(req, map[string]interface{}{"updateHistory": issue.UpdateHistory}) + if err != nil { + return nil, err + } + resp, err := req.Post(apiEndpoint) + if err != nil { + return nil, err + } + + createdResult, ok := resp.Result().(*CreateIssueResp) + + if !ok { + return nil, errmsg.AddMessage( + fmt.Errorf("failed to convert response to `Create Issue` Output"), + fmt.Sprintf("failed to convert %v to `Create Issue` Output", resp.Result()), + ) + } + debug.Info("Created Issue: ", createdResult) + + getIssueInput, err := base.ConvertToStructpb(GetIssueInput{IssueKey: createdResult.Key, UpdateHistory: issue.UpdateHistory}) + if err != nil { + return nil, err + } + getIssueOutput, err := jiraClient.getIssueTask(ctx, getIssueInput) + if err != nil { + return nil, err + } + var issueOutput CreateIssueOutput + if err := base.ConvertFromStructpb(getIssueOutput, &issueOutput); err != nil { + return nil, err + } + return base.ConvertToStructpb(issueOutput) +} diff --git a/application/jira/v0/main.go b/application/jira/v0/main.go index 71ae1fd2..cad46a1a 100644 --- a/application/jira/v0/main.go +++ b/application/jira/v0/main.go @@ -11,6 +11,7 @@ import ( "google.golang.org/protobuf/types/known/structpb" "github.com/instill-ai/component/base" + "github.com/instill-ai/component/tools/logger" "github.com/instill-ai/x/errmsg" ) @@ -21,6 +22,7 @@ const ( taskListSprints = "TASK_LIST_SPRINTS" taskGetIssue = "TASK_GET_ISSUE" taskGetSprint = "TASK_GET_SPRINT" + taskCreateIssue = "TASK_CREATE_ISSUE" ) var ( @@ -80,6 +82,8 @@ func (c *component) CreateExecution(x base.ComponentExecution) (base.IExecution, e.execute = e.client.getIssueTask case taskGetSprint: e.execute = e.client.getSprintTask + case taskCreateIssue: + e.execute = e.client.createIssueTask default: return nil, errmsg.AddMessage( fmt.Errorf("not supported task: %s", x.Task), diff --git a/application/jira/v0/mock_create_task.go b/application/jira/v0/mock_create_task.go new file mode 100644 index 00000000..987124d2 --- /dev/null +++ b/application/jira/v0/mock_create_task.go @@ -0,0 +1,127 @@ +package jira + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/instill-ai/component/tools/logger" +) + +type AdditionalFields struct { + Add map[string]interface{} `json:"add,omitempty"` + Copy map[string]interface{} `json:"copy,omitempty"` + Set map[string]interface{} `json:"set,omitempty"` + Edit map[string]interface{} `json:"edit,omitempty"` + Remove map[string]interface{} `json:"remove,omitempty"` +} +type mockCreateIssueRequest struct { + Fields map[string]interface{} `json:"fields"` + Update map[string][]AdditionalFields `json:"update"` +} + +type mockCreateIssueResponse struct { + ID string `json:"id"` + Key string `json:"key"` + Self string `json:"self"` + Transition struct { + Status string `json:"status"` + ErrorCollection struct { + ErrorMessages []string `json:"errorMessages"` + Errors map[string]interface{} `json:"errors"` + } `json:"errorCollection"` + } +} + +func mockCreateIssueTask(res http.ResponseWriter, req *http.Request) { + var debug logger.Session + defer debug.SessionStart("MockCreateIssueTask", logger.Develop).SessionEnd() + + var err error + if req.Method != http.MethodPost { + http.Error(res, "Method not allowed", http.StatusMethodNotAllowed) + return + } + body := mockCreateIssueRequest{} + err = json.NewDecoder(req.Body).Decode(&body) + if err != nil { + fmt.Println(err) + return + } + fields := body.Fields + update := body.Update + debug.Info("body: ", body) + debug.Info("Fields: ", fields) + debug.Info("Update: ", update) + + project, ok := fields["project"].(map[string]interface{})["key"].(string) + if !ok { + http.Error(res, "Invalid project", http.StatusBadRequest) + return + } + badResp := mockCreateIssueResponse{ + ID: "", + Key: "", + Self: "", + Transition: struct { + Status string `json:"status"` + ErrorCollection struct { + ErrorMessages []string `json:"errorMessages"` + Errors map[string]interface{} `json:"errors"` + } `json:"errorCollection"` + }{ + Status: "Failed", + ErrorCollection: struct { + ErrorMessages []string `json:"errorMessages"` + Errors map[string]interface{} `json:"errors"` + }{ + ErrorMessages: []string{"Invalid project"}, + Errors: map[string]interface{}{}, + }, + }, + } + if project == "INVALID" { + res.WriteHeader(http.StatusBadRequest) + err = json.NewEncoder(res).Encode(badResp) + if err != nil { + fmt.Println(err) + return + } + return + } + key := project + "-1" + ID := "30000" + successResp := mockCreateIssueResponse{ + ID: ID, + Key: key, + Self: "http://localhost:8080/rest/api/2/issue/10000", + Transition: struct { + Status string `json:"status"` + ErrorCollection struct { + ErrorMessages []string `json:"errorMessages"` + Errors map[string]interface{} `json:"errors"` + } `json:"errorCollection"` + }{ + Status: "Success", + ErrorCollection: struct { + ErrorMessages []string `json:"errorMessages"` + Errors map[string]interface{} `json:"errors"` + }{ + ErrorMessages: []string{}, + Errors: map[string]interface{}{}, + }, + }, + } + res.WriteHeader(http.StatusOK) + err = json.NewEncoder(res).Encode(successResp) + if err != nil { + fmt.Println(err) + return + } + + fakeIssues = append(fakeIssues, FakeIssue{ + ID: ID, + Key: key, + Fields: fields, + }) +} diff --git a/application/jira/v0/mock_server.go b/application/jira/v0/mock_server.go index c3eb09ce..38490220 100644 --- a/application/jira/v0/mock_server.go +++ b/application/jira/v0/mock_server.go @@ -34,6 +34,8 @@ func router(middlewares ...func(http.Handler) http.Handler) http.Handler { r.Get("/rest/api/2/search", mockIssuesSearch) r.Post("/rest/api/2/search", mockIssuesSearch) + + r.Post("/rest/api/2/issue", mockCreateIssueTask) return r } diff --git a/application/jira/v0/sprint.go b/application/jira/v0/sprint.go index 66724623..fcc28d77 100644 --- a/application/jira/v0/sprint.go +++ b/application/jira/v0/sprint.go @@ -97,7 +97,6 @@ type ListSprintsOutput struct { } func (jiraClient *Client) listSprintsTask(_ context.Context, props *structpb.Struct) (*structpb.Struct, error) { - var opt ListSprintInput if err := base.ConvertFromStructpb(props, &opt); err != nil { return nil, err diff --git a/go.mod b/go.mod index fc6d1bbd..c9cecba1 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/denisenkom/go-mssqldb v0.12.3 github.com/emersion/go-imap/v2 v2.0.0-beta.3 github.com/emersion/go-message v0.18.1 + github.com/fatih/color v1.16.0 github.com/fogleman/gg v1.3.0 github.com/frankban/quicktest v1.14.6 github.com/gabriel-vasile/mimetype v1.4.3 @@ -154,6 +155,8 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/levigross/exp-html v0.0.0-20120902181939-8df60c69a8f5 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index 7b2fb212..5c9b40bd 100644 --- a/go.sum +++ b/go.sum @@ -121,6 +121,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -374,6 +376,11 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= @@ -666,6 +673,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 012a3afd1b282964fc392455c6c731eb992f39e2 Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Wed, 7 Aug 2024 20:54:35 +0100 Subject: [PATCH 07/18] feat: add update issue mock --- application/jira/v0/component_test.go | 54 ++++++++++++ application/jira/v0/config/tasks.json | 15 ++++ application/jira/v0/issues.go | 86 ++++++++++++++++++- application/jira/v0/main.go | 3 + ...ck_create_task.go => mock_create_issue.go} | 7 -- application/jira/v0/mock_server.go | 1 + application/jira/v0/mock_update_issue.go | 80 +++++++++++++++++ 7 files changed, 236 insertions(+), 10 deletions(-) rename application/jira/v0/{mock_create_task.go => mock_create_issue.go} (90%) create mode 100644 application/jira/v0/mock_update_issue.go diff --git a/application/jira/v0/component_test.go b/application/jira/v0/component_test.go index 25b0f25a..7373a4bb 100644 --- a/application/jira/v0/component_test.go +++ b/application/jira/v0/component_test.go @@ -548,6 +548,60 @@ func TestComponent_CreateIssueTask(t *testing.T) { taskTesting(testcases, taskCreateIssue, t) } +func TestComponent_UpdateIssueTask(t *testing.T) { + testcases := []TaskCase[UpdateIssueInput, UpdateIssueOutput]{ + { + _type: "ok", + name: "update issue", + input: UpdateIssueInput{ + IssueKey: "TST-1", + Update: map[string][]AdditionalFields{ + "summary": { + { + Set: "Test issue 1 updated", + }, + }, + "description": { + { + Set: "Test description 1 updated", + }, + }, + }, + }, + wantResp: UpdateIssueOutput{ + Issue{ + ID: "1", + Key: "TST-1", + Fields: map[string]interface{}{ + "summary": "Test issue 1 updated", + "description": "Test description 1 updated", + "status": map[string]interface{}{ + "name": "To Do", + }, + "issuetype": map[string]interface{}{ + "name": "Task", + }, + }, + Self: "https://test.atlassian.net/rest/agile/1.0/issue/1", + Summary: "Test issue 1 updated", + Status: "To Do", + Description: "Test description 1 updated", + IssueType: "Task", + }, + }, + }, + { + _type: "nok", + name: "400 - Bad Request", + input: UpdateIssueInput{ + IssueKey: "INVALID", + }, + wantErr: "unsuccessful HTTP response.*", + }, + } + taskTesting(testcases, taskUpdateIssue, t) +} + func TestAuth_nok(t *testing.T) { c := qt.New(t) bc := base.Component{Logger: zap.NewNop()} diff --git a/application/jira/v0/config/tasks.json b/application/jira/v0/config/tasks.json index 5ba5faf7..e8351c58 100644 --- a/application/jira/v0/config/tasks.json +++ b/application/jira/v0/config/tasks.json @@ -1054,6 +1054,21 @@ "title": "Issue Key", "instillUIOrder": 0 }, + "notify-users": { + "title": "Notify Users", + "description": "Whether to notify users", + "instillUIOrder": 1, + "instillFormat": "boolean", + "instillAcceptFormats": [ + "boolean" + ], + "instillUpstreamTypes": [ + "value", + "reference", + "template" + ], + "type": "boolean" + }, "update": { "title": "Update", "description": "Update an issue in Jira", diff --git a/application/jira/v0/issues.go b/application/jira/v0/issues.go index 6dd50a3c..e320602e 100644 --- a/application/jira/v0/issues.go +++ b/application/jira/v0/issues.go @@ -283,10 +283,17 @@ func (jiraClient *Client) nextGenIssuesSearch(_ context.Context, opt nextGenSear return resp, nil } +type AdditionalFields struct { + Add string `json:"add,omitempty"` + Copy string `json:"copy,omitempty"` + Set string `json:"set,omitempty"` + Edit string `json:"edit,omitempty"` + Remove string `json:"remove,omitempty"` +} type CreateIssueInput struct { - UpdateHistory bool `json:"updateHistory"` - ProjectKey string `json:"projectKey"` - IssueType string `json:"issueType"` + UpdateHistory bool `json:"update-history"` + ProjectKey string `json:"project-key"` + IssueType string `json:"issue-type"` Status string `json:"status"` Summary string `json:"summary"` Description string `json:"description"` @@ -378,3 +385,76 @@ func (jiraClient *Client) createIssueTask(ctx context.Context, props *structpb.S } return base.ConvertToStructpb(issueOutput) } + +type UpdateIssueInput struct { + IssueKey string `json:"issue-key"` + Update map[string][]AdditionalFields `json:"update"` + NotifyUsers bool `json:"notify-users" api:"notifyUsers"` +} +type UpdateIssueRequset struct { + Body struct { + Update map[string][]AdditionalFields `json:"update"` + } + Query struct { + NotifyUsers bool `json:"notify-users" api:"notifyUsers"` + ReturnIssue bool `json:"return-issue" api:"returnIssue"` + } +} +type UpdateIssueResp struct { + Issue +} + +type UpdateIssueOutput struct { + Issue +} + +func (jiraClient *Client) updateIssueTask(_ context.Context, props *structpb.Struct) (*structpb.Struct, error) { + var debug logger.Session + defer debug.SessionStart("UpdateIssueTask", logger.Develop).SessionEnd() + + var input UpdateIssueInput + if err := base.ConvertFromStructpb(props, &input); err != nil { + return nil, err + } + + apiEndpoint := "rest/api/2/issue/" + input.IssueKey + request := UpdateIssueRequset{ + Body: struct { + Update map[string][]AdditionalFields `json:"update"` + }{ + Update: input.Update, + }, + Query: struct { + NotifyUsers bool `json:"notify-users" api:"notifyUsers"` + ReturnIssue bool `json:"return-issue" api:"returnIssue"` + }{ + NotifyUsers: input.NotifyUsers, + ReturnIssue: true, + }, + } + req := jiraClient.Client.R().SetResult(&UpdateIssueResp{}).SetBody(request.Body) + + err := addQueryOptions(req, request.Query) + if err != nil { + return nil, err + } + resp, err := req.Post(apiEndpoint) + if err != nil { + return nil, err + } + + updatedIssue, ok := resp.Result().(*UpdateIssueResp) + + if !ok { + return nil, errmsg.AddMessage( + fmt.Errorf("failed to convert response to `Update Issue` Output"), + fmt.Sprintf("failed to convert %v to `Update Issue` Output", resp.Result()), + ) + } + debug.Info("Updated Issue: ", updatedIssue) + out, err := base.ConvertToStructpb(UpdateIssueOutput{Issue: updatedIssue.Issue}) + if err != nil { + return nil, err + } + return out, nil +} diff --git a/application/jira/v0/main.go b/application/jira/v0/main.go index cad46a1a..09d90948 100644 --- a/application/jira/v0/main.go +++ b/application/jira/v0/main.go @@ -23,6 +23,7 @@ const ( taskGetIssue = "TASK_GET_ISSUE" taskGetSprint = "TASK_GET_SPRINT" taskCreateIssue = "TASK_CREATE_ISSUE" + taskUpdateIssue = "TASK_UPDATE_ISSUE" ) var ( @@ -84,6 +85,8 @@ func (c *component) CreateExecution(x base.ComponentExecution) (base.IExecution, e.execute = e.client.getSprintTask case taskCreateIssue: e.execute = e.client.createIssueTask + case taskUpdateIssue: + e.execute = e.client.updateIssueTask default: return nil, errmsg.AddMessage( fmt.Errorf("not supported task: %s", x.Task), diff --git a/application/jira/v0/mock_create_task.go b/application/jira/v0/mock_create_issue.go similarity index 90% rename from application/jira/v0/mock_create_task.go rename to application/jira/v0/mock_create_issue.go index 987124d2..b77ff1a5 100644 --- a/application/jira/v0/mock_create_task.go +++ b/application/jira/v0/mock_create_issue.go @@ -8,13 +8,6 @@ import ( "github.com/instill-ai/component/tools/logger" ) -type AdditionalFields struct { - Add map[string]interface{} `json:"add,omitempty"` - Copy map[string]interface{} `json:"copy,omitempty"` - Set map[string]interface{} `json:"set,omitempty"` - Edit map[string]interface{} `json:"edit,omitempty"` - Remove map[string]interface{} `json:"remove,omitempty"` -} type mockCreateIssueRequest struct { Fields map[string]interface{} `json:"fields"` Update map[string][]AdditionalFields `json:"update"` diff --git a/application/jira/v0/mock_server.go b/application/jira/v0/mock_server.go index 38490220..9538a302 100644 --- a/application/jira/v0/mock_server.go +++ b/application/jira/v0/mock_server.go @@ -35,6 +35,7 @@ func router(middlewares ...func(http.Handler) http.Handler) http.Handler { r.Get("/rest/api/2/search", mockIssuesSearch) r.Post("/rest/api/2/search", mockIssuesSearch) + r.Post("/rest/api/2/issue/{issue-key}", mockUpdateIssue) r.Post("/rest/api/2/issue", mockCreateIssueTask) return r } diff --git a/application/jira/v0/mock_update_issue.go b/application/jira/v0/mock_update_issue.go new file mode 100644 index 00000000..0885c754 --- /dev/null +++ b/application/jira/v0/mock_update_issue.go @@ -0,0 +1,80 @@ +package jira + +import ( + "encoding/json" + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/instill-ai/component/tools/logger" +) + +type MockUpdateIssueRequset struct { + IssueKey string `json:"issue-key"` + Update map[string][]AdditionalFields `json:"update"` + NotifyUsers bool `json:"notify-users" api:"notifyUsers"` + ReturnIssue bool `json:"return-issue" api:"returnIssue"` +} +type MockUpdateIssueResp struct { + Issue + NotifyUsers bool `json:"notify-users" api:"notifyUsers"` + ReturnIssue bool `json:"return-issue" api:"returnIssue"` +} + +// UpdateIssue updates an issue in Jira. +func mockUpdateIssue(res http.ResponseWriter, req *http.Request) { + var debug logger.Session + defer debug.SessionStart("UpdateIssue", logger.Develop).SessionEnd() + + var request MockUpdateIssueRequset + err := json.NewDecoder(req.Body).Decode(&request) + if err != nil { + http.Error(res, err.Error(), http.StatusBadRequest) + return + } + issueKey := chi.URLParam(req, "issue-key") + if issueKey == "" { + http.Error(res, "issue key is required", http.StatusBadRequest) + return + } + var issue *FakeIssue + for _, i := range fakeIssues { + if i.ID == issueKey || i.Key == issueKey { + issue = &i + issue.getSelf() + break + } + } + if issue == nil { + http.Error(res, "issue not found", http.StatusNotFound) + return + } + opt := req.URL.Query() + notifyUsers := opt.Get("notifyUsers") + returnIssue := opt.Get("returnIssue") + for key, fields := range request.Update { + for _, field := range fields { + if field.Set != "" { + issue.Fields[key] = field.Set + } + } + } + resp := MockUpdateIssueResp{ + Issue: Issue{ + ID: issue.ID, + Key: issue.Key, + Self: issue.Self, + Fields: issue.Fields, + Description: issue.Fields["description"].(string), + IssueType: issue.Fields["issuetype"].(map[string]interface{})["name"].(string), + Summary: issue.Fields["summary"].(string), + Status: issue.Fields["status"].(map[string]interface{})["name"].(string), + }, + NotifyUsers: notifyUsers == "true", + ReturnIssue: returnIssue == "true", + } + err = json.NewEncoder(res).Encode(resp) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } +} From 3348a57112e5cce00356dfb202a30ab1f6bb886b Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Wed, 7 Aug 2024 22:56:50 +0100 Subject: [PATCH 08/18] feat: add create sprint mock --- application/jira/v0/component_test.go | 37 ++++++++ application/jira/v0/main.go | 20 ++-- application/jira/v0/mock_create_issue.go | 2 +- application/jira/v0/mock_create_sprint.go | 54 +++++++++++ application/jira/v0/mock_server.go | 4 +- application/jira/v0/sprint.go | 106 ++++++++++++++++++++++ 6 files changed, 213 insertions(+), 10 deletions(-) create mode 100644 application/jira/v0/mock_create_sprint.go diff --git a/application/jira/v0/component_test.go b/application/jira/v0/component_test.go index 7373a4bb..d918cd5d 100644 --- a/application/jira/v0/component_test.go +++ b/application/jira/v0/component_test.go @@ -602,6 +602,43 @@ func TestComponent_UpdateIssueTask(t *testing.T) { taskTesting(testcases, taskUpdateIssue, t) } +func TestComponent_CreateSprintTask(t *testing.T) { + testcases := []TaskCase[CreateSprintInput, CreateSprintOutput]{ + { + _type: "ok", + name: "create sprint", + input: CreateSprintInput{ + Name: "Test Sprint", + Goal: "Sprint goal", + StartDate: "2021-01-01T00:00:00.000Z", + EndDate: "2021-01-15T00:00:00.000Z", + BoardName: "TST", + }, + wantResp: CreateSprintOutput{ + ID: 1, + Self: "https://test.atlassian.net/rest/agile/1.0/sprint/1", + State: "active", + Name: "Test Sprint", + StartDate: "2021-01-01T00:00:00.000Z", + EndDate: "2021-01-15T00:00:00.000Z", + CompleteDate: "", + OriginBoardID: 3, + Goal: "Sprint goal", + }, + }, + { + _type: "nok", + name: "400 - Bad Request", + input: CreateSprintInput{ + Name: "Test Sprint", + BoardName: "INVALID", + }, + wantErr: "board not found", + }, + } + taskTesting(testcases, taskCreateSprint, t) +} + func TestAuth_nok(t *testing.T) { c := qt.New(t) bc := base.Component{Logger: zap.NewNop()} diff --git a/application/jira/v0/main.go b/application/jira/v0/main.go index 09d90948..a4394895 100644 --- a/application/jira/v0/main.go +++ b/application/jira/v0/main.go @@ -16,14 +16,16 @@ import ( ) const ( - apiBaseURL = "https://api.atlassian.com" - taskListBoards = "TASK_LIST_BOARDS" - taskListIssues = "TASK_LIST_ISSUES" - taskListSprints = "TASK_LIST_SPRINTS" - taskGetIssue = "TASK_GET_ISSUE" - taskGetSprint = "TASK_GET_SPRINT" - taskCreateIssue = "TASK_CREATE_ISSUE" - taskUpdateIssue = "TASK_UPDATE_ISSUE" + apiBaseURL = "https://api.atlassian.com" + taskListBoards = "TASK_LIST_BOARDS" + taskListIssues = "TASK_LIST_ISSUES" + taskListSprints = "TASK_LIST_SPRINTS" + taskGetIssue = "TASK_GET_ISSUE" + taskGetSprint = "TASK_GET_SPRINT" + taskCreateIssue = "TASK_CREATE_ISSUE" + taskUpdateIssue = "TASK_UPDATE_ISSUE" + taskCreateSprint = "TASK_CREATE_SPRINT" + taskUpdateSprint = "TASK_UPDATE_SPRINT" ) var ( @@ -87,6 +89,8 @@ func (c *component) CreateExecution(x base.ComponentExecution) (base.IExecution, e.execute = e.client.createIssueTask case taskUpdateIssue: e.execute = e.client.updateIssueTask + case taskCreateSprint: + e.execute = e.client.createSprintTask default: return nil, errmsg.AddMessage( fmt.Errorf("not supported task: %s", x.Task), diff --git a/application/jira/v0/mock_create_issue.go b/application/jira/v0/mock_create_issue.go index b77ff1a5..e3549142 100644 --- a/application/jira/v0/mock_create_issue.go +++ b/application/jira/v0/mock_create_issue.go @@ -26,7 +26,7 @@ type mockCreateIssueResponse struct { } } -func mockCreateIssueTask(res http.ResponseWriter, req *http.Request) { +func mockCreateIssue(res http.ResponseWriter, req *http.Request) { var debug logger.Session defer debug.SessionStart("MockCreateIssueTask", logger.Develop).SessionEnd() diff --git a/application/jira/v0/mock_create_sprint.go b/application/jira/v0/mock_create_sprint.go new file mode 100644 index 00000000..bfe44bac --- /dev/null +++ b/application/jira/v0/mock_create_sprint.go @@ -0,0 +1,54 @@ +package jira + +import ( + "encoding/json" + "net/http" + + "github.com/instill-ai/component/tools/logger" +) + +type mockCreateSprintRequest struct { + Name string `json:"name"` + Goal string `json:"goal"` + StartDate string `json:"startDate"` + EndDate string `json:"endDate"` + OriginBoardID int `json:"originBoardId"` +} + +func mockCreateSprint(res http.ResponseWriter, req *http.Request) { + var debug logger.Session + defer debug.SessionStart("MockCreateIssueTask", logger.Develop).SessionEnd() + var err error + + debug.Info("MockCreateIssueTask called") + debug.Info(req.Method) + if req.Method != http.MethodPost { + http.Error(res, "Method not allowed", http.StatusMethodNotAllowed) + return + } + body := mockCreateSprintRequest{} + err = json.NewDecoder(req.Body).Decode(&body) + if err != nil { + http.Error(res, "Bad Request", http.StatusBadRequest) + return + } + var newSprint = FakeSprint{ + ID: 1, + Self: "https://test.atlassian.net/rest/agile/1.0/sprint/1", + State: "active", + Name: body.Name, + StartDate: body.StartDate, + EndDate: body.EndDate, + CompleteDate: "", + OriginBoardID: body.OriginBoardID, + Goal: body.Goal, + } + fakeSprints = append(fakeSprints, newSprint) + + res.WriteHeader(http.StatusCreated) + err = json.NewEncoder(res).Encode(newSprint) + if err != nil { + http.Error(res, "Bad Request", http.StatusBadRequest) + return + } +} diff --git a/application/jira/v0/mock_server.go b/application/jira/v0/mock_server.go index 9538a302..462df254 100644 --- a/application/jira/v0/mock_server.go +++ b/application/jira/v0/mock_server.go @@ -24,6 +24,8 @@ func router(middlewares ...func(http.Handler) http.Handler) http.Handler { }) r.Get("/rest/agile/1.0/issue/{issueIdOrKey:[a-zA-z0-9-]+}", mockGetIssue) r.Get("/rest/agile/1.0/sprint/{sprintId}", mockGetSprint) + r.Post("/rest/agile/1.0/sprint", mockCreateSprint) + r.Get("/rest/agile/1.0/board/{boardId}/issue", mockListIssues) // list all issues r.Get("/rest/agile/1.0/board/{boardId}/epic", mockListIssues) // list all epic r.Get("/rest/agile/1.0/board/{boardId}/sprint", mockListSprints) // list all sprint @@ -36,7 +38,7 @@ func router(middlewares ...func(http.Handler) http.Handler) http.Handler { r.Post("/rest/api/2/search", mockIssuesSearch) r.Post("/rest/api/2/issue/{issue-key}", mockUpdateIssue) - r.Post("/rest/api/2/issue", mockCreateIssueTask) + r.Post("/rest/api/2/issue", mockCreateIssue) return r } diff --git a/application/jira/v0/sprint.go b/application/jira/v0/sprint.go index fcc28d77..f6c5c6ac 100644 --- a/application/jira/v0/sprint.go +++ b/application/jira/v0/sprint.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/instill-ai/component/base" + "github.com/instill-ai/component/tools/logger" "github.com/instill-ai/x/errmsg" "google.golang.org/protobuf/types/known/structpb" ) @@ -134,3 +135,108 @@ func (jiraClient *Client) listSprintsTask(_ context.Context, props *structpb.Str out.Total = issues.Total return base.ConvertToStructpb(out) } + +type CreateSprintInput struct { + BoardName string `json:"board-name"` + Name string `json:"name"` + Goal string `json:"goal"` + StartDate string `json:"start-date"` + EndDate string `json:"end-date"` +} + +type CreateSprintRequest struct { + Name string `json:"name"` + Goal string `json:"goal"` + StartDate string `json:"startDate"` + EndDate string `json:"endDate"` + OriginBoardID int `json:"originBoardId"` +} + +type CreateSprintResp struct { + Sprint +} + +type CreateSprintOutput struct { + ID int `json:"id"` + Self string `json:"self"` + State string `json:"state"` + Name string `json:"name"` + StartDate string `json:"start-date"` + EndDate string `json:"end-date"` + CompleteDate string `json:"complete-date"` + OriginBoardID int `json:"origin-board-id"` + Goal string `json:"goal"` +} + +func (jiraClient *Client) createSprintTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { + var debug logger.Session + defer debug.SessionStart("MockCreateIssueTask", logger.Develop).SessionEnd() + + var opt CreateSprintInput + if err := base.ConvertFromStructpb(props, &opt); err != nil { + return nil, err + } + debug.Info("Create Sprint Task", opt) + apiBaseURL := "rest/agile/1.0/sprint" + + // TODO: Validate timestamp format RFC3339 + boardName := opt.BoardName + debug.Info("opt", opt) + debug.Info("boardName", boardName) + boards, err := jiraClient.listBoards(ctx, &ListBoardsInput{Name: boardName}) + if err != nil { + return nil, err + } + debug.Info("boards", boards) + + if len(boards.Values) == 0 { + return nil, errmsg.AddMessage( + fmt.Errorf("board not found"), + fmt.Sprintf("board with name %s not found", opt.BoardName), + ) + } else if len(boards.Values) > 1 { + return nil, errmsg.AddMessage( + fmt.Errorf("multiple boards found"), + fmt.Sprintf("multiple boards are found with the partial name \"%s\". Please provide a more specific name", opt.BoardName), + ) + } + board := boards.Values[0] + boardID := board.ID + debug.Info("boardID", boardID) + + req := jiraClient.Client.R().SetResult(&CreateSprintResp{}).SetBody(&CreateSprintRequest{ + Name: opt.Name, + Goal: opt.Goal, + StartDate: opt.StartDate, + EndDate: opt.EndDate, + OriginBoardID: boardID, + }) + + resp, err := req.Post(apiBaseURL) + if err != nil { + return nil, fmt.Errorf( + err.Error(), errmsg.Message(err), + ) + } + + sprint, ok := resp.Result().(*CreateSprintResp) + if !ok { + return nil, errmsg.AddMessage( + fmt.Errorf("failed to convert response to `Create Sprint` Output"), + fmt.Sprintf("failed to convert %v to `Create Sprint` Output", resp.Result()), + ) + } + + out := &CreateSprintOutput{ + ID: sprint.ID, + Self: sprint.Self, + State: sprint.State, + Name: sprint.Name, + StartDate: sprint.StartDate, + EndDate: sprint.EndDate, + CompleteDate: sprint.CompleteDate, + OriginBoardID: sprint.OriginBoardID, + Goal: sprint.Goal, + } + return base.ConvertToStructpb(out) +} From a2b240077b9f76defcd4697b6e9be61d3a164f14 Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Fri, 9 Aug 2024 11:43:29 +0100 Subject: [PATCH 09/18] feat: add update sprint and modify issue tasks --- application/jira/v0/component_test.go | 127 ++++++++++++-- application/jira/v0/config/tasks.json | 88 ++++------ application/jira/v0/issues.go | 102 ++++++++--- application/jira/v0/main.go | 198 +--------------------- application/jira/v0/mock_server.go | 3 +- application/jira/v0/mock_update_sprint.go | 107 ++++++++++++ application/jira/v0/sprint.go | 153 ++++++++++++++++- 7 files changed, 487 insertions(+), 291 deletions(-) create mode 100644 application/jira/v0/mock_update_sprint.go diff --git a/application/jira/v0/component_test.go b/application/jira/v0/component_test.go index d918cd5d..64328fef 100644 --- a/application/jira/v0/component_test.go +++ b/application/jira/v0/component_test.go @@ -495,18 +495,17 @@ func TestComponent_ListSprintsTask(t *testing.T) { } func TestComponent_CreateIssueTask(t *testing.T) { - testcases := []TaskCase[CreateIssueInput, CreateIssueOutput]{ { _type: "ok", name: "create issue", input: CreateIssueInput{ - ProjectKey: "CRI", - IssueType: "Task", - Status: "To Do", + ProjectKey: "CRI", + IssueType: IssueType{ + IssueType: "Task", + }, Summary: "Test issue 1", Description: "Test description 1", - Assignee: "testuser", }, wantResp: CreateIssueOutput{ Issue{ @@ -515,22 +514,15 @@ func TestComponent_CreateIssueTask(t *testing.T) { Fields: map[string]interface{}{ "summary": "Test issue 1", "description": "Test description 1", - "status": map[string]interface{}{ - "name": "To Do", - }, "issuetype": map[string]interface{}{ "name": "Task", }, - "assignee": map[string]interface{}{ - "name": "testuser", - }, "project": map[string]interface{}{ "key": "CRI", }, }, Self: "https://test.atlassian.net/rest/agile/1.0/issue/30000", Summary: "Test issue 1", - Status: "To Do", Description: "Test description 1", IssueType: "Task", }, @@ -555,15 +547,18 @@ func TestComponent_UpdateIssueTask(t *testing.T) { name: "update issue", input: UpdateIssueInput{ IssueKey: "TST-1", - Update: map[string][]AdditionalFields{ - "summary": { + Update: Update{ + UpdateType: "Custom Update", + UpdateFields: []UpdateField{ { - Set: "Test issue 1 updated", + FieldName: "summary", + Action: "set", + Value: "Test issue 1 updated", }, - }, - "description": { { - Set: "Test description 1 updated", + FieldName: "description", + Action: "set", + Value: "Test description 1 updated", }, }, }, @@ -633,12 +628,108 @@ func TestComponent_CreateSprintTask(t *testing.T) { Name: "Test Sprint", BoardName: "INVALID", }, + wantErr: "end date is required", + }, + { + _type: "nok", + name: "400 - Bad Request", + input: CreateSprintInput{ + Name: "Test Sprint", + BoardName: "INVALID", + EndDate: "2021-01-15T00:00:00.000Z", + }, wantErr: "board not found", }, } taskTesting(testcases, taskCreateSprint, t) } +func TestComponent_UpdateSprintTask(t *testing.T) { + testcases := []TaskCase[UpdateSprintInput, UpdateSprintOutput]{ + { + _type: "ok", + name: "update sprint", + input: UpdateSprintInput{ + SprintID: 1, + Name: "Test Sprint updated", + Goal: "Sprint goal updated", + StartDate: "2021-01-01T00:00:00.000Z", + EndDate: "2021-01-15T00:00:00.000Z", + CurrentState: "active", + EnterNextState: false, + }, + wantResp: UpdateSprintOutput{ + ID: 1, + Self: "https://test.atlassian.net/rest/agile/1.0/sprint/1", + State: "active", + Name: "Test Sprint updated", + StartDate: "2021-01-01T00:00:00.000Z", + EndDate: "2021-01-15T00:00:00.000Z", + CompleteDate: "2021-01-15T00:00:00.000Z", + OriginBoardID: 1, + Goal: "Sprint goal updated", + }, + }, + { + _type: "ok", + name: "future to active", + input: UpdateSprintInput{ + SprintID: 1, + Name: "Test Sprint updated", + Goal: "Sprint goal updated", + StartDate: "2021-01-01T00:00:00.000Z", + EndDate: "2021-01-15T00:00:00.000Z", + CurrentState: "future", + EnterNextState: true, + }, + wantResp: UpdateSprintOutput{ + ID: 1, + Self: "https://test.atlassian.net/rest/agile/1.0/sprint/1", + State: "active", + Name: "Test Sprint updated", + StartDate: "2021-01-01T00:00:00.000Z", + EndDate: "2021-01-15T00:00:00.000Z", + CompleteDate: "2021-01-15T00:00:00.000Z", + OriginBoardID: 1, + Goal: "Sprint goal updated", + }, + }, + { + _type: "ok", + name: "active to closed", + input: UpdateSprintInput{ + SprintID: 1, + Name: "Test Sprint updated", + Goal: "Sprint goal updated", + StartDate: "2021-01-01T00:00:00.000Z", + EndDate: "2021-01-15T00:00:00.000Z", + CurrentState: "active", + EnterNextState: true, + }, + wantResp: UpdateSprintOutput{ + ID: 1, + Self: "https://test.atlassian.net/rest/agile/1.0/sprint/1", + State: "closed", + Name: "Test Sprint updated", + StartDate: "2021-01-01T00:00:00.000Z", + EndDate: "2021-01-15T00:00:00.000Z", + CompleteDate: "2021-01-15T00:00:00.000Z", + OriginBoardID: 1, + Goal: "Sprint goal updated", + }, + }, + { + _type: "nok", + name: "400 - Bad Request", + input: UpdateSprintInput{ + SprintID: -1, + }, + wantErr: "end date is required", + }, + } + taskTesting(testcases, taskUpdateSprint, t) +} + func TestAuth_nok(t *testing.T) { c := qt.New(t) bc := base.Component{Logger: zap.NewNop()} diff --git a/application/jira/v0/config/tasks.json b/application/jira/v0/config/tasks.json index e8351c58..3c0849e4 100644 --- a/application/jira/v0/config/tasks.json +++ b/application/jira/v0/config/tasks.json @@ -823,7 +823,6 @@ "instillEditOnNodeFields": [ "project-key", "issue-type", - "status", "summary", "description" ], @@ -957,22 +956,6 @@ } ] }, - "status": { - "title": "Status", - "description": "The status of the issue, can be: `To Do`, `In Progress`, `Done`", - "instillShortDescription": "The status of the issue", - "instillUIOrder": 4, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, "summary": { "title": "Summary", "description": "The summary of the issue", @@ -1004,28 +987,11 @@ "template" ], "type": "string" - }, - "assignee": { - "title": "Assignee", - "description": "The assignee of the issue", - "instillShortDescription": "The assignee of the issue", - "instillUIOrder": 5, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" } }, "required": [ "project-key", - "issue-type", - "status" + "issue-type" ], "title": "Input", "type": "object" @@ -1089,6 +1055,7 @@ "update-fields": { "title": "Update Fields", "description": "The fields to update", + "instillUIMultiline": true, "instillFormat": "array:structured/update-field", "instillAcceptFormats": [ "array:structured/update-field" @@ -1208,7 +1175,7 @@ "description": "Create a sprint in Jira", "instillUIOrder": 0, "instillEditOnNodeFields": [ - "board-id", + "board-name", "name", "goal", "start-date", @@ -1297,7 +1264,7 @@ } }, "required": [ - "board-id", + "board-name", "name", "goal", "start-date", @@ -1321,29 +1288,30 @@ "description": "Update a sprint in Jira", "instillUIOrder": 0, "instillEditOnNodeFields": [ - "sprint-key", + "sprint-id", "name", "goal", "start-date", "end-date", - "complete-date" + "current-state", + "enter-next-state" ], "properties": { - "sprint-key": { - "title": "Sprint Key", - "description": "The key of the sprint", - "instillShortDescription": "The key of the sprint", + "sprint-id": { + "title": "Sprint ID", + "description": "The ID of the sprint", + "instillShortDescription": "The ID of the sprint", "instillUIOrder": 0, - "instillFormat": "string", + "instillFormat": "integer", "instillAcceptFormats": [ - "string" + "integer" ], "instillUpstreamTypes": [ "value", "reference", "template" ], - "type": "string" + "type": "integer" }, "name": { "title": "Name", @@ -1409,10 +1377,10 @@ ], "type": "string" }, - "complete-date": { - "title": "Complete Date", - "description": "The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", - "instillShortDescription": "The complete date of the sprint", + "current-state": { + "title": "Current State", + "description": "The current state of the sprint, can be: `future`, `active`, `closed`", + "instillShortDescription": "The current state of the sprint", "instillUIOrder": 5, "instillFormat": "string", "instillAcceptFormats": [ @@ -1424,10 +1392,28 @@ "template" ], "type": "string" + }, + "enter-next-state": { + "title": "Enter Next State", + "description": "Whether to enter the next state. If `true`, the sprint will enter the next state, i.e., `future` -> `active` -> `closed`", + "instillShortDescription": "Whether to enter the next state", + "instillUIOrder": 6, + "instillFormat": "boolean", + "instillAcceptFormats": [ + "boolean" + ], + "instillUpstreamTypes": [ + "value" + ], + "type": "boolean" } }, "required": [ - "sprint-key" + "sprint-id", + "name", + "start-date", + "end-date", + "current-state" ], "title": "Input", "type": "object" diff --git a/application/jira/v0/issues.go b/application/jira/v0/issues.go index e320602e..e77cb212 100644 --- a/application/jira/v0/issues.go +++ b/application/jira/v0/issues.go @@ -10,6 +10,7 @@ import ( "github.com/instill-ai/component/base" "github.com/instill-ai/component/tools/logger" "github.com/instill-ai/x/errmsg" + jsoniter "github.com/json-iterator/go" "google.golang.org/protobuf/types/known/structpb" ) @@ -290,14 +291,17 @@ type AdditionalFields struct { Edit string `json:"edit,omitempty"` Remove string `json:"remove,omitempty"` } +type IssueType struct { + IssueType string `json:"issue-type"` + ParentKey string `json:"parent-key"` + CustomIssueType string `json:"custom-issue-type"` +} type CreateIssueInput struct { - UpdateHistory bool `json:"update-history"` - ProjectKey string `json:"project-key"` - IssueType string `json:"issue-type"` - Status string `json:"status"` - Summary string `json:"summary"` - Description string `json:"description"` - Assignee string `json:"assignee"` + UpdateHistory bool `json:"update-history"` + ProjectKey string `json:"project-key"` + IssueType IssueType `json:"issue-type"` + Summary string `json:"summary"` + Description string `json:"description"` } type CreateIssueRequset struct { Fields map[string]interface{} `json:"fields"` @@ -321,30 +325,36 @@ type CreateIssueOutput struct { } func convertCreateIssueRequest(issue *CreateIssueInput) *CreateIssueRequset { - return &CreateIssueRequset{ + newRequest := &CreateIssueRequset{ Fields: map[string]interface{}{ "project": map[string]interface{}{ "key": issue.ProjectKey, }, "issuetype": map[string]interface{}{ - "name": issue.IssueType, - }, - "status": map[string]interface{}{ - "name": issue.Status, + "name": issue.IssueType.IssueType, }, "summary": issue.Summary, "description": issue.Description, - "assignee": map[string]interface{}{ - "name": issue.Assignee, - }, }, } + if issue.IssueType.ParentKey != "" { + newRequest.Fields["parent"] = map[string]interface{}{ + "key": issue.IssueType.ParentKey, + } + } + if issue.IssueType.CustomIssueType != "" { + newRequest.Fields["issuetype"] = map[string]interface{}{ + "name": issue.IssueType.CustomIssueType, + } + } + return newRequest } func (jiraClient *Client) createIssueTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { var debug logger.Session defer debug.SessionStart("CreateIssueTask", logger.Develop).SessionEnd() + debug.Info("props ", props) var issue CreateIssueInput if err := base.ConvertFromStructpb(props, &issue); err != nil { return nil, err @@ -386,10 +396,19 @@ func (jiraClient *Client) createIssueTask(ctx context.Context, props *structpb.S return base.ConvertToStructpb(issueOutput) } +type UpdateField struct { + Action string `json:"action"` + FieldName string `json:"field-name"` + Value string `json:"value"` +} +type Update struct { + UpdateType string `json:"update"` + UpdateFields []UpdateField `json:"update-fields"` +} type UpdateIssueInput struct { - IssueKey string `json:"issue-key"` - Update map[string][]AdditionalFields `json:"update"` - NotifyUsers bool `json:"notify-users" api:"notifyUsers"` + IssueKey string `json:"issue-key"` + Update Update `json:"update"` + NotifyUsers bool `json:"notify-users" api:"notifyUsers"` } type UpdateIssueRequset struct { Body struct { @@ -412,17 +431,48 @@ func (jiraClient *Client) updateIssueTask(_ context.Context, props *structpb.Str var debug logger.Session defer debug.SessionStart("UpdateIssueTask", logger.Develop).SessionEnd() + debug.Info("props ", props) var input UpdateIssueInput if err := base.ConvertFromStructpb(props, &input); err != nil { return nil, err } - + updateInfo := make(map[string][]AdditionalFields) + if input.Update.UpdateType == "Custom Update" { + for _, field := range input.Update.UpdateFields { + if field.FieldName == "" { + return nil, errmsg.AddMessage( + fmt.Errorf("field name is required"), + "field name is required", + ) + } + if updateInfo[field.FieldName] == nil { + updateInfo[field.FieldName] = []AdditionalFields{} + } + switch field.Action { + case "set": + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Set: field.Value}) + case "add": + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Add: field.Value}) + case "remove": + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Remove: field.Value}) + case "edit": + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Edit: field.Value}) + case "copy": + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Copy: field.Value}) + default: + return nil, errmsg.AddMessage( + fmt.Errorf("invalid action"), + fmt.Sprintf("%s is an invalid action", field.Action), + ) + } + } + } apiEndpoint := "rest/api/2/issue/" + input.IssueKey request := UpdateIssueRequset{ Body: struct { Update map[string][]AdditionalFields `json:"update"` }{ - Update: input.Update, + Update: updateInfo, }, Query: struct { NotifyUsers bool `json:"notify-users" api:"notifyUsers"` @@ -432,13 +482,19 @@ func (jiraClient *Client) updateIssueTask(_ context.Context, props *structpb.Str ReturnIssue: true, }, } - req := jiraClient.Client.R().SetResult(&UpdateIssueResp{}).SetBody(request.Body) - err := addQueryOptions(req, request.Query) + body, err := jsoniter.Marshal(request.Body) if err != nil { return nil, err } - resp, err := req.Post(apiEndpoint) + debug.Info("request ", string(body)) + req := jiraClient.Client.R().SetResult(&UpdateIssueResp{}).SetBody(string(body)) + + err = addQueryOptions(req, request.Query) + if err != nil { + return nil, err + } + resp, err := req.Put(apiEndpoint) if err != nil { return nil, err } diff --git a/application/jira/v0/main.go b/application/jira/v0/main.go index a4394895..ccd5920a 100644 --- a/application/jira/v0/main.go +++ b/application/jira/v0/main.go @@ -4,7 +4,6 @@ package jira import ( "context" _ "embed" - "encoding/json" "fmt" "sync" @@ -91,6 +90,8 @@ func (c *component) CreateExecution(x base.ComponentExecution) (base.IExecution, e.execute = e.client.updateIssueTask case taskCreateSprint: e.execute = e.client.createSprintTask + case taskUpdateSprint: + e.execute = e.client.updateSprintTask default: return nil, errmsg.AddMessage( fmt.Errorf("not supported task: %s", x.Task), @@ -101,199 +102,6 @@ func (c *component) CreateExecution(x base.ComponentExecution) (base.IExecution, return e, nil } -func (e *execution) getInputSchemaJSON(task string) (map[string]interface{}, error) { - taskSpec, ok := e.Component.GetTaskInputSchemas()[task] - if !ok { - return nil, errmsg.AddMessage( - fmt.Errorf("task %s not found", task), - fmt.Sprintf("Task %s not found", task), - ) - } - var taskSpecMap map[string]interface{} - err := json.Unmarshal([]byte(taskSpec), &taskSpecMap) - if err != nil { - return nil, errmsg.AddMessage( - err, - "Failed to unmarshal input", - ) - } - inputMap := taskSpecMap["properties"].(map[string]interface{}) - return inputMap, nil -} -func (e *execution) fillInDefaultValues(input *structpb.Struct) (*structpb.Struct, error) { - inputMap, err := e.getInputSchemaJSON(e.Task) - if err != nil { - return nil, err - } - return e.fillInDefaultValuesWithReference(input, inputMap) -} -func hasNextLevel(valueMap map[string]interface{}) bool { - if valType, ok := valueMap["type"]; ok { - if valType != "object" { - return false - } - } - if _, ok := valueMap["properties"]; ok { - return true - } - for _, target := range []string{"allOf", "anyOf", "oneOf"} { - if _, ok := valueMap[target]; ok { - items := valueMap[target].([]interface{}) - for _, v := range items { - if _, ok := v.(map[string]interface{})["properties"].(map[string]interface{}); ok { - return true - } - } - } - } - return false -} -func optionMatch(valueMap *structpb.Struct, reference map[string]interface{}, checkFields []string) bool { - for _, checkField := range checkFields { - if _, ok := valueMap.GetFields()[checkField]; !ok { - return false - } - if val, ok := reference[checkField].(map[string]interface{})["const"]; ok { - if valueMap.GetFields()[checkField].GetStringValue() != val { - return false - } - } - } - return true -} -func (e *execution) fillInDefaultValuesWithReference(input *structpb.Struct, reference map[string]interface{}) (*structpb.Struct, error) { - for key, value := range reference { - valueMap, ok := value.(map[string]interface{}) - if !ok { - continue - } - if _, ok := valueMap["default"]; !ok { - if !hasNextLevel(valueMap) { - continue - } - if _, ok := input.GetFields()[key]; !ok { - input.GetFields()[key] = &structpb.Value{ - Kind: &structpb.Value_StructValue{ - StructValue: &structpb.Struct{ - Fields: make(map[string]*structpb.Value), - }, - }, - } - } - var properties map[string]interface{} - if _, ok := valueMap["properties"]; !ok { - var requiredFieldsRaw []interface{} - if requiredFieldsRaw, ok = valueMap["required"].([]interface{}); !ok { - continue - } - requiredFields := make([]string, len(requiredFieldsRaw)) - for idx, v := range requiredFieldsRaw { - requiredFields[idx] = fmt.Sprintf("%v", v) - } - for _, target := range []string{"allOf", "anyOf", "oneOf"} { - var items []interface{} - if items, ok = valueMap[target].([]interface{}); !ok { - continue - } - for _, v := range items { - if properties, ok = v.(map[string]interface{})["properties"].(map[string]interface{}); !ok { - continue - } - inputSubField := input.GetFields()[key].GetStructValue() - if target == "oneOf" && !optionMatch(inputSubField, properties, requiredFields) { - continue - } - subField, err := e.fillInDefaultValuesWithReference(inputSubField, properties) - if err != nil { - return nil, err - } - input.GetFields()[key] = &structpb.Value{ - Kind: &structpb.Value_StructValue{ - StructValue: subField, - }, - } - } - } - } else { - if properties, ok = valueMap["properties"].(map[string]interface{}); !ok { - continue - } - subField, err := e.fillInDefaultValuesWithReference(input.GetFields()[key].GetStructValue(), properties) - if err != nil { - return nil, err - } - input.GetFields()[key] = &structpb.Value{ - Kind: &structpb.Value_StructValue{ - StructValue: subField, - }, - } - } - continue - } - if _, ok := input.GetFields()[key]; ok { - continue - } - defaultValue := valueMap["default"] - typeValue := valueMap["type"] - switch typeValue { - case "string": - input.GetFields()[key] = &structpb.Value{ - Kind: &structpb.Value_StringValue{ - StringValue: fmt.Sprintf("%v", defaultValue), - }, - } - case "integer", "number": - input.GetFields()[key] = &structpb.Value{ - Kind: &structpb.Value_NumberValue{ - NumberValue: defaultValue.(float64), - }, - } - case "boolean": - input.GetFields()[key] = &structpb.Value{ - Kind: &structpb.Value_BoolValue{ - BoolValue: defaultValue.(bool), - }, - } - case "array": - input.GetFields()[key] = &structpb.Value{ - Kind: &structpb.Value_ListValue{ - ListValue: &structpb.ListValue{ - Values: []*structpb.Value{}, - }, - }, - } - itemType := valueMap["items"].(map[string]interface{})["type"] - switch itemType { - case "string": - for _, v := range defaultValue.([]interface{}) { - input.GetFields()[key].GetListValue().Values = append(input.GetFields()[key].GetListValue().Values, &structpb.Value{ - Kind: &structpb.Value_StringValue{ - StringValue: fmt.Sprintf("%v", v), - }, - }) - } - case "integer", "number": - for _, v := range defaultValue.([]interface{}) { - input.GetFields()[key].GetListValue().Values = append(input.GetFields()[key].GetListValue().Values, &structpb.Value{ - Kind: &structpb.Value_NumberValue{ - NumberValue: v.(float64), - }, - }) - } - case "boolean": - for _, v := range defaultValue.([]interface{}) { - input.GetFields()[key].GetListValue().Values = append(input.GetFields()[key].GetListValue().Values, &structpb.Value{ - Kind: &structpb.Value_BoolValue{ - BoolValue: v.(bool), - }, - }) - } - } - } - } - return input, nil -} - func (e *execution) Execute(ctx context.Context, in base.InputReader, out base.OutputWriter) error { inputs, err := in.Read(ctx) if err != nil { @@ -302,7 +110,7 @@ func (e *execution) Execute(ctx context.Context, in base.InputReader, out base.O outputs := make([]*structpb.Struct, len(inputs)) for i, input := range inputs { - input, err := e.fillInDefaultValues(input) + input, err := e.ComponentExecution.FillInDefaultValues(input) if err != nil { return err } diff --git a/application/jira/v0/mock_server.go b/application/jira/v0/mock_server.go index 462df254..468c84b8 100644 --- a/application/jira/v0/mock_server.go +++ b/application/jira/v0/mock_server.go @@ -24,6 +24,7 @@ func router(middlewares ...func(http.Handler) http.Handler) http.Handler { }) r.Get("/rest/agile/1.0/issue/{issueIdOrKey:[a-zA-z0-9-]+}", mockGetIssue) r.Get("/rest/agile/1.0/sprint/{sprintId}", mockGetSprint) + r.Put("/rest/agile/1.0/sprint/{sprintId}", mockUpdateSprint) r.Post("/rest/agile/1.0/sprint", mockCreateSprint) r.Get("/rest/agile/1.0/board/{boardId}/issue", mockListIssues) // list all issues @@ -37,7 +38,7 @@ func router(middlewares ...func(http.Handler) http.Handler) http.Handler { r.Get("/rest/api/2/search", mockIssuesSearch) r.Post("/rest/api/2/search", mockIssuesSearch) - r.Post("/rest/api/2/issue/{issue-key}", mockUpdateIssue) + r.Put("/rest/api/2/issue/{issue-key}", mockUpdateIssue) r.Post("/rest/api/2/issue", mockCreateIssue) return r } diff --git a/application/jira/v0/mock_update_sprint.go b/application/jira/v0/mock_update_sprint.go new file mode 100644 index 00000000..4a244a34 --- /dev/null +++ b/application/jira/v0/mock_update_sprint.go @@ -0,0 +1,107 @@ +package jira + +import ( + "encoding/json" + "net/http" + "strconv" + + "github.com/go-chi/chi/v5" + "github.com/instill-ai/component/tools/logger" +) + +type MockUpdateSprintRequset struct { + Name string `json:"name,omitempty"` + State string `json:"state,omitempty"` + OriginBoardID int `json:"originBoardId,omitempty"` + Goal string `json:"goal,omitempty"` + StartDate string `json:"startDate,omitempty"` + EndDate string `json:"endDate,omitempty"` + CompleteDate string `json:"completeDate,omitempty"` +} +type MockUpdateSprintResp struct { + FakeSprint +} + +// UpdateSprint updates an issue in Jira. +func mockUpdateSprint(res http.ResponseWriter, req *http.Request) { + var debug logger.Session + defer debug.SessionStart("mockUpdateSprint", logger.Develop).SessionEnd() + + var request MockUpdateSprintRequset + + err := json.NewDecoder(req.Body).Decode(&request) + if err != nil { + http.Error(res, err.Error(), http.StatusBadRequest) + return + } + debug.Info("request", request) + sprintID, err := strconv.Atoi(chi.URLParam(req, "sprintId")) + if err != nil { + http.Error(res, "sprint id is required", http.StatusBadRequest) + return + } + + mockSprint := Sprint{ + ID: sprintID, + Name: request.Name, + State: request.State, + OriginBoardID: request.OriginBoardID, + Goal: request.Goal, + StartDate: request.StartDate, + EndDate: request.EndDate, + CompleteDate: request.CompleteDate, + } + var resp MockUpdateSprintResp + for i, s := range fakeSprints { + if s.ID == mockSprint.ID { + if mockSprint.Name != "" { + fakeSprints[i].Name = mockSprint.Name + } + if mockSprint.Goal != "" { + fakeSprints[i].Goal = mockSprint.Goal + } + if mockSprint.StartDate != "" { + fakeSprints[i].StartDate = mockSprint.StartDate + } + if mockSprint.EndDate != "" { + fakeSprints[i].EndDate = mockSprint.EndDate + } + if mockSprint.CompleteDate != "" { + fakeSprints[i].CompleteDate = mockSprint.CompleteDate + } + if mockSprint.State != "" { + fakeSprints[i].State = mockSprint.State + } + if mockSprint.OriginBoardID != 0 { + fakeSprints[i].OriginBoardID = mockSprint.OriginBoardID + } + debug.Info("target sprint", mockSprint) + debug.Info("updated sprint", fakeSprints[i]) + resp = MockUpdateSprintResp{ + FakeSprint: FakeSprint{ + ID: fakeSprints[i].ID, + Self: fakeSprints[i].Self, + Name: fakeSprints[i].Name, + State: fakeSprints[i].State, + OriginBoardID: fakeSprints[i].OriginBoardID, + Goal: fakeSprints[i].Goal, + StartDate: fakeSprints[i].StartDate, + EndDate: fakeSprints[i].EndDate, + CompleteDate: fakeSprints[i].CompleteDate, + }, + } + resp.FakeSprint.getSelf() + break + } + } + + if resp.ID == 0 { + http.Error(res, "sprint not found", http.StatusNotFound) + return + } + err = json.NewEncoder(res).Encode(resp) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } +} diff --git a/application/jira/v0/sprint.go b/application/jira/v0/sprint.go index f6c5c6ac..737e5d27 100644 --- a/application/jira/v0/sprint.go +++ b/application/jira/v0/sprint.go @@ -4,6 +4,7 @@ import ( "context" _ "embed" "fmt" + "time" "github.com/instill-ai/component/base" "github.com/instill-ai/component/tools/logger" @@ -97,7 +98,7 @@ type ListSprintsOutput struct { Total int `json:"total"` } -func (jiraClient *Client) listSprintsTask(_ context.Context, props *structpb.Struct) (*structpb.Struct, error) { +func (jiraClient *Client) listSprintsTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { var opt ListSprintInput if err := base.ConvertFromStructpb(props, &opt); err != nil { return nil, err @@ -170,7 +171,7 @@ type CreateSprintOutput struct { func (jiraClient *Client) createSprintTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { var debug logger.Session - defer debug.SessionStart("MockCreateIssueTask", logger.Develop).SessionEnd() + defer debug.SessionStart("CreateIssueTask", logger.Develop).SessionEnd() var opt CreateSprintInput if err := base.ConvertFromStructpb(props, &opt); err != nil { @@ -179,7 +180,30 @@ func (jiraClient *Client) createSprintTask(ctx context.Context, props *structpb. debug.Info("Create Sprint Task", opt) apiBaseURL := "rest/agile/1.0/sprint" - // TODO: Validate timestamp format RFC3339 + // Validate timestamp format RFC3339 + if _, err := time.Parse(time.RFC3339, opt.StartDate); err != nil { + if opt.StartDate == "" { + opt.StartDate = time.Now().Format(time.RFC3339) + } else { + return nil, errmsg.AddMessage( + err, + fmt.Sprintf("invalid start date format: %s", opt.StartDate), + ) + } + } + if _, err := time.Parse(time.RFC3339, opt.EndDate); err != nil { + if opt.EndDate == "" { + return nil, errmsg.AddMessage( + fmt.Errorf("end date is required"), + "end date is required", + ) + } else { + return nil, errmsg.AddMessage( + err, + fmt.Sprintf("invalid end date format: %s", opt.EndDate), + ) + } + } boardName := opt.BoardName debug.Info("opt", opt) debug.Info("boardName", boardName) @@ -240,3 +264,126 @@ func (jiraClient *Client) createSprintTask(ctx context.Context, props *structpb. } return base.ConvertToStructpb(out) } + +type UpdateSprintInput struct { + SprintID int `json:"sprint-id"` + Name string `json:"name"` + Goal string `json:"goal"` + StartDate string `json:"start-date"` + EndDate string `json:"end-date"` + CurrentState string `json:"current-state"` + EnterNextState bool `json:"enter-next-state"` +} + +type UpdateSprintRequest struct { + Name string `json:"name,omitempty"` + Goal string `json:"goal,omitempty"` + StartDate string `json:"startDate,omitempty"` + EndDate string `json:"endDate,omitempty"` + State string `json:"state,omitempty"` +} + +type UpdateSprintResp struct { + Sprint +} +type UpdateSprintOutput struct { + ID int `json:"id"` + Self string `json:"self"` + State string `json:"state"` + Name string `json:"name"` + StartDate string `json:"start-date"` + EndDate string `json:"end-date"` + CompleteDate string `json:"complete-date"` + OriginBoardID int `json:"origin-board-id"` + Goal string `json:"goal"` +} + +func (jiraClient *Client) updateSprintTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { + var debug logger.Session + defer debug.SessionStart("updateSprintTask", logger.Develop).SessionEnd() + var opt UpdateSprintInput + if err := base.ConvertFromStructpb(props, &opt); err != nil { + return nil, err + } + + apiEndpoint := fmt.Sprintf("rest/agile/1.0/sprint/%v", opt.SprintID) + debug.Info("Update Sprint Task", opt) + debug.Info("apiEndpoint", apiEndpoint) + + var body UpdateSprintRequest + structOpt, err := base.ConvertToStructpb(opt) + if err != nil { + return nil, err + } + if err := base.ConvertFromStructpb(structOpt, &body); err != nil { + return nil, err + } + body.StartDate = opt.StartDate + body.EndDate = opt.EndDate + if _, err := time.Parse(time.RFC3339, body.StartDate); err != nil { + debug.Info("body start date", body.StartDate) + debug.Info("opt start date", opt.StartDate) + if body.StartDate == "" { + body.StartDate = time.Now().Format(time.RFC3339) + } else { + return nil, errmsg.AddMessage( + err, + fmt.Sprintf("invalid start date format: %v", opt.StartDate), + ) + } + } + if _, err := time.Parse(time.RFC3339, body.EndDate); err != nil { + if body.EndDate == "" { + return nil, errmsg.AddMessage( + fmt.Errorf("end date is required"), + "end date is required", + ) + } else { + return nil, errmsg.AddMessage( + err, + fmt.Sprintf("invalid end date format: %s", opt.EndDate), + ) + } + } + if opt.EnterNextState { + switch opt.CurrentState { + case "future": + body.State = "active" + startTime, _ := time.Parse(time.RFC3339, body.StartDate) + if time.Now().Compare(startTime) == -1 { + body.StartDate = time.Now().Format(time.RFC3339) + } + case "active": + body.State = "closed" + case "closed": + body.State = "closed" + } + } else { + body.State = opt.CurrentState + } + jsonOpt, err := base.ConvertToStructpb(body) + if err != nil { + return nil, err + } + debug.Info("body", jsonOpt) + req := jiraClient.Client.R().SetResult(&Sprint{}).SetBody(jsonOpt) + debug.Info("body", req.Body) + + resp, err := req.Put(apiEndpoint) + // debug.Info("resp", resp) + + if err != nil { + debug.Error(err) + return nil, err + } + + updatedSprint, ok := resp.Result().(*Sprint) + if !ok { + return nil, errmsg.AddMessage( + fmt.Errorf("failed to convert response to `Update Sprint` Output"), + fmt.Sprintf("failed to convert %v to `Update Sprint` Output", resp.Result()), + ) + } + out := extractSprintOutput(updatedSprint) + return base.ConvertToStructpb(out) +} From c14bb002dd6ab26bdcbac3471b981f72b569a11e Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Sun, 11 Aug 2024 03:01:25 +0100 Subject: [PATCH 10/18] feat: add move to epic logic but api seems not working --- application/jira/v0/component_test.go | 4 + application/jira/v0/issues.go | 164 ++++++++++++++++++++------ 2 files changed, 129 insertions(+), 39 deletions(-) diff --git a/application/jira/v0/component_test.go b/application/jira/v0/component_test.go index 64328fef..93072e59 100644 --- a/application/jira/v0/component_test.go +++ b/application/jira/v0/component_test.go @@ -590,6 +590,10 @@ func TestComponent_UpdateIssueTask(t *testing.T) { name: "400 - Bad Request", input: UpdateIssueInput{ IssueKey: "INVALID", + Update: Update{ + UpdateType: "Custom Update", + UpdateFields: []UpdateField{}, + }, }, wantErr: "unsuccessful HTTP response.*", }, diff --git a/application/jira/v0/issues.go b/application/jira/v0/issues.go index e77cb212..0364e7a5 100644 --- a/application/jira/v0/issues.go +++ b/application/jira/v0/issues.go @@ -285,11 +285,11 @@ func (jiraClient *Client) nextGenIssuesSearch(_ context.Context, opt nextGenSear } type AdditionalFields struct { - Add string `json:"add,omitempty"` - Copy string `json:"copy,omitempty"` - Set string `json:"set,omitempty"` - Edit string `json:"edit,omitempty"` - Remove string `json:"remove,omitempty"` + Add any `json:"add,omitempty"` + Copy any `json:"copy,omitempty"` + Set any `json:"set,omitempty"` + Edit any `json:"edit,omitempty"` + Remove any `json:"remove,omitempty"` } type IssueType struct { IssueType string `json:"issue-type"` @@ -399,11 +399,13 @@ func (jiraClient *Client) createIssueTask(ctx context.Context, props *structpb.S type UpdateField struct { Action string `json:"action"` FieldName string `json:"field-name"` - Value string `json:"value"` + Value any `json:"value"` } + type Update struct { UpdateType string `json:"update"` UpdateFields []UpdateField `json:"update-fields"` + EpicKey string `json:"epic-key"` } type UpdateIssueInput struct { IssueKey string `json:"issue-key"` @@ -427,7 +429,8 @@ type UpdateIssueOutput struct { Issue } -func (jiraClient *Client) updateIssueTask(_ context.Context, props *structpb.Struct) (*structpb.Struct, error) { +func (jiraClient *Client) updateIssueTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { + var err error var debug logger.Session defer debug.SessionStart("UpdateIssueTask", logger.Develop).SessionEnd() @@ -436,36 +439,125 @@ func (jiraClient *Client) updateIssueTask(_ context.Context, props *structpb.Str if err := base.ConvertFromStructpb(props, &input); err != nil { return nil, err } - updateInfo := make(map[string][]AdditionalFields) + var ( + updatedIssue *UpdateIssueResp + out *structpb.Struct + ) if input.Update.UpdateType == "Custom Update" { - for _, field := range input.Update.UpdateFields { - if field.FieldName == "" { - return nil, errmsg.AddMessage( - fmt.Errorf("field name is required"), - "field name is required", - ) - } - if updateInfo[field.FieldName] == nil { - updateInfo[field.FieldName] = []AdditionalFields{} + updatedIssue, err = jiraClient.updateIssue(ctx, &input) + if err != nil { + return nil, err + } + out, err = base.ConvertToStructpb(UpdateIssueOutput{Issue: updatedIssue.Issue}) + } else if input.Update.UpdateType == "Move Issue to Epic" { + err = jiraClient.moveIssueToEpic(ctx, input.IssueKey, input.Update.EpicKey) + if err != nil { + debug.Error("Error: ", err) + if !strings.Contains(errmsg.Message(err), "The request contains a next-gen issue") { + return nil, err } - switch field.Action { - case "set": - updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Set: field.Value}) - case "add": - updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Add: field.Value}) - case "remove": - updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Remove: field.Value}) - case "edit": - updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Edit: field.Value}) - case "copy": - updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Copy: field.Value}) - default: + input.Update.UpdateType = "Custom Update" + // TODO: both api seems not working + input.Update.UpdateFields = append(input.Update.UpdateFields, UpdateField{ + Action: "set", + FieldName: "parent", + Value: map[string]string{ + "key": input.Update.EpicKey, + }, + }) + debug.Info("Updating issue with parent key") + debug.Info("Input: ", input.Update.UpdateFields) + if _, err = jiraClient.updateIssue(ctx, &input); err != nil { return nil, errmsg.AddMessage( - fmt.Errorf("invalid action"), - fmt.Sprintf("%s is an invalid action", field.Action), + fmt.Errorf("failed to update issue with parent key"), + "You can only move issues to epics.", ) } } + // get issue + getIssueInput, err := base.ConvertToStructpb(GetIssueInput{IssueKey: input.IssueKey}) + if err != nil { + return nil, err + } + getIssueOutput, err := jiraClient.getIssueTask(ctx, getIssueInput) + if err != nil { + return nil, err + } + var newIssue Issue + err = base.ConvertFromStructpb(getIssueOutput, &newIssue) + if err != nil { + return nil, err + } + out, err = base.ConvertToStructpb(UpdateIssueOutput{Issue: newIssue}) + if err != nil { + return nil, err + } + } else { + return nil, errmsg.AddMessage( + fmt.Errorf("invalid update type"), + fmt.Sprintf("%s is an invalid update type", input.Update.UpdateType), + ) + } + debug.Info("Updated Issue: ", out) + if err != nil { + return nil, err + } + return out, nil +} + +func (jiraClient *Client) moveIssueToEpic(_ context.Context, issueKey, epicKey string) error { + apiEndpoint := fmt.Sprintf("/rest/agile/1.0/epic/%s/issue", epicKey) + req := jiraClient.Client.R().SetBody(fmt.Sprintf(`{"issues":["%s"]}`, issueKey)) + resp, err := req.Post(apiEndpoint) + if err != nil { + return err + } + if resp.StatusCode() != 204 { + return errmsg.AddMessage( + fmt.Errorf("failed to move issue to epic"), + fmt.Sprintf(`failed to move issue "%s" to epic "%s"`, issueKey, epicKey), + ) + } + return nil +} + +func (jiraClient *Client) updateIssue(_ context.Context, input *UpdateIssueInput) (*UpdateIssueResp, error) { + var debug logger.Session + defer debug.SessionStart("UpdateIssue", logger.Develop).SessionEnd() + if input.Update.UpdateType != "Custom Update" { + return nil, errmsg.AddMessage( + fmt.Errorf("invalid update type"), + fmt.Sprintf("%s is an invalid update type", input.Update.UpdateType), + ) + } + updateInfo := make(map[string][]AdditionalFields) + for _, field := range input.Update.UpdateFields { + if field.FieldName == "" { + return nil, errmsg.AddMessage( + fmt.Errorf("field name is required"), + "field name is required", + ) + } + if updateInfo[field.FieldName] == nil { + updateInfo[field.FieldName] = []AdditionalFields{} + } + switch field.Action { + case "set": + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Set: field.Value}) + case "add": + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Add: field.Value}) + case "remove": + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Remove: field.Value}) + case "edit": + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Edit: field.Value}) + case "copy": + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Copy: field.Value}) + default: + return nil, errmsg.AddMessage( + fmt.Errorf("invalid action"), + fmt.Sprintf("%s is an invalid action", field.Action), + ) + } } apiEndpoint := "rest/api/2/issue/" + input.IssueKey request := UpdateIssueRequset{ @@ -487,9 +579,8 @@ func (jiraClient *Client) updateIssueTask(_ context.Context, props *structpb.Str if err != nil { return nil, err } - debug.Info("request ", string(body)) + debug.Info("Request Body: ", string(body)) req := jiraClient.Client.R().SetResult(&UpdateIssueResp{}).SetBody(string(body)) - err = addQueryOptions(req, request.Query) if err != nil { return nil, err @@ -507,10 +598,5 @@ func (jiraClient *Client) updateIssueTask(_ context.Context, props *structpb.Str fmt.Sprintf("failed to convert %v to `Update Issue` Output", resp.Result()), ) } - debug.Info("Updated Issue: ", updatedIssue) - out, err := base.ConvertToStructpb(UpdateIssueOutput{Issue: updatedIssue.Issue}) - if err != nil { - return nil, err - } - return out, nil + return updatedIssue, nil } From 7356ff512e04a618e788b438c809501bbc56b82f Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Mon, 12 Aug 2024 00:44:32 +0200 Subject: [PATCH 11/18] feat: add real move issue to epic --- application/jira/v0/issues.go | 19 ++++++++--- application/jira/v0/mock_update_issue.go | 41 +++++++++++++++++------- go.mod | 1 + 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/application/jira/v0/issues.go b/application/jira/v0/issues.go index 0364e7a5..89a069b7 100644 --- a/application/jira/v0/issues.go +++ b/application/jira/v0/issues.go @@ -4,6 +4,7 @@ import ( "context" _ "embed" "fmt" + "reflect" "strings" "github.com/go-resty/resty/v2" @@ -414,7 +415,8 @@ type UpdateIssueInput struct { } type UpdateIssueRequset struct { Body struct { - Update map[string][]AdditionalFields `json:"update"` + Update map[string][]AdditionalFields `json:"update,omitempty"` + Fields map[string]interface{} `json:"fields,omitempty"` } Query struct { NotifyUsers bool `json:"notify-users" api:"notifyUsers"` @@ -457,7 +459,6 @@ func (jiraClient *Client) updateIssueTask(ctx context.Context, props *structpb.S return nil, err } input.Update.UpdateType = "Custom Update" - // TODO: both api seems not working input.Update.UpdateFields = append(input.Update.UpdateFields, UpdateField{ Action: "set", FieldName: "parent", @@ -468,6 +469,7 @@ func (jiraClient *Client) updateIssueTask(ctx context.Context, props *structpb.S debug.Info("Updating issue with parent key") debug.Info("Input: ", input.Update.UpdateFields) if _, err = jiraClient.updateIssue(ctx, &input); err != nil { + debug.Error("updateIssue err: ", err) return nil, errmsg.AddMessage( fmt.Errorf("failed to update issue with parent key"), "You can only move issues to epics.", @@ -531,6 +533,7 @@ func (jiraClient *Client) updateIssue(_ context.Context, input *UpdateIssueInput ) } updateInfo := make(map[string][]AdditionalFields) + fieldsInfo := make(map[string]interface{}) for _, field := range input.Update.UpdateFields { if field.FieldName == "" { return nil, errmsg.AddMessage( @@ -543,7 +546,12 @@ func (jiraClient *Client) updateIssue(_ context.Context, input *UpdateIssueInput } switch field.Action { case "set": - updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Set: field.Value}) + if v := reflect.ValueOf(field.Value); v.Kind() != reflect.Slice || v.Len() <= 1 { + fieldsInfo[field.FieldName] = field.Value + delete(updateInfo, field.FieldName) + } else { + updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Set: field.Value}) + } case "add": updateInfo[field.FieldName] = append(updateInfo[field.FieldName], AdditionalFields{Add: field.Value}) case "remove": @@ -560,11 +568,14 @@ func (jiraClient *Client) updateIssue(_ context.Context, input *UpdateIssueInput } } apiEndpoint := "rest/api/2/issue/" + input.IssueKey + debug.Info("apiEndpoint: ", apiEndpoint) request := UpdateIssueRequset{ Body: struct { - Update map[string][]AdditionalFields `json:"update"` + Update map[string][]AdditionalFields `json:"update,omitempty"` + Fields map[string]interface{} `json:"fields,omitempty"` }{ Update: updateInfo, + Fields: fieldsInfo, }, Query: struct { NotifyUsers bool `json:"notify-users" api:"notifyUsers"` diff --git a/application/jira/v0/mock_update_issue.go b/application/jira/v0/mock_update_issue.go index 0885c754..1052ae3d 100644 --- a/application/jira/v0/mock_update_issue.go +++ b/application/jira/v0/mock_update_issue.go @@ -11,6 +11,7 @@ import ( type MockUpdateIssueRequset struct { IssueKey string `json:"issue-key"` Update map[string][]AdditionalFields `json:"update"` + Fields map[string]interface{} `json:"fields"` NotifyUsers bool `json:"notify-users" api:"notifyUsers"` ReturnIssue bool `json:"return-issue" api:"returnIssue"` } @@ -23,7 +24,7 @@ type MockUpdateIssueResp struct { // UpdateIssue updates an issue in Jira. func mockUpdateIssue(res http.ResponseWriter, req *http.Request) { var debug logger.Session - defer debug.SessionStart("UpdateIssue", logger.Develop).SessionEnd() + defer debug.SessionStart("mock UpdateIssue", logger.Develop).SessionEnd() var request MockUpdateIssueRequset err := json.NewDecoder(req.Body).Decode(&request) @@ -53,22 +54,40 @@ func mockUpdateIssue(res http.ResponseWriter, req *http.Request) { returnIssue := opt.Get("returnIssue") for key, fields := range request.Update { for _, field := range fields { + debug.Info("field", field) if field.Set != "" { issue.Fields[key] = field.Set } } } + for key, field := range request.Fields { + if field != "" { + issue.Fields[key] = field + } + } + newIssue := Issue{ + ID: issue.ID, + Key: issue.Key, + Self: issue.Self, + Fields: issue.Fields, + Description: issue.Fields["description"].(string), + IssueType: issue.Fields["issuetype"].(map[string]interface{})["name"].(string), + Summary: issue.Fields["summary"].(string), + Status: issue.Fields["status"].(map[string]interface{})["name"].(string), + } + for issue := range fakeIssues { + if fakeIssues[issue].ID == newIssue.ID { + fakeIssues[issue] = FakeIssue{ + ID: newIssue.ID, + Key: newIssue.Key, + Self: newIssue.Self, + Fields: newIssue.Fields, + } + break + } + } resp := MockUpdateIssueResp{ - Issue: Issue{ - ID: issue.ID, - Key: issue.Key, - Self: issue.Self, - Fields: issue.Fields, - Description: issue.Fields["description"].(string), - IssueType: issue.Fields["issuetype"].(map[string]interface{})["name"].(string), - Summary: issue.Fields["summary"].(string), - Status: issue.Fields["status"].(map[string]interface{})["name"].(string), - }, + Issue: newIssue, NotifyUsers: notifyUsers == "true", ReturnIssue: returnIssue == "true", } diff --git a/go.mod b/go.mod index c9cecba1..926e78af 100644 --- a/go.mod +++ b/go.mod @@ -184,6 +184,7 @@ require ( gitlab.com/golang-commonmark/mdurl v0.0.0-20191124015652-932350d1cb84 // indirect gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f // indirect gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b // indirect + go.mongodb.org/mongo-driver v1.16.0 go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect From b6a2c1ec3e4628d5a1315a25c485d4419de43b3f Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Mon, 12 Aug 2024 01:24:03 +0200 Subject: [PATCH 12/18] fix: remove task that the vendor does not support --- application/jira/v0/README.mdx | 133 +++++++++++++++++++++ application/jira/v0/config/definition.json | 3 +- application/jira/v0/config/tasks.json | 101 ---------------- 3 files changed, 134 insertions(+), 103 deletions(-) diff --git a/application/jira/v0/README.mdx b/application/jira/v0/README.mdx index 7075c604..abaeec70 100644 --- a/application/jira/v0/README.mdx +++ b/application/jira/v0/README.mdx @@ -13,6 +13,10 @@ It can carry out the following tasks: - [List Sprints](#list-sprints) - [Get Issue](#get-issue) - [Get Sprint](#get-sprint) +- [Create Issue](#create-issue) +- [Update Issue](#update-issue) +- [Create Sprint](#create-sprint) +- [Update Sprint](#update-sprint) @@ -183,4 +187,133 @@ Get a sprint in Jira +### Create Issue + +Create an issue in Jira + + +| Input | ID | Type | Description | +| :--- | :--- | :--- | :--- | +| Task ID (required) | `task` | string | `TASK_CREATE_ISSUE` | +| Project Key (required) | `project-key` | string | The key of the project, e.g. `INS` | +| Issue Type (required) | `issue-type` | object | The type of the issue, can be: `Task`, `Epic`, `Subtask`, etc. | +| Summary | `summary` | string | The summary of the issue | +| Description | `description` | string | The description of the issue | + + + +| Output | ID | Type | Description | +| :--- | :--- | :--- | :--- | +| ID | `id` | string | The ID of the issue | +| Key | `key` | string | The key of the issue, e.g. `JRA-1330` | +| Self | `self` | string | The URL of the issue | +| Fields | `fields` | object | The fields of the issue. All navigable and Agile fields are returned | +| Issue Type (optional) | `issue-type` | string | The type of the issue, can be: `Task`, `Epic` | +| Summary (optional) | `summary` | string | The summary of the issue | +| Description (optional) | `description` | string | The description of the issue | +| Status (optional) | `status` | string | The status of the issue, can be: `To Do`, `In Progress`, `Done` | + + + + + + +### Update Issue + +Update an issue in Jira + + +| Input | ID | Type | Description | +| :--- | :--- | :--- | :--- | +| Task ID (required) | `task` | string | `TASK_UPDATE_ISSUE` | +| Issue Key (required) | `issue-key` | string | The key of the issue, e.g. `JRA-1330` | +| Notify Users | `notify-users` | boolean | Whether to notify users | +| Update | `update` | object | Update an issue in Jira | + + + +| Output | ID | Type | Description | +| :--- | :--- | :--- | :--- | +| ID | `id` | string | The ID of the issue | +| Key | `key` | string | The key of the issue, e.g. `JRA-1330` | +| Self | `self` | string | The URL of the issue | +| Fields | `fields` | object | The fields of the issue. All navigable and Agile fields are returned | +| Issue Type (optional) | `issue-type` | string | The type of the issue, can be: `Task`, `Epic` | +| Summary (optional) | `summary` | string | The summary of the issue | +| Description (optional) | `description` | string | The description of the issue | +| Status (optional) | `status` | string | The status of the issue, can be: `To Do`, `In Progress`, `Done` | + + + + + + +### Create Sprint + +Create a sprint in Jira + + +| Input | ID | Type | Description | +| :--- | :--- | :--- | :--- | +| Task ID (required) | `task` | string | `TASK_CREATE_SPRINT` | +| Board Name (required) | `board-name` | string | The name of the board | +| Name (required) | `name` | string | The name of the sprint | +| Goal (required) | `goal` | string | The goal of the sprint | +| Start Date (required) | `start-date` | string | The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| End Date (required) | `end-date` | string | The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | + + + +| Output | ID | Type | Description | +| :--- | :--- | :--- | :--- | +| ID | `id` | integer | The ID of the sprint | +| Self | `self` | string | The URL of the sprint | +| State (optional) | `state` | string | The state of the sprint, can be: `active`, `closed`, `future` | +| Name (optional) | `name` | string | The name of the sprint | +| Start Date (optional) | `start-date` | string | The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| End Date (optional) | `end-date` | string | The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| Complete Date (optional) | `complete-date` | string | The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| Origin Board ID (optional) | `origin-board-id` | integer | The ID of the origin board | +| Goal (optional) | `goal` | string | The Goal of the sprint | + + + + + + +### Update Sprint + +Update a sprint in Jira + + +| Input | ID | Type | Description | +| :--- | :--- | :--- | :--- | +| Task ID (required) | `task` | string | `TASK_UPDATE_SPRINT` | +| Sprint ID (required) | `sprint-id` | integer | The ID of the sprint | +| Name (required) | `name` | string | The name of the sprint | +| Goal | `goal` | string | The goal of the sprint | +| Start Date (required) | `start-date` | string | The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| End Date (required) | `end-date` | string | The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| Current State (required) | `current-state` | string | The current state of the sprint, can be: `future`, `active`, `closed` | +| Enter Next State | `enter-next-state` | boolean | Whether to enter the next state. If `true`, the sprint will enter the next state, i.e., `future` -> `active` -> `closed` | + + + +| Output | ID | Type | Description | +| :--- | :--- | :--- | :--- | +| ID | `id` | integer | The ID of the sprint | +| Self | `self` | string | The URL of the sprint | +| State (optional) | `state` | string | The state of the sprint, can be: `active`, `closed`, `future` | +| Name (optional) | `name` | string | The name of the sprint | +| Start Date (optional) | `start-date` | string | The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| End Date (optional) | `end-date` | string | The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| Complete Date (optional) | `complete-date` | string | The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| Origin Board ID (optional) | `origin-board-id` | integer | The ID of the origin board | +| Goal (optional) | `goal` | string | The Goal of the sprint | + + + + + + diff --git a/application/jira/v0/config/definition.json b/application/jira/v0/config/definition.json index 23b9f026..002f80d6 100644 --- a/application/jira/v0/config/definition.json +++ b/application/jira/v0/config/definition.json @@ -8,8 +8,7 @@ "TASK_CREATE_ISSUE", "TASK_UPDATE_ISSUE", "TASK_CREATE_SPRINT", - "TASK_UPDATE_SPRINT", - "TASK_RANK_EPIC" + "TASK_UPDATE_SPRINT" ], "documentationUrl": "https://www.instill.tech/docs/component/application/jira", "icon": "assets/jira.svg", diff --git a/application/jira/v0/config/tasks.json b/application/jira/v0/config/tasks.json index 3c0849e4..0afd64a0 100644 --- a/application/jira/v0/config/tasks.json +++ b/application/jira/v0/config/tasks.json @@ -1425,106 +1425,5 @@ "title": "Output", "type": "object" } - }, - "TASK_RANK_EPIC": { - "description": "Rank an epic in Jira", - "instillShortDescription": "Rank an epic in Jira", - "input": { - "description": "Rank an epic in Jira", - "instillUIOrder": 0, - "instillEditOnNodeFields": [ - "epic-key", - "rank" - ], - "properties": { - "epic-key": { - "title": "Epic Key", - "description": "The key of the epic, e.g. `JRA-1330`", - "instillShortDescription": "The key of the epic", - "instillUIOrder": 0, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "after": { - "title": "Rank After Epic", - "description": "The key of the epic to rank after", - "instillShortDescription": "The key of the epic to rank after", - "instillUIOrder": 1, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "before": { - "title": "Rank Before Epic", - "description": "The key of the epic to rank before", - "instillShortDescription": "The key of the epic to rank before", - "instillUIOrder": 2, - "instillFormat": "string", - "instillAcceptFormats": [ - "string" - ], - "instillUpstreamTypes": [ - "value", - "reference", - "template" - ], - "type": "string" - }, - "rank": { - "title": "Rank", - "description": "The custom rank field ID of the epic", - "instillShortDescription": "The custom rank field ID of the epic", - "instillUIOrder": 3, - "instillFormat": "integer", - "instillAcceptFormats": [ - "integer" - ], - "instillUpstreamTypes": [ - "value", - "reference" - ], - "type": "integer" - } - }, - "required": [ - "epic-key", - "rank" - ], - "title": "Input", - "type": "object" - }, - "output": { - "description": "Rank an epic in Jira", - "instillUIOrder": 0, - "properties": { - "success": { - "description": "Whether the epic is ranked", - "instillUIOrder": 0, - "title": "Success", - "instillFormat": "boolean", - "type": "boolean" - } - }, - "required": [ - "success" - ], - "title": "Output", - "type": "object" - } } } From 2baca74c7cf6726a041fbae31be4fa393ed73fdd Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Mon, 12 Aug 2024 01:39:38 +0200 Subject: [PATCH 13/18] fix: remove vulnerablility --- application/jira/v0/mock_server.go | 2 +- go.mod | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/application/jira/v0/mock_server.go b/application/jira/v0/mock_server.go index 468c84b8..44f59059 100644 --- a/application/jira/v0/mock_server.go +++ b/application/jira/v0/mock_server.go @@ -230,7 +230,7 @@ func mockListIssues(res http.ResponseWriter, req *http.Request) { for _, issue := range fakeIssues { prefix := strings.Split(issue.Key, "-")[0] if board.Name != "" && prefix != board.Name { - fmt.Println("prefix", prefix, "board.Name", board.Name) + // fmt.Println("prefix", prefix, "board.Name", board.Name) continue } if jql != "" { diff --git a/go.mod b/go.mod index 926e78af..c9cecba1 100644 --- a/go.mod +++ b/go.mod @@ -184,7 +184,6 @@ require ( gitlab.com/golang-commonmark/mdurl v0.0.0-20191124015652-932350d1cb84 // indirect gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f // indirect gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b // indirect - go.mongodb.org/mongo-driver v1.16.0 go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect From f4c58c163c25435e9d15ad1054756cbbf812c9a8 Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Mon, 12 Aug 2024 01:44:57 +0200 Subject: [PATCH 14/18] fix: remove logger --- application/jira/v0/issues.go | 18 ------------------ application/jira/v0/main.go | 1 - application/jira/v0/mock_create_issue.go | 11 +---------- application/jira/v0/mock_create_sprint.go | 6 ------ application/jira/v0/mock_server.go | 1 - application/jira/v0/mock_update_issue.go | 5 ----- application/jira/v0/mock_update_sprint.go | 7 ------- application/jira/v0/sprint.go | 19 ------------------- go.mod | 3 --- go.sum | 9 --------- 10 files changed, 1 insertion(+), 79 deletions(-) diff --git a/application/jira/v0/issues.go b/application/jira/v0/issues.go index 89a069b7..2d690070 100644 --- a/application/jira/v0/issues.go +++ b/application/jira/v0/issues.go @@ -9,7 +9,6 @@ import ( "github.com/go-resty/resty/v2" "github.com/instill-ai/component/base" - "github.com/instill-ai/component/tools/logger" "github.com/instill-ai/x/errmsg" jsoniter "github.com/json-iterator/go" "google.golang.org/protobuf/types/known/structpb" @@ -352,10 +351,6 @@ func convertCreateIssueRequest(issue *CreateIssueInput) *CreateIssueRequset { } func (jiraClient *Client) createIssueTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { - var debug logger.Session - defer debug.SessionStart("CreateIssueTask", logger.Develop).SessionEnd() - - debug.Info("props ", props) var issue CreateIssueInput if err := base.ConvertFromStructpb(props, &issue); err != nil { return nil, err @@ -380,7 +375,6 @@ func (jiraClient *Client) createIssueTask(ctx context.Context, props *structpb.S fmt.Sprintf("failed to convert %v to `Create Issue` Output", resp.Result()), ) } - debug.Info("Created Issue: ", createdResult) getIssueInput, err := base.ConvertToStructpb(GetIssueInput{IssueKey: createdResult.Key, UpdateHistory: issue.UpdateHistory}) if err != nil { @@ -433,10 +427,7 @@ type UpdateIssueOutput struct { func (jiraClient *Client) updateIssueTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { var err error - var debug logger.Session - defer debug.SessionStart("UpdateIssueTask", logger.Develop).SessionEnd() - debug.Info("props ", props) var input UpdateIssueInput if err := base.ConvertFromStructpb(props, &input); err != nil { return nil, err @@ -454,7 +445,6 @@ func (jiraClient *Client) updateIssueTask(ctx context.Context, props *structpb.S } else if input.Update.UpdateType == "Move Issue to Epic" { err = jiraClient.moveIssueToEpic(ctx, input.IssueKey, input.Update.EpicKey) if err != nil { - debug.Error("Error: ", err) if !strings.Contains(errmsg.Message(err), "The request contains a next-gen issue") { return nil, err } @@ -466,10 +456,7 @@ func (jiraClient *Client) updateIssueTask(ctx context.Context, props *structpb.S "key": input.Update.EpicKey, }, }) - debug.Info("Updating issue with parent key") - debug.Info("Input: ", input.Update.UpdateFields) if _, err = jiraClient.updateIssue(ctx, &input); err != nil { - debug.Error("updateIssue err: ", err) return nil, errmsg.AddMessage( fmt.Errorf("failed to update issue with parent key"), "You can only move issues to epics.", @@ -500,7 +487,6 @@ func (jiraClient *Client) updateIssueTask(ctx context.Context, props *structpb.S fmt.Sprintf("%s is an invalid update type", input.Update.UpdateType), ) } - debug.Info("Updated Issue: ", out) if err != nil { return nil, err } @@ -524,8 +510,6 @@ func (jiraClient *Client) moveIssueToEpic(_ context.Context, issueKey, epicKey s } func (jiraClient *Client) updateIssue(_ context.Context, input *UpdateIssueInput) (*UpdateIssueResp, error) { - var debug logger.Session - defer debug.SessionStart("UpdateIssue", logger.Develop).SessionEnd() if input.Update.UpdateType != "Custom Update" { return nil, errmsg.AddMessage( fmt.Errorf("invalid update type"), @@ -568,7 +552,6 @@ func (jiraClient *Client) updateIssue(_ context.Context, input *UpdateIssueInput } } apiEndpoint := "rest/api/2/issue/" + input.IssueKey - debug.Info("apiEndpoint: ", apiEndpoint) request := UpdateIssueRequset{ Body: struct { Update map[string][]AdditionalFields `json:"update,omitempty"` @@ -590,7 +573,6 @@ func (jiraClient *Client) updateIssue(_ context.Context, input *UpdateIssueInput if err != nil { return nil, err } - debug.Info("Request Body: ", string(body)) req := jiraClient.Client.R().SetResult(&UpdateIssueResp{}).SetBody(string(body)) err = addQueryOptions(req, request.Query) if err != nil { diff --git a/application/jira/v0/main.go b/application/jira/v0/main.go index ccd5920a..ace8ca9c 100644 --- a/application/jira/v0/main.go +++ b/application/jira/v0/main.go @@ -10,7 +10,6 @@ import ( "google.golang.org/protobuf/types/known/structpb" "github.com/instill-ai/component/base" - "github.com/instill-ai/component/tools/logger" "github.com/instill-ai/x/errmsg" ) diff --git a/application/jira/v0/mock_create_issue.go b/application/jira/v0/mock_create_issue.go index e3549142..ff832136 100644 --- a/application/jira/v0/mock_create_issue.go +++ b/application/jira/v0/mock_create_issue.go @@ -4,8 +4,6 @@ import ( "encoding/json" "fmt" "net/http" - - "github.com/instill-ai/component/tools/logger" ) type mockCreateIssueRequest struct { @@ -27,9 +25,6 @@ type mockCreateIssueResponse struct { } func mockCreateIssue(res http.ResponseWriter, req *http.Request) { - var debug logger.Session - defer debug.SessionStart("MockCreateIssueTask", logger.Develop).SessionEnd() - var err error if req.Method != http.MethodPost { http.Error(res, "Method not allowed", http.StatusMethodNotAllowed) @@ -42,11 +37,7 @@ func mockCreateIssue(res http.ResponseWriter, req *http.Request) { return } fields := body.Fields - update := body.Update - debug.Info("body: ", body) - debug.Info("Fields: ", fields) - debug.Info("Update: ", update) - + // update := body.Update project, ok := fields["project"].(map[string]interface{})["key"].(string) if !ok { http.Error(res, "Invalid project", http.StatusBadRequest) diff --git a/application/jira/v0/mock_create_sprint.go b/application/jira/v0/mock_create_sprint.go index bfe44bac..bab7bf34 100644 --- a/application/jira/v0/mock_create_sprint.go +++ b/application/jira/v0/mock_create_sprint.go @@ -3,8 +3,6 @@ package jira import ( "encoding/json" "net/http" - - "github.com/instill-ai/component/tools/logger" ) type mockCreateSprintRequest struct { @@ -16,12 +14,8 @@ type mockCreateSprintRequest struct { } func mockCreateSprint(res http.ResponseWriter, req *http.Request) { - var debug logger.Session - defer debug.SessionStart("MockCreateIssueTask", logger.Develop).SessionEnd() var err error - debug.Info("MockCreateIssueTask called") - debug.Info(req.Method) if req.Method != http.MethodPost { http.Error(res, "Method not allowed", http.StatusMethodNotAllowed) return diff --git a/application/jira/v0/mock_server.go b/application/jira/v0/mock_server.go index 44f59059..ea0d5ca8 100644 --- a/application/jira/v0/mock_server.go +++ b/application/jira/v0/mock_server.go @@ -230,7 +230,6 @@ func mockListIssues(res http.ResponseWriter, req *http.Request) { for _, issue := range fakeIssues { prefix := strings.Split(issue.Key, "-")[0] if board.Name != "" && prefix != board.Name { - // fmt.Println("prefix", prefix, "board.Name", board.Name) continue } if jql != "" { diff --git a/application/jira/v0/mock_update_issue.go b/application/jira/v0/mock_update_issue.go index 1052ae3d..e7f7d045 100644 --- a/application/jira/v0/mock_update_issue.go +++ b/application/jira/v0/mock_update_issue.go @@ -5,7 +5,6 @@ import ( "net/http" "github.com/go-chi/chi/v5" - "github.com/instill-ai/component/tools/logger" ) type MockUpdateIssueRequset struct { @@ -23,9 +22,6 @@ type MockUpdateIssueResp struct { // UpdateIssue updates an issue in Jira. func mockUpdateIssue(res http.ResponseWriter, req *http.Request) { - var debug logger.Session - defer debug.SessionStart("mock UpdateIssue", logger.Develop).SessionEnd() - var request MockUpdateIssueRequset err := json.NewDecoder(req.Body).Decode(&request) if err != nil { @@ -54,7 +50,6 @@ func mockUpdateIssue(res http.ResponseWriter, req *http.Request) { returnIssue := opt.Get("returnIssue") for key, fields := range request.Update { for _, field := range fields { - debug.Info("field", field) if field.Set != "" { issue.Fields[key] = field.Set } diff --git a/application/jira/v0/mock_update_sprint.go b/application/jira/v0/mock_update_sprint.go index 4a244a34..8d1dbc0f 100644 --- a/application/jira/v0/mock_update_sprint.go +++ b/application/jira/v0/mock_update_sprint.go @@ -6,7 +6,6 @@ import ( "strconv" "github.com/go-chi/chi/v5" - "github.com/instill-ai/component/tools/logger" ) type MockUpdateSprintRequset struct { @@ -24,9 +23,6 @@ type MockUpdateSprintResp struct { // UpdateSprint updates an issue in Jira. func mockUpdateSprint(res http.ResponseWriter, req *http.Request) { - var debug logger.Session - defer debug.SessionStart("mockUpdateSprint", logger.Develop).SessionEnd() - var request MockUpdateSprintRequset err := json.NewDecoder(req.Body).Decode(&request) @@ -34,7 +30,6 @@ func mockUpdateSprint(res http.ResponseWriter, req *http.Request) { http.Error(res, err.Error(), http.StatusBadRequest) return } - debug.Info("request", request) sprintID, err := strconv.Atoi(chi.URLParam(req, "sprintId")) if err != nil { http.Error(res, "sprint id is required", http.StatusBadRequest) @@ -75,8 +70,6 @@ func mockUpdateSprint(res http.ResponseWriter, req *http.Request) { if mockSprint.OriginBoardID != 0 { fakeSprints[i].OriginBoardID = mockSprint.OriginBoardID } - debug.Info("target sprint", mockSprint) - debug.Info("updated sprint", fakeSprints[i]) resp = MockUpdateSprintResp{ FakeSprint: FakeSprint{ ID: fakeSprints[i].ID, diff --git a/application/jira/v0/sprint.go b/application/jira/v0/sprint.go index 737e5d27..f6015975 100644 --- a/application/jira/v0/sprint.go +++ b/application/jira/v0/sprint.go @@ -7,7 +7,6 @@ import ( "time" "github.com/instill-ai/component/base" - "github.com/instill-ai/component/tools/logger" "github.com/instill-ai/x/errmsg" "google.golang.org/protobuf/types/known/structpb" ) @@ -170,14 +169,10 @@ type CreateSprintOutput struct { } func (jiraClient *Client) createSprintTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { - var debug logger.Session - defer debug.SessionStart("CreateIssueTask", logger.Develop).SessionEnd() - var opt CreateSprintInput if err := base.ConvertFromStructpb(props, &opt); err != nil { return nil, err } - debug.Info("Create Sprint Task", opt) apiBaseURL := "rest/agile/1.0/sprint" // Validate timestamp format RFC3339 @@ -205,13 +200,10 @@ func (jiraClient *Client) createSprintTask(ctx context.Context, props *structpb. } } boardName := opt.BoardName - debug.Info("opt", opt) - debug.Info("boardName", boardName) boards, err := jiraClient.listBoards(ctx, &ListBoardsInput{Name: boardName}) if err != nil { return nil, err } - debug.Info("boards", boards) if len(boards.Values) == 0 { return nil, errmsg.AddMessage( @@ -226,7 +218,6 @@ func (jiraClient *Client) createSprintTask(ctx context.Context, props *structpb. } board := boards.Values[0] boardID := board.ID - debug.Info("boardID", boardID) req := jiraClient.Client.R().SetResult(&CreateSprintResp{}).SetBody(&CreateSprintRequest{ Name: opt.Name, @@ -299,16 +290,12 @@ type UpdateSprintOutput struct { } func (jiraClient *Client) updateSprintTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) { - var debug logger.Session - defer debug.SessionStart("updateSprintTask", logger.Develop).SessionEnd() var opt UpdateSprintInput if err := base.ConvertFromStructpb(props, &opt); err != nil { return nil, err } apiEndpoint := fmt.Sprintf("rest/agile/1.0/sprint/%v", opt.SprintID) - debug.Info("Update Sprint Task", opt) - debug.Info("apiEndpoint", apiEndpoint) var body UpdateSprintRequest structOpt, err := base.ConvertToStructpb(opt) @@ -321,8 +308,6 @@ func (jiraClient *Client) updateSprintTask(ctx context.Context, props *structpb. body.StartDate = opt.StartDate body.EndDate = opt.EndDate if _, err := time.Parse(time.RFC3339, body.StartDate); err != nil { - debug.Info("body start date", body.StartDate) - debug.Info("opt start date", opt.StartDate) if body.StartDate == "" { body.StartDate = time.Now().Format(time.RFC3339) } else { @@ -365,15 +350,11 @@ func (jiraClient *Client) updateSprintTask(ctx context.Context, props *structpb. if err != nil { return nil, err } - debug.Info("body", jsonOpt) req := jiraClient.Client.R().SetResult(&Sprint{}).SetBody(jsonOpt) - debug.Info("body", req.Body) resp, err := req.Put(apiEndpoint) - // debug.Info("resp", resp) if err != nil { - debug.Error(err) return nil, err } diff --git a/go.mod b/go.mod index c9cecba1..fc6d1bbd 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( github.com/denisenkom/go-mssqldb v0.12.3 github.com/emersion/go-imap/v2 v2.0.0-beta.3 github.com/emersion/go-message v0.18.1 - github.com/fatih/color v1.16.0 github.com/fogleman/gg v1.3.0 github.com/frankban/quicktest v1.14.6 github.com/gabriel-vasile/mimetype v1.4.3 @@ -155,8 +154,6 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/levigross/exp-html v0.0.0-20120902181939-8df60c69a8f5 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index 5c9b40bd..89a95cc1 100644 --- a/go.sum +++ b/go.sum @@ -121,8 +121,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -376,11 +374,6 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= @@ -673,10 +666,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= From a2f2bf49f9f9181cd541017b66bbafb02b13a4f4 Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Tue, 13 Aug 2024 01:20:29 +0200 Subject: [PATCH 15/18] chore: add move to epic testcase --- application/jira/v0/component_test.go | 181 +++++++++++++++++++---- application/jira/v0/mock_database.go | 17 ++- application/jira/v0/mock_server.go | 2 + application/jira/v0/mock_update_issue.go | 4 + 4 files changed, 173 insertions(+), 31 deletions(-) diff --git a/application/jira/v0/component_test.go b/application/jira/v0/component_test.go index 93072e59..309cbd8a 100644 --- a/application/jira/v0/component_test.go +++ b/application/jira/v0/component_test.go @@ -126,7 +126,8 @@ func TestComponent_GetIssueTask(t *testing.T) { ID: "4", Key: "KAN-4", Fields: map[string]interface{}{ - "summary": "Test issue 4", + "summary": "Test issue 4", + "description": "Test description 4", "status": map[string]interface{}{ "name": "Done", }, @@ -134,10 +135,11 @@ func TestComponent_GetIssueTask(t *testing.T) { "name": "Epic", }, }, - Self: "https://test.atlassian.net/rest/agile/1.0/issue/4", - Summary: "Test issue 4", - Status: "Done", - IssueType: "Epic", + Self: "https://test.atlassian.net/rest/agile/1.0/issue/4", + Summary: "Test issue 4", + Description: "Test description 4", + Status: "Done", + IssueType: "Epic", }, }, }, @@ -145,7 +147,7 @@ func TestComponent_GetIssueTask(t *testing.T) { _type: "nok", name: "404 - Not Found", input: GetIssueInput{ - IssueKey: "5", + IssueKey: "100", UpdateHistory: true, }, wantErr: "unsuccessful HTTP response.*", @@ -208,7 +210,7 @@ func TestComponent_ListIssuesTask(t *testing.T) { }, }, wantResp: ListIssuesOutput{ - Total: 1, + Total: 2, StartAt: 0, MaxResults: 10, Issues: []Issue{ @@ -216,7 +218,8 @@ func TestComponent_ListIssuesTask(t *testing.T) { ID: "4", Key: "KAN-4", Fields: map[string]interface{}{ - "summary": "Test issue 4", + "summary": "Test issue 4", + "description": "Test description 4", "status": map[string]interface{}{ "name": "Done", }, @@ -224,10 +227,30 @@ func TestComponent_ListIssuesTask(t *testing.T) { "name": "Epic", }, }, - IssueType: "Epic", - Self: "https://test.atlassian.net/rest/agile/1.0/issue/4", - Status: "Done", - Summary: "Test issue 4", + IssueType: "Epic", + Self: "https://test.atlassian.net/rest/agile/1.0/issue/4", + Description: "Test description 4", + Status: "Done", + Summary: "Test issue 4", + }, + { + ID: "5", + Key: "KAN-5", + Fields: map[string]interface{}{ + "summary": "Test issue 5", + "description": "Test description 5", + "status": map[string]interface{}{ + "name": "Done", + }, + "issuetype": map[string]interface{}{ + "name": "Task", + }, + }, + IssueType: "Task", + Self: "https://test.atlassian.net/rest/agile/1.0/issue/5", + Description: "Test description 5", + Status: "Done", + Summary: "Test issue 5", }, }, }, @@ -244,7 +267,7 @@ func TestComponent_ListIssuesTask(t *testing.T) { }, }, wantResp: ListIssuesOutput{ - Total: 1, + Total: 2, StartAt: 0, MaxResults: 10, Issues: []Issue{ @@ -252,7 +275,8 @@ func TestComponent_ListIssuesTask(t *testing.T) { ID: "4", Key: "KAN-4", Fields: map[string]interface{}{ - "summary": "Test issue 4", + "summary": "Test issue 4", + "description": "Test description 4", "status": map[string]interface{}{ "name": "Done", }, @@ -260,10 +284,30 @@ func TestComponent_ListIssuesTask(t *testing.T) { "name": "Epic", }, }, - IssueType: "Epic", - Self: "https://test.atlassian.net/rest/agile/1.0/issue/4", - Status: "Done", - Summary: "Test issue 4", + IssueType: "Epic", + Self: "https://test.atlassian.net/rest/agile/1.0/issue/4", + Description: "Test description 4", + Status: "Done", + Summary: "Test issue 4", + }, + { + ID: "5", + Key: "KAN-5", + Fields: map[string]interface{}{ + "summary": "Test issue 5", + "description": "Test description 5", + "status": map[string]interface{}{ + "name": "Done", + }, + "issuetype": map[string]interface{}{ + "name": "Task", + }, + }, + IssueType: "Epic", + Self: "https://test.atlassian.net/rest/agile/1.0/issue/5", + Description: "Test description 5", + Status: "Done", + Summary: "Test issue 5", }, }, }, @@ -280,7 +324,7 @@ func TestComponent_ListIssuesTask(t *testing.T) { }, }, wantResp: ListIssuesOutput{ - Total: 1, + Total: 2, StartAt: 0, MaxResults: 10, Issues: []Issue{ @@ -288,7 +332,8 @@ func TestComponent_ListIssuesTask(t *testing.T) { ID: "4", Key: "KAN-4", Fields: map[string]interface{}{ - "summary": "Test issue 4", + "summary": "Test issue 4", + "description": "Test description 4", "status": map[string]interface{}{ "name": "Done", }, @@ -296,10 +341,30 @@ func TestComponent_ListIssuesTask(t *testing.T) { "name": "Epic", }, }, - IssueType: "Epic", - Self: "https://test.atlassian.net/rest/agile/1.0/issue/4", - Status: "Done", - Summary: "Test issue 4", + IssueType: "Epic", + Self: "https://test.atlassian.net/rest/agile/1.0/issue/4", + Description: "Test description 4", + Status: "Done", + Summary: "Test issue 4", + }, + { + ID: "5", + Key: "KAN-5", + Fields: map[string]interface{}{ + "summary": "Test issue 5", + "description": "Test description 5", + "status": map[string]interface{}{ + "name": "Done", + }, + "issuetype": map[string]interface{}{ + "name": "Task", + }, + }, + IssueType: "Task", + Self: "https://test.atlassian.net/rest/agile/1.0/issue/5", + Description: "Test description 5", + Status: "Done", + Summary: "Test issue 5", }, }, }, @@ -316,7 +381,7 @@ func TestComponent_ListIssuesTask(t *testing.T) { }, }, wantResp: ListIssuesOutput{ - Total: 1, + Total: 2, StartAt: 0, MaxResults: 10, Issues: []Issue{ @@ -324,7 +389,8 @@ func TestComponent_ListIssuesTask(t *testing.T) { ID: "4", Key: "KAN-4", Fields: map[string]interface{}{ - "summary": "Test issue 4", + "summary": "Test issue 4", + "description": "Test description 4", "status": map[string]interface{}{ "name": "Done", }, @@ -332,10 +398,30 @@ func TestComponent_ListIssuesTask(t *testing.T) { "name": "Epic", }, }, - IssueType: "Epic", - Self: "https://test.atlassian.net/rest/agile/1.0/issue/4", - Status: "Done", - Summary: "Test issue 4", + IssueType: "Epic", + Self: "https://test.atlassian.net/rest/agile/1.0/issue/4", + Description: "Test description 4", + Status: "Done", + Summary: "Test issue 4", + }, + { + ID: "5", + Key: "KAN-5", + Fields: map[string]interface{}{ + "summary": "Test issue 5", + "description": "Test description 5", + "status": map[string]interface{}{ + "name": "Done", + }, + "issuetype": map[string]interface{}{ + "name": "Task", + }, + }, + IssueType: "Task", + Self: "https://test.atlassian.net/rest/agile/1.0/issue/5", + Description: "Test description 5", + Status: "Done", + Summary: "Test issue 5", }, }, }, @@ -585,6 +671,41 @@ func TestComponent_UpdateIssueTask(t *testing.T) { }, }, }, + { + _type: "ok", + name: "move issue to epic", + input: UpdateIssueInput{ + IssueKey: "KAN-5", + Update: Update{ + UpdateType: "Move Issue to Epic", + EpicKey: "KAN-4", + }, + }, + wantResp: UpdateIssueOutput{ + Issue{ + ID: "5", + Key: "KAN-5", + Fields: map[string]interface{}{ + "summary": "Test issue 5", + "description": "Test description 5", + "status": map[string]interface{}{ + "name": "Done", + }, + "issuetype": map[string]interface{}{ + "name": "Task", + }, + "parent": map[string]interface{}{ + "key": "KAN-4", + }, + }, + Self: "https://test.atlassian.net/rest/agile/1.0/issue/5", + Summary: "Test issue 5", + Status: "Done", + Description: "Test description 5", + IssueType: "Task", + }, + }, + }, { _type: "nok", name: "400 - Bad Request", diff --git a/application/jira/v0/mock_database.go b/application/jira/v0/mock_database.go index 34b2339c..461997ff 100644 --- a/application/jira/v0/mock_database.go +++ b/application/jira/v0/mock_database.go @@ -100,7 +100,8 @@ var fakeIssues = []FakeIssue{ ID: "4", Key: "KAN-4", Fields: map[string]interface{}{ - "summary": "Test issue 4", + "summary": "Test issue 4", + "description": "Test description 4", "status": map[string]interface{}{ "name": "Done", }, @@ -109,6 +110,20 @@ var fakeIssues = []FakeIssue{ }, }, }, + { + ID: "5", + Key: "KAN-5", + Fields: map[string]interface{}{ + "summary": "Test issue 5", + "description": "Test description 5", + "status": map[string]interface{}{ + "name": "Done", + }, + "issuetype": map[string]interface{}{ + "name": "Task", + }, + }, + }, } type FakeSprint struct { diff --git a/application/jira/v0/mock_server.go b/application/jira/v0/mock_server.go index ea0d5ca8..699048fb 100644 --- a/application/jira/v0/mock_server.go +++ b/application/jira/v0/mock_server.go @@ -22,6 +22,8 @@ func router(middlewares ...func(http.Handler) http.Handler) http.Handler { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"cloudId":"12345678-1234-1234-1234-123456789012"}`)) }) + + r.Post("/rest/agile/1.0/epic/{epic-key}/issue", mockMoveIssueToEpic) r.Get("/rest/agile/1.0/issue/{issueIdOrKey:[a-zA-z0-9-]+}", mockGetIssue) r.Get("/rest/agile/1.0/sprint/{sprintId}", mockGetSprint) r.Put("/rest/agile/1.0/sprint/{sprintId}", mockUpdateSprint) diff --git a/application/jira/v0/mock_update_issue.go b/application/jira/v0/mock_update_issue.go index e7f7d045..51bef3e8 100644 --- a/application/jira/v0/mock_update_issue.go +++ b/application/jira/v0/mock_update_issue.go @@ -92,3 +92,7 @@ func mockUpdateIssue(res http.ResponseWriter, req *http.Request) { return } } + +func mockMoveIssueToEpic(res http.ResponseWriter, _ *http.Request) { + http.Error(res, "The request contains a next-gen issue", http.StatusBadRequest) +} From c7c7727abaaebf50f1ee820b37b492283def7a36 Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Wed, 21 Aug 2024 17:15:17 +0100 Subject: [PATCH 16/18] chore: improve function readibility --- application/jira/v0/client.go | 65 +++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/application/jira/v0/client.go b/application/jira/v0/client.go index 34727728..f34ed9d9 100644 --- a/application/jira/v0/client.go +++ b/application/jira/v0/client.go @@ -97,32 +97,49 @@ func (e errBody) Message() string { return strings.Join(e.Body.Msg, " ") } +func turnToStringQueryParams(val any) string { + var stringVal string + switch val := val.(type) { + case string: + stringVal = val + case int: + stringVal = fmt.Sprintf("%d", val) + case bool: + stringVal = fmt.Sprintf("%t", val) + case []string: + stringVal = strings.Join(val, ",") + case []int: + var strVals []string + for _, v := range val { + strVals = append(strVals, fmt.Sprintf("%d", v)) + } + stringVal = strings.Join(strVals, ",") + default: + return "" + } + return stringVal +} + func addQueryOptions(req *resty.Request, opt interface{}) error { v := reflect.ValueOf(opt) if v.Kind() == reflect.Ptr && v.IsNil() { return nil } + if v.Kind() == reflect.Ptr { + v = v.Elem() + } if v.Kind() == reflect.Map { for _, key := range v.MapKeys() { - if v.MapIndex(key).IsValid() && v.MapIndex(key).CanInterface() { - val := v.MapIndex(key).Interface() - var stringVal string - switch val := val.(type) { - case string: - stringVal = val - case int: - stringVal = fmt.Sprintf("%d", val) - case bool: - stringVal = fmt.Sprintf("%t", val) - default: - continue - } - if stringVal == fmt.Sprintf("%v", reflect.Zero(reflect.TypeOf(val))) { - continue - } - paramName := key.String() - req.SetQueryParam(paramName, stringVal) + if !v.MapIndex(key).IsValid() || !v.MapIndex(key).CanInterface() { + continue } + val := v.MapIndex(key).Interface() + stringVal := turnToStringQueryParams(val) + if stringVal == fmt.Sprintf("%v", reflect.Zero(reflect.TypeOf(val))) { + continue + } + paramName := key.String() + req.SetQueryParam(paramName, stringVal) } } else if v.Kind() == reflect.Struct { typeOfS := v.Type() @@ -131,17 +148,7 @@ func addQueryOptions(req *resty.Request, opt interface{}) error { continue } val := v.Field(i).Interface() - var stringVal string - switch val := val.(type) { - case string: - stringVal = val - case int: - stringVal = fmt.Sprintf("%d", val) - case bool: - stringVal = fmt.Sprintf("%t", val) - default: - continue - } + stringVal := turnToStringQueryParams(val) if stringVal == fmt.Sprintf("%v", reflect.Zero(reflect.TypeOf(val))) { continue } From 77221451df670073183bbdf1c0a4522d5ac72baf Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Thu, 22 Aug 2024 21:35:55 +0100 Subject: [PATCH 17/18] fix: fix typo --- application/jira/v0/config/tasks.json | 4 ++-- application/jira/v0/issues.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application/jira/v0/config/tasks.json b/application/jira/v0/config/tasks.json index 0afd64a0..7126eaca 100644 --- a/application/jira/v0/config/tasks.json +++ b/application/jira/v0/config/tasks.json @@ -1161,7 +1161,7 @@ "type": "object" }, "output": { - "description": "Create an issue in Jira", + "description": "Update an issue in Jira", "instillUIOrder": 0, "$ref": "#/$defs/issue", "title": "Output", @@ -1419,7 +1419,7 @@ "type": "object" }, "output": { - "description": "Create a sprint in Jira", + "description": "Update a sprint in Jira", "instillUIOrder": 0, "$ref": "#/$defs/sprint", "title": "Output", diff --git a/application/jira/v0/issues.go b/application/jira/v0/issues.go index 2d690070..0973feb5 100644 --- a/application/jira/v0/issues.go +++ b/application/jira/v0/issues.go @@ -459,7 +459,7 @@ func (jiraClient *Client) updateIssueTask(ctx context.Context, props *structpb.S if _, err = jiraClient.updateIssue(ctx, &input); err != nil { return nil, errmsg.AddMessage( fmt.Errorf("failed to update issue with parent key"), - "You can only move issues to epics.", + "You can only move issues to epics. The Jira API response with: "+errmsg.Message(err), ) } } From 1e3e18452ca6cbb5aa631a7f0d7c085a26eead90 Mon Sep 17 00:00:00 2001 From: YCK1130 Date: Fri, 23 Aug 2024 13:03:46 +0100 Subject: [PATCH 18/18] fix: fix time description --- application/jira/v0/README.mdx | 26 +++++++++++++------------- application/jira/v0/config/tasks.json | 14 +++++++------- application/jira/v0/sprint.go | 8 ++++++++ go.sum | 1 + 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/application/jira/v0/README.mdx b/application/jira/v0/README.mdx index abaeec70..378183c0 100644 --- a/application/jira/v0/README.mdx +++ b/application/jira/v0/README.mdx @@ -176,9 +176,9 @@ Get a sprint in Jira | Self (optional) | `self` | string | The URL of the sprint | | State (optional) | `state` | string | The state of the sprint, can be: `active`, `closed`, `future` | | Name (optional) | `name` | string | The name of the sprint | -| Start Date (optional) | `start-date` | string | The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | -| End Date (optional) | `end-date` | string | The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | -| Complete Date (optional) | `complete-date` | string | The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| Start Date (optional) | `start-date` | string | The start date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | +| End Date (optional) | `end-date` | string | The end date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | +| Complete Date (optional) | `complete-date` | string | The complete date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | | Origin Board ID (optional) | `origin-board-id` | integer | The ID of the origin board | | Goal (optional) | `goal` | string | The Goal of the sprint | @@ -259,8 +259,8 @@ Create a sprint in Jira | Board Name (required) | `board-name` | string | The name of the board | | Name (required) | `name` | string | The name of the sprint | | Goal (required) | `goal` | string | The goal of the sprint | -| Start Date (required) | `start-date` | string | The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | -| End Date (required) | `end-date` | string | The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| Start Date (required) | `start-date` | string | The start date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | +| End Date (required) | `end-date` | string | The end date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | @@ -270,9 +270,9 @@ Create a sprint in Jira | Self | `self` | string | The URL of the sprint | | State (optional) | `state` | string | The state of the sprint, can be: `active`, `closed`, `future` | | Name (optional) | `name` | string | The name of the sprint | -| Start Date (optional) | `start-date` | string | The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | -| End Date (optional) | `end-date` | string | The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | -| Complete Date (optional) | `complete-date` | string | The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| Start Date (optional) | `start-date` | string | The start date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | +| End Date (optional) | `end-date` | string | The end date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | +| Complete Date (optional) | `complete-date` | string | The complete date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | | Origin Board ID (optional) | `origin-board-id` | integer | The ID of the origin board | | Goal (optional) | `goal` | string | The Goal of the sprint | @@ -292,8 +292,8 @@ Update a sprint in Jira | Sprint ID (required) | `sprint-id` | integer | The ID of the sprint | | Name (required) | `name` | string | The name of the sprint | | Goal | `goal` | string | The goal of the sprint | -| Start Date (required) | `start-date` | string | The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | -| End Date (required) | `end-date` | string | The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| Start Date (required) | `start-date` | string | The start date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | +| End Date (required) | `end-date` | string | The end date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | | Current State (required) | `current-state` | string | The current state of the sprint, can be: `future`, `active`, `closed` | | Enter Next State | `enter-next-state` | boolean | Whether to enter the next state. If `true`, the sprint will enter the next state, i.e., `future` -> `active` -> `closed` | @@ -305,9 +305,9 @@ Update a sprint in Jira | Self | `self` | string | The URL of the sprint | | State (optional) | `state` | string | The state of the sprint, can be: `active`, `closed`, `future` | | Name (optional) | `name` | string | The name of the sprint | -| Start Date (optional) | `start-date` | string | The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | -| End Date (optional) | `end-date` | string | The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | -| Complete Date (optional) | `complete-date` | string | The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z | +| Start Date (optional) | `start-date` | string | The start date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | +| End Date (optional) | `end-date` | string | The end date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | +| Complete Date (optional) | `complete-date` | string | The complete date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000` | | Origin Board ID (optional) | `origin-board-id` | integer | The ID of the origin board | | Goal (optional) | `goal` | string | The Goal of the sprint | diff --git a/application/jira/v0/config/tasks.json b/application/jira/v0/config/tasks.json index 7126eaca..012987d8 100644 --- a/application/jira/v0/config/tasks.json +++ b/application/jira/v0/config/tasks.json @@ -157,21 +157,21 @@ }, "start-date": { "title": "Start Date", - "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "description": "The start date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000`", "type": "string", "instillUIOrder": 4, "instillFormat": "string" }, "end-date": { "title": "End Date", - "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "description": "The end date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000`", "type": "string", "instillUIOrder": 5, "instillFormat": "string" }, "complete-date": { "title": "Complete Date", - "description": "The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "description": "The complete date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000`", "type": "string", "instillUIOrder": 6, "instillFormat": "string" @@ -1232,7 +1232,7 @@ }, "start-date": { "title": "Start Date", - "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "description": "The start date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000`", "instillShortDescription": "The start date of the sprint", "instillUIOrder": 3, "instillFormat": "string", @@ -1248,7 +1248,7 @@ }, "end-date": { "title": "End Date", - "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "description": "The end date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000`", "instillShortDescription": "The end date of the sprint", "instillUIOrder": 4, "instillFormat": "string", @@ -1347,7 +1347,7 @@ }, "start-date": { "title": "Start Date", - "description": "The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "description": "The start date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000`", "instillShortDescription": "The start date of the sprint", "instillUIOrder": 3, "instillFormat": "string", @@ -1363,7 +1363,7 @@ }, "end-date": { "title": "End Date", - "description": "The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z", + "description": "The end date of the sprint, e.g. 2018-03-05 (default 00:00 UTC+0). To adjust your time zone, please provide a more detailed RFC3339 format like `2018-03-05T09:00:00Z0000`", "instillShortDescription": "The end date of the sprint", "instillUIOrder": 4, "instillFormat": "string", diff --git a/application/jira/v0/sprint.go b/application/jira/v0/sprint.go index f6015975..cb95bf9b 100644 --- a/application/jira/v0/sprint.go +++ b/application/jira/v0/sprint.go @@ -179,6 +179,8 @@ func (jiraClient *Client) createSprintTask(ctx context.Context, props *structpb. if _, err := time.Parse(time.RFC3339, opt.StartDate); err != nil { if opt.StartDate == "" { opt.StartDate = time.Now().Format(time.RFC3339) + } else if _, err := time.Parse(time.RFC3339, opt.StartDate+"T00:00:00Z"); err == nil { + opt.StartDate = opt.StartDate + "T00:00:00.000Z" } else { return nil, errmsg.AddMessage( err, @@ -192,6 +194,8 @@ func (jiraClient *Client) createSprintTask(ctx context.Context, props *structpb. fmt.Errorf("end date is required"), "end date is required", ) + } else if _, err := time.Parse(time.RFC3339, opt.EndDate+"T00:00:00Z"); err == nil { + opt.EndDate = opt.EndDate + "T00:00:00.000Z" } else { return nil, errmsg.AddMessage( err, @@ -310,6 +314,8 @@ func (jiraClient *Client) updateSprintTask(ctx context.Context, props *structpb. if _, err := time.Parse(time.RFC3339, body.StartDate); err != nil { if body.StartDate == "" { body.StartDate = time.Now().Format(time.RFC3339) + } else if _, err := time.Parse(time.RFC3339, body.StartDate+"T00:00:00Z"); err == nil { + body.StartDate = body.StartDate + "T00:00:00.000Z" } else { return nil, errmsg.AddMessage( err, @@ -323,6 +329,8 @@ func (jiraClient *Client) updateSprintTask(ctx context.Context, props *structpb. fmt.Errorf("end date is required"), "end date is required", ) + } else if _, err := time.Parse(time.RFC3339, body.EndDate+"T00:00:00Z"); err == nil { + body.EndDate = body.EndDate + "T00:00:00.000Z" } else { return nil, errmsg.AddMessage( err, diff --git a/go.sum b/go.sum index 89a95cc1..7b2fb212 100644 --- a/go.sum +++ b/go.sum @@ -668,6 +668,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=