diff --git a/.github/ci.yml b/.github/ci.yml new file mode 100644 index 0000000..03fcc0b --- /dev/null +++ b/.github/ci.yml @@ -0,0 +1,43 @@ +name: CI + +on: + pull_request: + types: + - "opened" + - "synchronize" + paths-ignore: + - "README.md" + - ".gitignore" + +jobs: + lint: + runs-on: ubuntu-latest + env: + GOPATH: /home/runner/go + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 1.22 + - name: golangci-lint + uses: golangci/golangci-lint-action@v4 + with: + version: v1.54 + + test: + runs-on: ubuntu-latest + env: + GOPATH: /home/runner/go + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 1.22 + - name: Test + run: go test -v ./... + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..753842b --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3d157ff --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +# Go parameters +GOCMD=go +GOBUILD=$(GOCMD) build +GOFMT=$(GOCMD) fmt +GOVET=$(GOCMD) vet +GOTEST=$(GOCMD) test +BINARY_NAME=jira-translator-cdevents +PKG_DIR=./pkg/ + +all: fmt vet test build + +build: + CGO_ENABLED=0 GOOS=linux $(GOBUILD) -o $(BINARY_NAME) $(PKG_DIR) + +test: + $(GOTEST) -v ./... + +fmt: + $(GOFMT) ./... + +vet: + $(GOVET) ./... + +clean: + rm -f $(BINARY_NAME) \ No newline at end of file diff --git a/README.md b/README.md index c0a082e..6c0e255 100644 --- a/README.md +++ b/README.md @@ -1 +1,30 @@ -# jira-translator \ No newline at end of file +# Jira Translator CDEvents +A translator plugin for translating [Jira issue and worklog events](https://developer.atlassian.com/platform/forge/events-reference/jira/#jira-events) into [Continuous Operations Ticket events](https://github.com/cdevents/spec/blob/v0.4.1/continuous-operations.md#ticket). +This plugin is served using Hashicorp's [go-plugin](https://github.com/hashicorp/go-plugin/). + +The binary of this plugin is published with a release URL and is used by external applications like [cdevents/webhook-adapter](https://github.com/cdevents/webhook-adapter) + +The published plugin's binary can be downloaded and loaded by creating a new plugin client using HashiCorp's go-plugin, which manages the lifecycle of this plugin and establishes the RPC connection. + +### How to build locally +Run the `make` command from the project root directory, which creates a plugin's binary with the name `jira-translator-cdevents` +````make +make all +```` + +### Jira-CDEvents type mapping for translation +Below are the Jira events that currently have mappings with CDEvents and are supported by this translator. + +| CDEvent Type | Jira Event Type | +| :------------ |:-------------------| +| dev.cdevents.ticket.created| issue_created | +| dev.cdevents.ticket.updated | issue_updated | +| dev.cdevents.ticket.updated | issue_commented | +| dev.cdevents.ticket.updated | issue_assigned | +| dev.cdevents.ticket.updated | issue_work_logged | +| dev.cdevents.ticket.updated | issue_worklog_updated | +| dev.cdevents.ticket.updated | issue_worklog_deleted | +| dev.cdevents.ticket.closed | issue_generic | + +Note: +CDEvents do not have corresponding mapping with Jira Project, User, Configuration and Software related events, these events can be mapped using [Custom CDEvents](https://github.com/cdevents/spec/tree/v0.4.1/custom) \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4002e74 --- /dev/null +++ b/go.mod @@ -0,0 +1,48 @@ +module github.com/cdevents/jira-translator + +go 1.22.0 + +toolchain go1.22.5 + +replace github.com/cdevents/webhook-adapter => github.com/Nordix/webhook-cdevents-adapter v0.0.0-20240724114025-d156792a38ce + +require ( + github.com/cdevents/sdk-go v0.4.1 + github.com/cdevents/webhook-adapter v0.0.0-20240319203345-6d6c6c54f065 + github.com/go-playground/assert/v2 v2.2.0 + github.com/hashicorp/go-plugin v1.6.1 + go.uber.org/zap v1.27.0 +) + +require ( + github.com/cloudevents/sdk-go/v2 v2.15.2 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.18.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/go-hclog v1.6.2 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/package-url/packageurl-go v0.1.2 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect + google.golang.org/grpc v1.62.1 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1ce99b1 --- /dev/null +++ b/go.sum @@ -0,0 +1,121 @@ +github.com/Nordix/webhook-cdevents-adapter v0.0.0-20240724114025-d156792a38ce h1:EO9KcHXw7IN3Y+hO0nA4zRfKDokGN0sJG8PYmahGYEc= +github.com/Nordix/webhook-cdevents-adapter v0.0.0-20240724114025-d156792a38ce/go.mod h1:OVIMSvpfxW4BWRS5extnKJBCTTfx0Ntfs62fZuN8hok= +github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= +github.com/cdevents/sdk-go v0.4.1 h1:Cr/iH/I51Z+slxKRx9AV7stn6hr2pjRHQ5wpPJhRLTU= +github.com/cdevents/sdk-go v0.4.1/go.mod h1:3IhWLoY4vsyUEzv7XJbyr0BRQ0KPgvNx+wiD2hQGFNU= +github.com/cloudevents/sdk-go/v2 v2.15.2 h1:54+I5xQEnI73RBhWHxbI1XJcqOFOVJN85vb41+8mHUc= +github.com/cloudevents/sdk-go/v2 v2.15.2/go.mod h1:lL7kSWAE/V8VI4Wh0jbL2v/jvqsm6tjmaQBSvxcv4uE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +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/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= +github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= +github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= +github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +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/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/package-url/packageurl-go v0.1.2 h1:0H2DQt6DHd/NeRlVwW4EZ4oEI6Bn40XlNPRqegcxuo4= +github.com/package-url/packageurl-go v0.1.2/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/jira/create_cdevents.go b/pkg/jira/create_cdevents.go new file mode 100644 index 0000000..9b383a3 --- /dev/null +++ b/pkg/jira/create_cdevents.go @@ -0,0 +1,123 @@ +/* +Copyright (C) 2024 Nordix Foundation. +For a full list of individual contributors, please see the commit history. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +SPDX-License-Identifier: Apache-2.0 +*/ + +package jira + +import ( + cdevents "github.com/cdevents/sdk-go/pkg/api" + cdevents04 "github.com/cdevents/sdk-go/pkg/api/v04" +) + +func (issueCreatedEvent *IssueCreatedEvent) TicketCreatedCDEvent() (string, error) { + Log().Info("Creating dev.cdevents.ticket.created for Jira ", issueCreatedEvent.IssueEventTypeName) + cdEvent, err := cdevents04.NewTicketCreatedEvent() + if err != nil { + Log().Error("Error creating CDEvent TicketCreatedEvent, ", err) + return "", err + } + + cdEvent.SetSource(issueCreatedEvent.Issue.Fields.Status.IconURL) + cdEvent.SetSubjectId(issueCreatedEvent.Issue.ID) + cdEvent.SetSubjectUri(issueCreatedEvent.Issue.Self) + cdEvent.SetSubjectSummary(issueCreatedEvent.Issue.Fields.Summary) + cdEvent.SetSubjectCreator(issueCreatedEvent.Issue.Fields.Creator.Name) + + cdEventStr, err := cdevents.AsJsonString(cdEvent) + if err != nil { + Log().Error("Error creating TicketCreatedEvent CDEvent as Json string, ", err) + return "", err + } + + return cdEventStr, nil +} + +func (issueUpdatedEvent *IssueUpdatedEvent) TicketUpdatedCDEvent() (string, error) { + Log().Info("Creating dev.cdevents.ticket.updated for Jira ", issueUpdatedEvent.IssueEventTypeName) + return createTicketUpdatedCDEvent( + issueUpdatedEvent.Issue.Fields.Status.IconURL, + issueUpdatedEvent.Issue.ID, + issueUpdatedEvent.Issue.Self, + issueUpdatedEvent.Issue.Fields.Creator.Name, + issueUpdatedEvent.Changelog) +} + +func (issueCommentedEvent *IssueCommentedEvent) TicketUpdatedCDEvent() (string, error) { + Log().Info("Creating dev.cdevents.ticket.updated for Jira ", issueCommentedEvent.IssueEventTypeName) + return createTicketUpdatedCDEvent( + issueCommentedEvent.Issue.Fields.Status.IconURL, + issueCommentedEvent.Issue.ID, + issueCommentedEvent.Issue.Self, + issueCommentedEvent.Issue.Fields.Creator.Name, + issueCommentedEvent.Comment) +} + +func (issueAssignedEvent *IssueAssignedEvent) TicketUpdatedCDEvent() (string, error) { + Log().Info("Creating dev.cdevents.ticket.updated for Jira ", issueAssignedEvent.IssueEventTypeName) + return createTicketUpdatedCDEvent( + issueAssignedEvent.Issue.Fields.Status.IconURL, + issueAssignedEvent.Issue.ID, + issueAssignedEvent.Issue.Self, + issueAssignedEvent.Issue.Fields.Creator.Name, + issueAssignedEvent.Changelog) +} + +func createTicketUpdatedCDEvent(source, id, uri, updatedBy string, customData interface{}) (string, error) { + cdEvent, err := cdevents04.NewTicketUpdatedEvent() + if err != nil { + Log().Error("Error creating CDEvent TicketUpdatedEvent, ", err) + return "", err + } + + cdEvent.SetSource(source) + cdEvent.SetSubjectId(id) + cdEvent.SetSubjectUri(uri) + cdEvent.SetSubjectUpdatedBy(updatedBy) + cdEvent.SetCustomData("application/json", customData) + + cdEventStr, err := cdevents.AsJsonString(cdEvent) + if err != nil { + Log().Error("Error creating TicketUpdatedEvent CDEvent as Json string, ", err) + return "", err + } + + return cdEventStr, nil +} + +func (issueGenericEvent *IssueGenericEvent) TicketClosedCDEvent() (string, error) { + Log().Info("Creating dev.cdevents.ticket.closed for Jira ", issueGenericEvent.IssueEventTypeName) + cdEvent, err := cdevents04.NewTicketClosedEvent() + if err != nil { + Log().Error("Error creating CDEvent TicketClosedEvent, ", err) + return "", err + } + + cdEvent.SetSource(issueGenericEvent.Issue.Fields.Status.IconURL) + cdEvent.SetSubjectId(issueGenericEvent.Issue.ID) + cdEvent.SetSubjectUri(issueGenericEvent.Issue.Self) + cdEvent.SetSubjectResolution(issueGenericEvent.Issue.Fields.Resolution.Name) + cdEvent.SetSubjectSummary(issueGenericEvent.Issue.Fields.Summary) + cdEvent.SetSubjectCreator(issueGenericEvent.Issue.Fields.Creator.Name) + cdEvent.SetCustomData("application/json", issueGenericEvent.Changelog) + + cdEventStr, err := cdevents.AsJsonString(cdEvent) + if err != nil { + Log().Error("Error creating TicketClosedEvent CDEvent as Json string, ", err) + return "", err + } + + return cdEventStr, nil +} diff --git a/pkg/jira/event.go b/pkg/jira/event.go new file mode 100644 index 0000000..4bbc013 --- /dev/null +++ b/pkg/jira/event.go @@ -0,0 +1,89 @@ +/* +Copyright (C) 2024 Nordix Foundation. +For a full list of individual contributors, please see the commit history. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +SPDX-License-Identifier: Apache-2.0 +*/ + +package jira + +import ( + "encoding/json" + "fmt" + "net/http" +) + +type JiraEvent struct { + Event string +} + +func NewJiraEvent(event string) (pEvent *JiraEvent) { + pEvent = &JiraEvent{event} + return +} + +func HandleTranslateJiraEvent(event string, header http.Header) (string, error) { + Log().Debug("Handle translation into CDEvent from Jira event %s\n", event) + jiraEvent := NewJiraEvent(event) + cdEvent, err := jiraEvent.TranslateIntoCDEvent() + if err != nil { + Log().Error("Error translating Jira event into CDEvent, ", err) + return "", err + } + Log().Info("Jira Event translated into CDEvent", cdEvent) + return cdEvent, nil +} + +func (pEvent *JiraEvent) TranslateIntoCDEvent() (string, error) { + eventMap := make(map[string]interface{}) + cdEvent := "" + err := json.Unmarshal([]byte(pEvent.Event), &eventMap) + if err != nil { + Log().Error("Error occurred while Unmarshal jiraEvent data into eventMap", err) + return "", err + } + eventType := eventMap["issue_event_type_name"] + Log().Info("handling translating to CDEvent from Jira event type name: ", eventType) + + switch eventType { + case "issue_created": + cdEvent, err = pEvent.HandleIssueCreatedEvent() + if err != nil { + return "", err + } + case "issue_updated", "issue_work_logged", "issue_worklog_updated", "issue_worklog_deleted": + cdEvent, err = pEvent.HandleIssueUpdatedEvent(eventType.(string)) + if err != nil { + return "", err + } + case "issue_commented": + cdEvent, err = pEvent.HandleIssueCommentedEvent() + if err != nil { + return "", err + } + case "issue_assigned": + cdEvent, err = pEvent.HandleIssueAssignedEvent() + if err != nil { + return "", err + } + case "issue_generic": + cdEvent, err = pEvent.HandleIssueGenericEvent() + if err != nil { + return "", err + } + default: + Log().Info("Not handling CDEvent translation for Jira event type: ", eventType) + return "", fmt.Errorf("jira event type %q, not supported for translation", eventType) + } + return cdEvent, nil +} diff --git a/pkg/jira/event_test.go b/pkg/jira/event_test.go new file mode 100644 index 0000000..2df6639 --- /dev/null +++ b/pkg/jira/event_test.go @@ -0,0 +1,129 @@ +package jira + +import ( + "net/http" + "os" + "testing" + + cdevents "github.com/cdevents/sdk-go/pkg/api" + cdevents04 "github.com/cdevents/sdk-go/pkg/api/v04" + assert "github.com/go-playground/assert/v2" +) + +func TestHandleTranslateIssueCreatedEvent(t *testing.T) { + newCDEvent, err := translateJiraEvent("testdata/issue_created.json") + if err != nil { + t.Errorf("Expected translate Jira issue_created into dev.cdevents.ticket.created to be successful") + return + } + assert.Equal(t, newCDEvent.GetSource(), "http://localhost:9090/") + assert.Equal(t, newCDEvent.GetSubjectId(), "10023") + assert.Equal(t, newCDEvent.GetSubject().GetSubjectType().String(), "ticket") + assert.Equal(t, newCDEvent.GetType().UnversionedString(), "dev.cdevents.ticket.created") +} + +func TestHandleTranslateIssueUpdatedEvent(t *testing.T) { + newCDEvent, err := translateJiraEvent("testdata/issue_updated.json") + if err != nil { + t.Errorf("Expected translate Jira issue_updated into dev.cdevents.ticket.updated to be successful") + return + } + assert.Equal(t, newCDEvent.GetSource(), "http://localhost:9090/") + assert.Equal(t, newCDEvent.GetSubjectId(), "10023") + assert.Equal(t, newCDEvent.GetSubject().GetSubjectType().String(), "ticket") + assert.Equal(t, newCDEvent.GetType().UnversionedString(), "dev.cdevents.ticket.updated") +} + +func TestHandleTranslateIssueCommentedEvent(t *testing.T) { + newCDEvent, err := translateJiraEvent("testdata/issue_commented.json") + if err != nil { + t.Errorf("Expected translate Jira issue_commented into dev.cdevents.ticket.updated to be successful") + return + } + assert.Equal(t, newCDEvent.GetSource(), "http://localhost:9090/") + assert.Equal(t, newCDEvent.GetSubjectId(), "10023") + assert.Equal(t, newCDEvent.GetSubject().GetSubjectType().String(), "ticket") + assert.Equal(t, newCDEvent.GetType().UnversionedString(), "dev.cdevents.ticket.updated") +} + +func TestHandleTranslateIssueAssignedEvent(t *testing.T) { + newCDEvent, err := translateJiraEvent("testdata/issue_assigned.json") + if err != nil { + t.Errorf("Expected translate Jira issue_assigned into dev.cdevents.ticket.updated to be successful") + return + } + assert.Equal(t, newCDEvent.GetSource(), "http://localhost:9090/") + assert.Equal(t, newCDEvent.GetSubjectId(), "10023") + assert.Equal(t, newCDEvent.GetSubject().GetSubjectType().String(), "ticket") + assert.Equal(t, newCDEvent.GetType().UnversionedString(), "dev.cdevents.ticket.updated") +} + +func TestHandleTranslateIssueWorkLoggedEvent(t *testing.T) { + newCDEvent, err := translateJiraEvent("testdata/issue_work_logged.json") + if err != nil { + t.Errorf("Expected translate Jira issue_work_logged into dev.cdevents.ticket.updated to be successful") + return + } + assert.Equal(t, newCDEvent.GetSource(), "http://localhost:9090/") + assert.Equal(t, newCDEvent.GetSubjectId(), "10023") + assert.Equal(t, newCDEvent.GetSubject().GetSubjectType().String(), "ticket") + assert.Equal(t, newCDEvent.GetType().UnversionedString(), "dev.cdevents.ticket.updated") +} + +func TestHandleTranslateIssueWorklogUpdatedEvent(t *testing.T) { + newCDEvent, err := translateJiraEvent("testdata/issue_worklog_updated.json") + if err != nil { + t.Errorf("Expected translate Jira issue_worklog_updated into dev.cdevents.ticket.updated to be successful") + return + } + assert.Equal(t, newCDEvent.GetSource(), "http://localhost:9090/") + assert.Equal(t, newCDEvent.GetSubjectId(), "10023") + assert.Equal(t, newCDEvent.GetSubject().GetSubjectType().String(), "ticket") + assert.Equal(t, newCDEvent.GetType().UnversionedString(), "dev.cdevents.ticket.updated") +} + +func TestHandleTranslateIssueWorklogDeletedEvent(t *testing.T) { + newCDEvent, err := translateJiraEvent("testdata/issue_worklog_deleted.json") + if err != nil { + t.Errorf("Expected translate Jira issue_worklog_deleted into dev.cdevents.ticket.updated to be successful") + return + } + assert.Equal(t, newCDEvent.GetSource(), "http://localhost:9090/") + assert.Equal(t, newCDEvent.GetSubjectId(), "10023") + assert.Equal(t, newCDEvent.GetSubject().GetSubjectType().String(), "ticket") + assert.Equal(t, newCDEvent.GetType().UnversionedString(), "dev.cdevents.ticket.updated") +} + +func TestHandleTranslateIssueGenericClosedEvent(t *testing.T) { + newCDEvent, err := translateJiraEvent("testdata/issue_generic.json") + if err != nil { + t.Errorf("Expected translate Jira issue_generic into dev.cdevents.ticket.closed to be successful") + return + } + assert.Equal(t, newCDEvent.GetSource(), "http://localhost:9090/") + assert.Equal(t, newCDEvent.GetSubjectId(), "10008") + assert.Equal(t, newCDEvent.GetSubject().GetSubjectType().String(), "ticket") + assert.Equal(t, newCDEvent.GetType().UnversionedString(), "dev.cdevents.ticket.closed") +} + +func translateJiraEvent(testFile string) (cdevents.CDEventV04, error) { + event, err := os.ReadFile(testFile) + if err != nil { + Log().Errorf("Failed to read %s file: %v", testFile, err) + return nil, err + } + headers := http.Header{} + headers.Set("X-Origin-Url", "http://jira.est.tech") + + cdEvent, err := HandleTranslateJiraEvent(string(event), headers) + if err != nil { + Log().Errorf("Failed to translate Jira Event into CDEvent: %v", err) + return nil, err + } + newCDEvent, err := cdevents04.NewFromJsonString(cdEvent) + if err != nil { + Log().Errorf("Failed to create CDEvent as Json string: %v", err) + return nil, err + } + return newCDEvent, nil +} diff --git a/pkg/jira/event_types.go b/pkg/jira/event_types.go new file mode 100644 index 0000000..83a997d --- /dev/null +++ b/pkg/jira/event_types.go @@ -0,0 +1,276 @@ +/* +Copyright (C) 2024 Nordix Foundation. +For a full list of individual contributors, please see the commit history. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +SPDX-License-Identifier: Apache-2.0 +*/ + +package jira + +// Define structs for nested JSON objects +type AvatarUrls struct { + Four8X48 string `json:"48x48"` + Two4X24 string `json:"24x24"` + One6X16 string `json:"16x16"` + Three2X32 string `json:"32x32"` +} +type User struct { + Self string `json:"self"` + Name string `json:"name"` + Key string `json:"key"` + EmailAddress string `json:"emailAddress"` + AvatarUrls AvatarUrls `json:"avatarUrls"` + DisplayName string `json:"displayName"` + Active bool `json:"active"` + TimeZone string `json:"timeZone"` +} +type Issuetype struct { + Self string `json:"self"` + ID string `json:"id"` + Description string `json:"description"` + IconURL string `json:"iconUrl"` + Name string `json:"name"` + Subtask bool `json:"subtask"` +} +type Project struct { + Self string `json:"self"` + ID string `json:"id"` + Key string `json:"key"` + Name string `json:"name"` + ProjectTypeKey string `json:"projectTypeKey"` + AvatarUrls AvatarUrls `json:"avatarUrls"` +} +type Watches struct { + Self string `json:"self"` + WatchCount int `json:"watchCount"` + IsWatching bool `json:"isWatching"` +} +type Priority struct { + Self string `json:"self"` + IconURL string `json:"iconUrl"` + Name string `json:"name"` + ID string `json:"id"` +} +type Assignee struct { + Self string `json:"self"` + Name string `json:"name"` + Key string `json:"key"` + EmailAddress string `json:"emailAddress"` + AvatarUrls AvatarUrls `json:"avatarUrls"` + DisplayName string `json:"displayName"` + Active bool `json:"active"` + TimeZone string `json:"timeZone"` +} +type StatusCategory struct { + Self string `json:"self"` + ID int `json:"id"` + Key string `json:"key"` + ColorName string `json:"colorName"` + Name string `json:"name"` +} +type Status struct { + Self string `json:"self"` + Description string `json:"description"` + IconURL string `json:"iconUrl"` + Name string `json:"name"` + ID string `json:"id"` + StatusCategory StatusCategory `json:"statusCategory"` +} +type Timetracking struct { +} +type Creator struct { + Self string `json:"self"` + Name string `json:"name"` + Key string `json:"key"` + EmailAddress string `json:"emailAddress"` + AvatarUrls AvatarUrls `json:"avatarUrls"` + DisplayName string `json:"displayName"` + Active bool `json:"active"` + TimeZone string `json:"timeZone"` +} +type Reporter struct { + Self string `json:"self"` + Name string `json:"name"` + Key string `json:"key"` + EmailAddress string `json:"emailAddress"` + AvatarUrls AvatarUrls `json:"avatarUrls"` + DisplayName string `json:"displayName"` + Active bool `json:"active"` + TimeZone string `json:"timeZone"` +} +type Aggregateprogress struct { + Progress int `json:"progress"` + Total int `json:"total"` +} +type Progress struct { + Progress int `json:"progress"` + Total int `json:"total"` +} +type Author struct { + Self string `json:"self"` + Name string `json:"name"` + Key string `json:"key"` + EmailAddress string `json:"emailAddress"` + AvatarUrls AvatarUrls `json:"avatarUrls"` + DisplayName string `json:"displayName"` + Active bool `json:"active"` + TimeZone string `json:"timeZone"` +} +type UpdateAuthor struct { + Self string `json:"self"` + Name string `json:"name"` + Key string `json:"key"` + EmailAddress string `json:"emailAddress"` + AvatarUrls AvatarUrls `json:"avatarUrls"` + DisplayName string `json:"displayName"` + Active bool `json:"active"` + TimeZone string `json:"timeZone"` +} +type Comments struct { + Self string `json:"self"` + ID string `json:"id"` + Author Author `json:"author"` + Body string `json:"body"` + UpdateAuthor UpdateAuthor `json:"updateAuthor"` + Created string `json:"created"` + Updated string `json:"updated"` +} +type Comment struct { + Comments []Comments `json:"comments"` + MaxResults int `json:"maxResults"` + Total int `json:"total"` + StartAt int `json:"startAt"` +} +type Votes struct { + Self string `json:"self"` + Votes int `json:"votes"` + HasVoted bool `json:"hasVoted"` +} +type Worklog struct { + StartAt int `json:"startAt"` + MaxResults int `json:"maxResults"` + Total int `json:"total"` + Worklogs []any `json:"worklogs"` +} +type Resolution struct { + Self string `json:"self"` + Id string `json:"id"` + Description string `json:"description"` + Name string `json:"name"` +} +type Fields struct { + Issuetype Issuetype `json:"issuetype"` + Timespent any `json:"timespent"` + Project Project `json:"project"` + FixVersions []any `json:"fixVersions"` + Customfield10110 string `json:"customfield_10110"` + Customfield10111 any `json:"customfield_10111"` + Aggregatetimespent any `json:"aggregatetimespent"` + Resolution Resolution `json:"resolution"` + Customfield10107 any `json:"customfield_10107"` + Customfield10108 any `json:"customfield_10108"` + Customfield10109 any `json:"customfield_10109"` + Resolutiondate any `json:"resolutiondate"` + Workratio int `json:"workratio"` + LastViewed any `json:"lastViewed"` + Watches Watches `json:"watches"` + Created string `json:"created"` + Priority Priority `json:"priority"` + Customfield10100 any `json:"customfield_10100"` + Customfield10101 any `json:"customfield_10101"` + Customfield10102 any `json:"customfield_10102"` + Labels []any `json:"labels"` + Customfield10103 any `json:"customfield_10103"` + Timeestimate any `json:"timeestimate"` + Aggregatetimeoriginalestimate any `json:"aggregatetimeoriginalestimate"` + Versions []any `json:"versions"` + Issuelinks []any `json:"issuelinks"` + Assignee Assignee `json:"assignee"` + Updated string `json:"updated"` + Status Status `json:"status"` + Components []any `json:"components"` + Timeoriginalestimate any `json:"timeoriginalestimate"` + Description string `json:"description"` + Timetracking Timetracking `json:"timetracking"` + Archiveddate any `json:"archiveddate"` + Attachment []any `json:"attachment"` + Aggregatetimeestimate any `json:"aggregatetimeestimate"` + Summary string `json:"summary"` + Creator Creator `json:"creator"` + Subtasks []any `json:"subtasks"` + Reporter Reporter `json:"reporter"` + Customfield10000 string `json:"customfield_10000"` + Aggregateprogress Aggregateprogress `json:"aggregateprogress"` + Environment any `json:"environment"` + Duedate any `json:"duedate"` + Progress Progress `json:"progress"` + Comment Comment `json:"comment"` + Votes Votes `json:"votes"` + Worklog Worklog `json:"worklog"` + Archivedby any `json:"archivedby"` +} +type Issue struct { + ID string `json:"id"` + Self string `json:"self"` + Key string `json:"key"` + Fields Fields `json:"fields"` +} +type Items struct { + Field string `json:"field"` + Fieldtype string `json:"fieldtype"` + From any `json:"from"` + FromString string `json:"fromString"` + To any `json:"to"` + ToString string `json:"toString"` +} +type Changelog struct { + ID string `json:"id"` + Items []Items `json:"items"` +} + +// Jira event types +type IssueCreatedEvent struct { + Timestamp int64 `json:"timestamp"` + WebhookEvent string `json:"webhookEvent"` + IssueEventTypeName string `json:"issue_event_type_name"` + User User `json:"user"` + Issue Issue `json:"issue"` +} +type IssueUpdatedEvent struct { + Timestamp int64 `json:"timestamp"` + WebhookEvent string `json:"webhookEvent"` + IssueEventTypeName string `json:"issue_event_type_name"` + User User `json:"user"` + Issue Issue `json:"issue"` + Changelog Changelog `json:"changelog"` +} +type IssueCommentedEvent struct { + Timestamp int64 `json:"timestamp"` + WebhookEvent string `json:"webhookEvent"` + IssueEventTypeName string `json:"issue_event_type_name"` + User User `json:"user"` + Issue Issue `json:"issue"` + Comment Comments `json:"comment"` +} +type IssueAssignedEvent struct { + Timestamp int64 `json:"timestamp"` + WebhookEvent string `json:"webhookEvent"` + IssueEventTypeName string `json:"issue_event_type_name"` + User User `json:"user"` + Issue Issue `json:"issue"` + Changelog Changelog `json:"changelog"` + Comment Comments `json:"comment"` +} +type IssueGenericEvent struct { + IssueUpdatedEvent +} diff --git a/pkg/jira/logger.go b/pkg/jira/logger.go new file mode 100644 index 0000000..6c692dc --- /dev/null +++ b/pkg/jira/logger.go @@ -0,0 +1,41 @@ +package jira + +import ( + "errors" + "syscall" + + "go.uber.org/zap" +) + +var sugar *zap.SugaredLogger + +// init Initialize a new production zap logger +func init() { + logger, err := zap.NewProduction() + if err != nil { + panic("failed to initialize logger: " + err.Error()) + } + defer func(logger *zap.Logger) { + err := Sync(sugar) + if err != nil { + panic("failed to initialize logger Sync: " + err.Error()) + } + }(logger) + sugar = logger.Sugar() +} + +func Sync(logger *zap.SugaredLogger) error { + err := logger.Sync() + if !errors.Is(err, syscall.EINVAL) { + return err + } + return nil +} + +// Log returns the zap logger If initialized +func Log() *zap.SugaredLogger { + if sugar == nil { + panic("zap logger is not initialized") + } + return sugar +} diff --git a/pkg/jira/testdata/issue_assigned.json b/pkg/jira/testdata/issue_assigned.json new file mode 100644 index 0000000..a42dca7 --- /dev/null +++ b/pkg/jira/testdata/issue_assigned.json @@ -0,0 +1,290 @@ +{ + "timestamp": 1717163183565, + "webhookEvent": "jira:issue_updated", + "issue_event_type_name": "issue_assigned", + "user": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "issue": { + "id": "10023", + "self": "http://localhost:9090/rest/api/2/issue/10023", + "key": "ES-24", + "fields": { + "issuetype": { + "self": "http://localhost:9090/rest/api/2/issuetype/10002", + "id": "10002", + "description": "Created by Jira Software - do not edit or delete. Issue type for a user story.", + "iconUrl": "http://localhost:9090/images/icons/issuetypes/story.svg", + "name": "Story", + "subtask": false + }, + "timespent": null, + "project": { + "self": "http://localhost:9090/rest/api/2/project/10000", + "id": "10000", + "key": "ES", + "name": "EST-SeaTel", + "projectTypeKey": "software", + "avatarUrls": { + "48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324", + "24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324", + "16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324", + "32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324" + } + }, + "fixVersions": [], + "customfield_10110": "0i00053:", + "customfield_10111": null, + "aggregatetimespent": null, + "resolution": null, + "customfield_10107": null, + "customfield_10108": null, + "customfield_10109": null, + "resolutiondate": null, + "workratio": -1, + "lastViewed": "2024-05-31T14:46:23.243+0100", + "watches": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/watchers", + "watchCount": 1, + "isWatching": true + }, + "created": "2024-05-29T15:04:18.022+0100", + "priority": { + "self": "http://localhost:9090/rest/api/2/priority/3", + "iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg", + "name": "Medium", + "id": "3" + }, + "customfield_10100": null, + "customfield_10101": null, + "customfield_10102": null, + "labels": [], + "customfield_10103": null, + "timeestimate": null, + "aggregatetimeoriginalestimate": null, + "versions": [], + "issuelinks": [], + "assignee": null, + "updated": "2024-05-31T14:46:23.467+0100", + "status": { + "self": "http://localhost:9090/rest/api/2/status/10000", + "description": "", + "iconUrl": "http://localhost:9090/", + "name": "To Do", + "id": "10000", + "statusCategory": { + "self": "http://localhost:9090/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "default", + "name": "To Do" + } + }, + "components": [], + "timeoriginalestimate": null, + "description": "CDEvents integration with Jira updates needed", + "timetracking": {}, + "archiveddate": null, + "attachment": [], + "aggregatetimeestimate": null, + "summary": "CDEvents integration with Jira", + "creator": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "subtasks": [], + "reporter": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "customfield_10000": "{summaryBean=com.atlassian.jira.plugin.devstatus.rest.SummaryBean@5bb224de[summary={pullrequest=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@67fcd09d[byInstanceType={},overall=PullRequestOverallBean{stateCount=0, state='OPEN', details=PullRequestOverallDetails{openCount=0, mergedCount=0, declinedCount=0}}], build=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@3a883ec7[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BuildOverallBean@3bf17933[failedBuildCount=0,successfulBuildCount=0,unknownBuildCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], review=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@64eb3b4f[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.ReviewsOverallBean@344a5f8[dueDate=,overDue=false,state=,stateCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], deployment-environment=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@7841dd51[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.DeploymentOverallBean@48c90b8b[showProjects=false,successfulCount=0,topEnvironments=[],count=0,lastUpdated=,lastUpdatedTimestamp=]], repository=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@7545609e[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.CommitOverallBean@530860ab[count=0,lastUpdated=,lastUpdatedTimestamp=]], branch=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@25d594ce[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BranchOverallBean@25c056ac[count=0,lastUpdated=,lastUpdatedTimestamp=]]},configErrors=[],errors=[]], devSummaryJson={\"cachedValue\":{\"errors\":[],\"configErrors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":\"OPEN\",\"details\":{\"openCount\":0,\"mergedCount\":0,\"declinedCount\":0,\"total\":0},\"open\":true},\"byInstanceType\":{}},\"build\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"failedBuildCount\":0,\"successfulBuildCount\":0,\"unknownBuildCount\":0},\"byInstanceType\":{}},\"review\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":null,\"dueDate\":null,\"overDue\":false,\"completed\":false},\"byInstanceType\":{}},\"deployment-environment\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"topEnvironments\":[],\"showProjects\":false,\"successfulCount\":0},\"byInstanceType\":{}},\"repository\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}},\"branch\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}}}},\"isStale\":false}}", + "aggregateprogress": { + "progress": 0, + "total": 0 + }, + "environment": null, + "duedate": null, + "progress": { + "progress": 0, + "total": 0 + }, + "comment": { + "comments": [ + { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10019", + "id": "10019", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "is the issue fixed?", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-29T15:22:43.937+0100", + "updated": "2024-05-29T15:22:43.937+0100" + }, + { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10100", + "id": "10100", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "updating to Unassigned", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-31T14:46:23.440+0100", + "updated": "2024-05-31T14:46:23.440+0100" + } + ], + "maxResults": 2, + "total": 2, + "startAt": 0 + }, + "votes": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/votes", + "votes": 0, + "hasVoted": false + }, + "worklog": { + "startAt": 0, + "maxResults": 20, + "total": 0, + "worklogs": [] + }, + "archivedby": null + } + }, + "changelog": { + "id": "10100", + "items": [ + { + "field": "assignee", + "fieldtype": "jira", + "from": "JIRAUSER10000", + "fromString": "jalander.ramagiri@est.tech", + "to": null, + "toString": null + } + ] + }, + "comment": { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10100", + "id": "10100", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "updating to Unassigned", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-31T14:46:23.440+0100", + "updated": "2024-05-31T14:46:23.440+0100" + } +} \ No newline at end of file diff --git a/pkg/jira/testdata/issue_commented.json b/pkg/jira/testdata/issue_commented.json new file mode 100644 index 0000000..1923fb6 --- /dev/null +++ b/pkg/jira/testdata/issue_commented.json @@ -0,0 +1,255 @@ +{ + "timestamp": 1716992564052, + "webhookEvent": "jira:issue_updated", + "issue_event_type_name": "issue_commented", + "user": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "issue": { + "id": "10023", + "self": "http://localhost:9090/rest/api/2/issue/10023", + "key": "ES-24", + "fields": { + "issuetype": { + "self": "http://localhost:9090/rest/api/2/issuetype/10002", + "id": "10002", + "description": "Created by Jira Software - do not edit or delete. Issue type for a user story.", + "iconUrl": "http://localhost:9090/images/icons/issuetypes/story.svg", + "name": "Story", + "subtask": false + }, + "timespent": null, + "project": { + "self": "http://localhost:9090/rest/api/2/project/10000", + "id": "10000", + "key": "ES", + "name": "EST-SeaTel", + "projectTypeKey": "software", + "avatarUrls": { + "48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324", + "24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324", + "16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324", + "32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324" + } + }, + "fixVersions": [], + "customfield_10110": "0i00053:", + "customfield_10111": null, + "aggregatetimespent": null, + "resolution": null, + "customfield_10107": null, + "customfield_10108": null, + "customfield_10109": null, + "resolutiondate": null, + "workratio": -1, + "lastViewed": "2024-05-29T15:19:47.733+0100", + "watches": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/watchers", + "watchCount": 1, + "isWatching": true + }, + "created": "2024-05-29T15:04:18.022+0100", + "priority": { + "self": "http://localhost:9090/rest/api/2/priority/3", + "iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg", + "name": "Medium", + "id": "3" + }, + "customfield_10100": null, + "customfield_10101": null, + "customfield_10102": null, + "labels": [], + "customfield_10103": null, + "timeestimate": null, + "aggregatetimeoriginalestimate": null, + "versions": [], + "issuelinks": [], + "assignee": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "updated": "2024-05-29T15:22:43.937+0100", + "status": { + "self": "http://localhost:9090/rest/api/2/status/10000", + "description": "", + "iconUrl": "http://localhost:9090/", + "name": "To Do", + "id": "10000", + "statusCategory": { + "self": "http://localhost:9090/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "default", + "name": "To Do" + } + }, + "components": [], + "timeoriginalestimate": null, + "description": "CDEvents integration with Jira updates needed", + "timetracking": {}, + "archiveddate": null, + "attachment": [], + "aggregatetimeestimate": null, + "summary": "CDEvents integration with Jira", + "creator": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "subtasks": [], + "reporter": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "customfield_10000": "{summaryBean=com.atlassian.jira.plugin.devstatus.rest.SummaryBean@451a472c[summary={pullrequest=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@f57ab5c[byInstanceType={},overall=PullRequestOverallBean{stateCount=0, state='OPEN', details=PullRequestOverallDetails{openCount=0, mergedCount=0, declinedCount=0}}], build=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@507b8518[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BuildOverallBean@62c4d900[failedBuildCount=0,successfulBuildCount=0,unknownBuildCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], review=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@259bef5a[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.ReviewsOverallBean@1e20a2da[dueDate=,overDue=false,state=,stateCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], deployment-environment=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@127fa743[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.DeploymentOverallBean@563760d5[showProjects=false,successfulCount=0,topEnvironments=[],count=0,lastUpdated=,lastUpdatedTimestamp=]], repository=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@2caca476[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.CommitOverallBean@3701f54b[count=0,lastUpdated=,lastUpdatedTimestamp=]], branch=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@582b0957[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BranchOverallBean@3577fb4f[count=0,lastUpdated=,lastUpdatedTimestamp=]]},configErrors=[],errors=[]], devSummaryJson={\"cachedValue\":{\"errors\":[],\"configErrors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":\"OPEN\",\"details\":{\"openCount\":0,\"mergedCount\":0,\"declinedCount\":0,\"total\":0},\"open\":true},\"byInstanceType\":{}},\"build\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"failedBuildCount\":0,\"successfulBuildCount\":0,\"unknownBuildCount\":0},\"byInstanceType\":{}},\"review\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":null,\"dueDate\":null,\"overDue\":false,\"completed\":false},\"byInstanceType\":{}},\"deployment-environment\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"topEnvironments\":[],\"showProjects\":false,\"successfulCount\":0},\"byInstanceType\":{}},\"repository\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}},\"branch\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}}}},\"isStale\":false}}", + "aggregateprogress": { + "progress": 0, + "total": 0 + }, + "environment": null, + "duedate": null, + "progress": { + "progress": 0, + "total": 0 + }, + "comment": { + "comments": [ + { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10019", + "id": "10019", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "is the issue fixed?", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-29T15:22:43.937+0100", + "updated": "2024-05-29T15:22:43.937+0100" + } + ], + "maxResults": 1, + "total": 1, + "startAt": 0 + }, + "votes": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/votes", + "votes": 0, + "hasVoted": false + }, + "worklog": { + "startAt": 0, + "maxResults": 20, + "total": 0, + "worklogs": [] + }, + "archivedby": null + } + }, + "comment": { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10019", + "id": "10019", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "is the issue fixed?", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-29T15:22:43.937+0100", + "updated": "2024-05-29T15:22:43.937+0100" + } + } + \ No newline at end of file diff --git a/pkg/jira/testdata/issue_created.json b/pkg/jira/testdata/issue_created.json new file mode 100644 index 0000000..6342a9e --- /dev/null +++ b/pkg/jira/testdata/issue_created.json @@ -0,0 +1,179 @@ +{ + "timestamp": 1716991458232, + "webhookEvent": "jira:issue_created", + "issue_event_type_name": "issue_created", + "user": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "issue": { + "id": "10023", + "self": "http://localhost:9090/rest/api/2/issue/10023", + "key": "ES-24", + "fields": { + "issuetype": { + "self": "http://localhost:9090/rest/api/2/issuetype/10002", + "id": "10002", + "description": "Created by Jira Software - do not edit or delete. Issue type for a user story.", + "iconUrl": "http://localhost:9090/images/icons/issuetypes/story.svg", + "name": "Story", + "subtask": false + }, + "timespent": null, + "project": { + "self": "http://localhost:9090/rest/api/2/project/10000", + "id": "10000", + "key": "ES", + "name": "EST-SeaTel", + "projectTypeKey": "software", + "avatarUrls": { + "48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324", + "24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324", + "16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324", + "32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324" + } + }, + "fixVersions": [], + "customfield_10110": "0i00053:", + "customfield_10111": null, + "aggregatetimespent": null, + "resolution": null, + "customfield_10107": null, + "customfield_10108": null, + "customfield_10109": null, + "resolutiondate": null, + "workratio": -1, + "lastViewed": null, + "watches": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/watchers", + "watchCount": 0, + "isWatching": false + }, + "created": "2024-05-29T15:04:18.022+0100", + "priority": { + "self": "http://localhost:9090/rest/api/2/priority/3", + "iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg", + "name": "Medium", + "id": "3" + }, + "customfield_10100": null, + "customfield_10101": null, + "customfield_10102": null, + "labels": [], + "customfield_10103": null, + "timeestimate": null, + "aggregatetimeoriginalestimate": null, + "versions": [], + "issuelinks": [], + "assignee": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "updated": "2024-05-29T15:04:18.022+0100", + "status": { + "self": "http://localhost:9090/rest/api/2/status/10000", + "description": "", + "iconUrl": "http://localhost:9090/", + "name": "To Do", + "id": "10000", + "statusCategory": { + "self": "http://localhost:9090/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "default", + "name": "To Do" + } + }, + "components": [], + "timeoriginalestimate": null, + "description": "CDEvents integration with Jira", + "timetracking": {}, + "archiveddate": null, + "attachment": [], + "aggregatetimeestimate": null, + "summary": "CDEvents integration with Jira", + "creator": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "subtasks": [], + "reporter": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "customfield_10000": "{summaryBean=com.atlassian.jira.plugin.devstatus.rest.SummaryBean@6f5ce47b[summary={pullrequest=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@2658d383[byInstanceType={},overall=PullRequestOverallBean{stateCount=0, state='OPEN', details=PullRequestOverallDetails{openCount=0, mergedCount=0, declinedCount=0}}], build=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@5d1d8c4f[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BuildOverallBean@3db998a4[failedBuildCount=0,successfulBuildCount=0,unknownBuildCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], review=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@14ec6fe6[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.ReviewsOverallBean@fb11e8b[dueDate=,overDue=false,state=,stateCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], deployment-environment=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@3eff84c6[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.DeploymentOverallBean@4ff25705[showProjects=false,successfulCount=0,topEnvironments=[],count=0,lastUpdated=,lastUpdatedTimestamp=]], repository=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@38968242[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.CommitOverallBean@3fd4869d[count=0,lastUpdated=,lastUpdatedTimestamp=]], branch=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@7d4fec15[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BranchOverallBean@2058d6c6[count=0,lastUpdated=,lastUpdatedTimestamp=]]},configErrors=[],errors=[]], devSummaryJson={\"cachedValue\":{\"errors\":[],\"configErrors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":\"OPEN\",\"details\":{\"openCount\":0,\"mergedCount\":0,\"declinedCount\":0,\"total\":0},\"open\":true},\"byInstanceType\":{}},\"build\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"failedBuildCount\":0,\"successfulBuildCount\":0,\"unknownBuildCount\":0},\"byInstanceType\":{}},\"review\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":null,\"dueDate\":null,\"overDue\":false,\"completed\":false},\"byInstanceType\":{}},\"deployment-environment\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"topEnvironments\":[],\"showProjects\":false,\"successfulCount\":0},\"byInstanceType\":{}},\"repository\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}},\"branch\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}}}},\"isStale\":false}}", + "aggregateprogress": { + "progress": 0, + "total": 0 + }, + "environment": null, + "duedate": null, + "progress": { + "progress": 0, + "total": 0 + }, + "comment": { + "comments": [], + "maxResults": 0, + "total": 0, + "startAt": 0 + }, + "votes": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/votes", + "votes": 0, + "hasVoted": false + }, + "worklog": { + "startAt": 0, + "maxResults": 20, + "total": 0, + "worklogs": [] + }, + "archivedby": null + } + } +} \ No newline at end of file diff --git a/pkg/jira/testdata/issue_generic.json b/pkg/jira/testdata/issue_generic.json new file mode 100644 index 0000000..5ac81e8 --- /dev/null +++ b/pkg/jira/testdata/issue_generic.json @@ -0,0 +1,193 @@ + +{ + "timestamp": 1717165102467, + "webhookEvent": "jira:issue_updated", + "issue_event_type_name": "issue_generic", + "user": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "issue": { + "id": "10008", + "self": "http://localhost:9090/rest/api/2/issue/10008", + "key": "ES-9", + "fields": { + "issuetype": { + "self": "http://localhost:9090/rest/api/2/issuetype/10002", + "id": "10002", + "description": "Created by Jira Software - do not edit or delete. Issue type for a user story.", + "iconUrl": "http://localhost:9090/images/icons/issuetypes/story.svg", + "name": "Story", + "subtask": false + }, + "timespent": null, + "project": { + "self": "http://localhost:9090/rest/api/2/project/10000", + "id": "10000", + "key": "ES", + "name": "EST-SeaTel", + "projectTypeKey": "software", + "avatarUrls": { + "48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324", + "24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324", + "16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324", + "32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324" + } + }, + "fixVersions": [], + "customfield_10110": "0i0001r:", + "customfield_10111": 3, + "aggregatetimespent": null, + "resolution": { + "self": "http://localhost:9090/rest/api/2/resolution/10000", + "id": "10000", + "description": "Work has been completed on this issue.", + "name": "Done" + }, + "customfield_10107": null, + "customfield_10108": null, + "customfield_10109": null, + "resolutiondate": "2024-05-31T15:18:22.446+0100", + "workratio": -1, + "lastViewed": "2024-05-31T15:18:22.419+0100", + "watches": { + "self": "http://localhost:9090/rest/api/2/issue/ES-9/watchers", + "watchCount": 1, + "isWatching": true + }, + "created": "2024-05-29T14:18:38.402+0100", + "priority": { + "self": "http://localhost:9090/rest/api/2/priority/3", + "iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg", + "name": "Medium", + "id": "3" + }, + "customfield_10100": null, + "customfield_10101": null, + "customfield_10102": null, + "labels": [], + "customfield_10103": null, + "timeestimate": null, + "aggregatetimeoriginalestimate": null, + "versions": [], + "issuelinks": [], + "assignee": null, + "updated": "2024-05-31T15:18:22.450+0100", + "status": { + "self": "http://localhost:9090/rest/api/2/status/10001", + "description": "", + "iconUrl": "http://localhost:9090/", + "name": "Done", + "id": "10001", + "statusCategory": { + "self": "http://localhost:9090/rest/api/2/statuscategory/3", + "id": 3, + "key": "done", + "colorName": "success", + "name": "Done" + } + }, + "components": [], + "timeoriginalestimate": null, + "description": null, + "timetracking": {}, + "archiveddate": null, + "attachment": [], + "aggregatetimeestimate": null, + "summary": "As a developer, I'd like to update story status during the sprint >> Click the Active sprints link at the top right of the screen to go to the Active sprints where the current Sprint's items can be updated", + "creator": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "subtasks": [], + "reporter": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "customfield_10000": "{summaryBean=com.atlassian.jira.plugin.devstatus.rest.SummaryBean@368b0950[summary={pullrequest=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@57d89d90[byInstanceType={},overall=PullRequestOverallBean{stateCount=0, state='OPEN', details=PullRequestOverallDetails{openCount=0, mergedCount=0, declinedCount=0}}], build=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@6fd95e24[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BuildOverallBean@7c7e6a52[failedBuildCount=0,successfulBuildCount=0,unknownBuildCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], review=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@47d13bf4[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.ReviewsOverallBean@76206dc3[dueDate=,overDue=false,state=,stateCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], deployment-environment=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@532d355d[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.DeploymentOverallBean@5c8ee990[showProjects=false,successfulCount=0,topEnvironments=[],count=0,lastUpdated=,lastUpdatedTimestamp=]], repository=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@12ce676e[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.CommitOverallBean@67f54e81[count=0,lastUpdated=,lastUpdatedTimestamp=]], branch=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@5d4808ff[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BranchOverallBean@7621b8af[count=0,lastUpdated=,lastUpdatedTimestamp=]]},configErrors=[],errors=[]], devSummaryJson={\"cachedValue\":{\"errors\":[],\"configErrors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":\"OPEN\",\"details\":{\"openCount\":0,\"mergedCount\":0,\"declinedCount\":0,\"total\":0},\"open\":true},\"byInstanceType\":{}},\"build\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"failedBuildCount\":0,\"successfulBuildCount\":0,\"unknownBuildCount\":0},\"byInstanceType\":{}},\"review\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":null,\"dueDate\":null,\"overDue\":false,\"completed\":false},\"byInstanceType\":{}},\"deployment-environment\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"topEnvironments\":[],\"showProjects\":false,\"successfulCount\":0},\"byInstanceType\":{}},\"repository\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}},\"branch\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}}}},\"isStale\":false}}", + "aggregateprogress": { + "progress": 0, + "total": 0 + }, + "environment": null, + "duedate": null, + "progress": { + "progress": 0, + "total": 0 + }, + "comment": { + "comments": [], + "maxResults": 0, + "total": 0, + "startAt": 0 + }, + "votes": { + "self": "http://localhost:9090/rest/api/2/issue/ES-9/votes", + "votes": 0, + "hasVoted": false + }, + "worklog": { + "startAt": 0, + "maxResults": 20, + "total": 0, + "worklogs": [] + }, + "archivedby": null + } + }, + "changelog": { + "id": "10104", + "items": [ + { + "field": "resolution", + "fieldtype": "jira", + "from": null, + "fromString": null, + "to": "10000", + "toString": "Done" + }, + { + "field": "status", + "fieldtype": "jira", + "from": "10000", + "fromString": "To Do", + "to": "10001", + "toString": "Done" + } + ] + } + } + \ No newline at end of file diff --git a/pkg/jira/testdata/issue_updated.json b/pkg/jira/testdata/issue_updated.json new file mode 100644 index 0000000..86e9ea6 --- /dev/null +++ b/pkg/jira/testdata/issue_updated.json @@ -0,0 +1,194 @@ + +{ + "timestamp": 1716992387526, + "webhookEvent": "jira:issue_updated", + "issue_event_type_name": "issue_updated", + "user": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "issue": { + "id": "10023", + "self": "http://localhost:9090/rest/api/2/issue/10023", + "key": "ES-24", + "fields": { + "issuetype": { + "self": "http://localhost:9090/rest/api/2/issuetype/10002", + "id": "10002", + "description": "Created by Jira Software - do not edit or delete. Issue type for a user story.", + "iconUrl": "http://localhost:9090/images/icons/issuetypes/story.svg", + "name": "Story", + "subtask": false + }, + "timespent": null, + "project": { + "self": "http://localhost:9090/rest/api/2/project/10000", + "id": "10000", + "key": "ES", + "name": "EST-SeaTel", + "projectTypeKey": "software", + "avatarUrls": { + "48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324", + "24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324", + "16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324", + "32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324" + } + }, + "fixVersions": [], + "customfield_10110": "0i00053:", + "customfield_10111": null, + "aggregatetimespent": null, + "resolution": null, + "customfield_10107": null, + "customfield_10108": null, + "customfield_10109": null, + "resolutiondate": null, + "workratio": -1, + "lastViewed": "2024-05-29T15:14:18.184+0100", + "watches": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/watchers", + "watchCount": 1, + "isWatching": true + }, + "created": "2024-05-29T15:04:18.022+0100", + "priority": { + "self": "http://localhost:9090/rest/api/2/priority/3", + "iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg", + "name": "Medium", + "id": "3" + }, + "customfield_10100": null, + "customfield_10101": null, + "customfield_10102": null, + "labels": [], + "customfield_10103": null, + "timeestimate": null, + "aggregatetimeoriginalestimate": null, + "versions": [], + "issuelinks": [], + "assignee": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "updated": "2024-05-29T15:19:47.389+0100", + "status": { + "self": "http://localhost:9090/rest/api/2/status/10000", + "description": "", + "iconUrl": "http://localhost:9090/", + "name": "To Do", + "id": "10000", + "statusCategory": { + "self": "http://localhost:9090/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "default", + "name": "To Do" + } + }, + "components": [], + "timeoriginalestimate": null, + "description": "CDEvents integration with Jira updates needed", + "timetracking": {}, + "archiveddate": null, + "attachment": [], + "aggregatetimeestimate": null, + "summary": "CDEvents integration with Jira", + "creator": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "subtasks": [], + "reporter": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "customfield_10000": "{summaryBean=com.atlassian.jira.plugin.devstatus.rest.SummaryBean@314e1b0d[summary={pullrequest=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@2d06df[byInstanceType={},overall=PullRequestOverallBean{stateCount=0, state='OPEN', details=PullRequestOverallDetails{openCount=0, mergedCount=0, declinedCount=0}}], build=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@35d32094[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BuildOverallBean@5d20d30f[failedBuildCount=0,successfulBuildCount=0,unknownBuildCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], review=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@38ef0f2a[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.ReviewsOverallBean@6ae24cd6[dueDate=,overDue=false,state=,stateCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], deployment-environment=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@4619169e[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.DeploymentOverallBean@1dfa0df9[showProjects=false,successfulCount=0,topEnvironments=[],count=0,lastUpdated=,lastUpdatedTimestamp=]], repository=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@5dacc733[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.CommitOverallBean@2550a4ac[count=0,lastUpdated=,lastUpdatedTimestamp=]], branch=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@2b73fb7e[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BranchOverallBean@662bae2c[count=0,lastUpdated=,lastUpdatedTimestamp=]]},configErrors=[],errors=[]], devSummaryJson={\"cachedValue\":{\"errors\":[],\"configErrors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":\"OPEN\",\"details\":{\"openCount\":0,\"mergedCount\":0,\"declinedCount\":0,\"total\":0},\"open\":true},\"byInstanceType\":{}},\"build\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"failedBuildCount\":0,\"successfulBuildCount\":0,\"unknownBuildCount\":0},\"byInstanceType\":{}},\"review\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":null,\"dueDate\":null,\"overDue\":false,\"completed\":false},\"byInstanceType\":{}},\"deployment-environment\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"topEnvironments\":[],\"showProjects\":false,\"successfulCount\":0},\"byInstanceType\":{}},\"repository\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}},\"branch\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}}}},\"isStale\":false}}", + "aggregateprogress": { + "progress": 0, + "total": 0 + }, + "environment": null, + "duedate": null, + "progress": { + "progress": 0, + "total": 0 + }, + "comment": { + "comments": [], + "maxResults": 0, + "total": 0, + "startAt": 0 + }, + "votes": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/votes", + "votes": 0, + "hasVoted": false + }, + "worklog": { + "startAt": 0, + "maxResults": 20, + "total": 0, + "worklogs": [] + }, + "archivedby": null + } + }, + "changelog": { + "id": "10027", + "items": [ + { + "field": "description", + "fieldtype": "jira", + "from": null, + "fromString": "CDEvents integration with Jira", + "to": null, + "toString": "CDEvents integration with Jira updates needed" + } + ] + } + } + \ No newline at end of file diff --git a/pkg/jira/testdata/issue_work_logged.json b/pkg/jira/testdata/issue_work_logged.json new file mode 100644 index 0000000..d2b10a6 --- /dev/null +++ b/pkg/jira/testdata/issue_work_logged.json @@ -0,0 +1,319 @@ +{ + "timestamp": 1717164468987, + "webhookEvent": "jira:worklog_updated", + "issue_event_type_name": "issue_work_logged", + "user": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "issue": { + "id": "10023", + "self": "http://localhost:9090/rest/api/2/issue/10023", + "key": "ES-24", + "fields": { + "issuetype": { + "self": "http://localhost:9090/rest/api/2/issuetype/10002", + "id": "10002", + "description": "Created by Jira Software - do not edit or delete. Issue type for a user story.", + "iconUrl": "http://localhost:9090/images/icons/issuetypes/story.svg", + "name": "Story", + "subtask": false + }, + "timespent": 46800, + "project": { + "self": "http://localhost:9090/rest/api/2/project/10000", + "id": "10000", + "key": "ES", + "name": "EST-SeaTel", + "projectTypeKey": "software", + "avatarUrls": { + "48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324", + "24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324", + "16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324", + "32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324" + } + }, + "fixVersions": [], + "customfield_10110": "0i00053:", + "customfield_10111": null, + "aggregatetimespent": 46800, + "resolution": null, + "customfield_10107": null, + "customfield_10108": null, + "customfield_10109": null, + "resolutiondate": null, + "workratio": -1, + "lastViewed": "2024-05-31T15:07:48.793+0100", + "watches": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/watchers", + "watchCount": 1, + "isWatching": true + }, + "created": "2024-05-29T15:04:18.022+0100", + "priority": { + "self": "http://localhost:9090/rest/api/2/priority/3", + "iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg", + "name": "Medium", + "id": "3" + }, + "customfield_10100": null, + "customfield_10101": null, + "customfield_10102": null, + "labels": [], + "customfield_10103": null, + "timeestimate": 0, + "aggregatetimeoriginalestimate": null, + "versions": [], + "issuelinks": [], + "assignee": null, + "updated": "2024-05-31T15:07:48.986+0100", + "status": { + "self": "http://localhost:9090/rest/api/2/status/10000", + "description": "", + "iconUrl": "http://localhost:9090/", + "name": "To Do", + "id": "10000", + "statusCategory": { + "self": "http://localhost:9090/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "default", + "name": "To Do" + } + }, + "components": [], + "timeoriginalestimate": null, + "description": "CDEvents integration with Jira updates needed", + "timetracking": { + "remainingEstimate": "0m", + "timeSpent": "1d 5h", + "remainingEstimateSeconds": 0, + "timeSpentSeconds": 46800 + }, + "archiveddate": null, + "attachment": [], + "aggregatetimeestimate": 0, + "summary": "CDEvents integration with Jira", + "creator": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "subtasks": [], + "reporter": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "customfield_10000": "{summaryBean=com.atlassian.jira.plugin.devstatus.rest.SummaryBean@1bbaf445[summary={pullrequest=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@490e04f7[byInstanceType={},overall=PullRequestOverallBean{stateCount=0, state='OPEN', details=PullRequestOverallDetails{openCount=0, mergedCount=0, declinedCount=0}}], build=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@78895598[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BuildOverallBean@4119dfc2[failedBuildCount=0,successfulBuildCount=0,unknownBuildCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], review=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@460ac642[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.ReviewsOverallBean@1e33f8ce[dueDate=,overDue=false,state=,stateCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], deployment-environment=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@2ca44b05[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.DeploymentOverallBean@23cc769c[showProjects=false,successfulCount=0,topEnvironments=[],count=0,lastUpdated=,lastUpdatedTimestamp=]], repository=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@33649c71[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.CommitOverallBean@3210f75b[count=0,lastUpdated=,lastUpdatedTimestamp=]], branch=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@3c626e7f[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BranchOverallBean@7c5a97f9[count=0,lastUpdated=,lastUpdatedTimestamp=]]},configErrors=[],errors=[]], devSummaryJson={\"cachedValue\":{\"errors\":[],\"configErrors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":\"OPEN\",\"details\":{\"openCount\":0,\"mergedCount\":0,\"declinedCount\":0,\"total\":0},\"open\":true},\"byInstanceType\":{}},\"build\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"failedBuildCount\":0,\"successfulBuildCount\":0,\"unknownBuildCount\":0},\"byInstanceType\":{}},\"review\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":null,\"dueDate\":null,\"overDue\":false,\"completed\":false},\"byInstanceType\":{}},\"deployment-environment\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"topEnvironments\":[],\"showProjects\":false,\"successfulCount\":0},\"byInstanceType\":{}},\"repository\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}},\"branch\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}}}},\"isStale\":false}}", + "aggregateprogress": { + "progress": 46800, + "total": 46800, + "percent": 100 + }, + "environment": null, + "duedate": null, + "progress": { + "progress": 46800, + "total": 46800, + "percent": 100 + }, + "comment": { + "comments": [ + { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10019", + "id": "10019", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "is the issue fixed?", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-29T15:22:43.937+0100", + "updated": "2024-05-29T15:22:43.937+0100" + }, + { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10100", + "id": "10100", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "updating to Unassigned", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-31T14:46:23.440+0100", + "updated": "2024-05-31T14:46:23.440+0100" + } + ], + "maxResults": 2, + "total": 2, + "startAt": 0 + }, + "votes": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/votes", + "votes": 0, + "hasVoted": false + }, + "worklog": { + "startAt": 0, + "maxResults": 20, + "total": 1, + "worklogs": [ + { + "self": "http://localhost:9090/rest/api/2/issue/10023/worklog/10000", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "comment": "Work in progress integrating CDEvents with Jira", + "created": "2024-05-31T15:07:48.978+0100", + "updated": "2024-05-31T15:07:48.978+0100", + "started": "2024-05-30T15:06:00.000+0100", + "timeSpent": "1d 5h", + "timeSpentSeconds": 46800, + "id": "10000", + "issueId": "10023" + } + ] + }, + "archivedby": null + } + }, + "changelog": { + "id": "10101", + "items": [ + { + "field": "WorklogId", + "fieldtype": "jira", + "from": "10000", + "fromString": "10000", + "to": null, + "toString": null + }, + { + "field": "timeestimate", + "fieldtype": "jira", + "from": null, + "fromString": null, + "to": "0", + "toString": "0" + }, + { + "field": "timespent", + "fieldtype": "jira", + "from": null, + "fromString": null, + "to": "46800", + "toString": "46800" + } + ] + } + } + \ No newline at end of file diff --git a/pkg/jira/testdata/issue_worklog_deleted.json b/pkg/jira/testdata/issue_worklog_deleted.json new file mode 100644 index 0000000..130e25f --- /dev/null +++ b/pkg/jira/testdata/issue_worklog_deleted.json @@ -0,0 +1,322 @@ +{ + "timestamp": 1717167743759, + "webhookEvent": "jira:worklog_updated", + "issue_event_type_name": "issue_worklog_deleted", + "user": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "issue": { + "id": "10023", + "self": "http://localhost:9090/rest/api/2/issue/10023", + "key": "ES-24", + "fields": { + "issuetype": { + "self": "http://localhost:9090/rest/api/2/issuetype/10002", + "id": "10002", + "description": "Created by Jira Software - do not edit or delete. Issue type for a user story.", + "iconUrl": "http://localhost:9090/images/icons/issuetypes/story.svg", + "name": "Story", + "subtask": false + }, + "timespent": 0, + "project": { + "self": "http://localhost:9090/rest/api/2/project/10000", + "id": "10000", + "key": "ES", + "name": "EST-SeaTel", + "projectTypeKey": "software", + "avatarUrls": { + "48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324", + "24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324", + "16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324", + "32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324" + } + }, + "fixVersions": [], + "customfield_10110": "0i00053:", + "customfield_10111": null, + "aggregatetimespent": 0, + "resolution": null, + "customfield_10107": null, + "customfield_10108": null, + "customfield_10109": null, + "resolutiondate": null, + "workratio": -1, + "lastViewed": "2024-05-31T16:02:12.962+0100", + "watches": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/watchers", + "watchCount": 1, + "isWatching": true + }, + "created": "2024-05-29T15:04:18.022+0100", + "priority": { + "self": "http://localhost:9090/rest/api/2/priority/3", + "iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg", + "name": "Medium", + "id": "3" + }, + "customfield_10100": null, + "customfield_10101": null, + "customfield_10102": null, + "labels": [], + "customfield_10103": null, + "timeestimate": 46800, + "aggregatetimeoriginalestimate": null, + "versions": [], + "issuelinks": [], + "assignee": null, + "updated": "2024-05-31T16:02:23.758+0100", + "status": { + "self": "http://localhost:9090/rest/api/2/status/10000", + "description": "", + "iconUrl": "http://localhost:9090/", + "name": "To Do", + "id": "10000", + "statusCategory": { + "self": "http://localhost:9090/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "default", + "name": "To Do" + } + }, + "components": [], + "timeoriginalestimate": null, + "description": "CDEvents integration with Jira updates needed", + "timetracking": { + "remainingEstimate": "1d 5h", + "timeSpent": "0m", + "remainingEstimateSeconds": 46800, + "timeSpentSeconds": 0 + }, + "archiveddate": null, + "attachment": [], + "aggregatetimeestimate": 46800, + "summary": "CDEvents integration with Jira", + "creator": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "subtasks": [], + "reporter": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "customfield_10000": "{summaryBean=com.atlassian.jira.plugin.devstatus.rest.SummaryBean@12dc1a84[summary={pullrequest=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@2b3dea0[byInstanceType={},overall=PullRequestOverallBean{stateCount=0, state='OPEN', details=PullRequestOverallDetails{openCount=0, mergedCount=0, declinedCount=0}}], build=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@606cec3a[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BuildOverallBean@7cb66849[failedBuildCount=0,successfulBuildCount=0,unknownBuildCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], review=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@19a6ae48[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.ReviewsOverallBean@3d72b1c6[dueDate=,overDue=false,state=,stateCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], deployment-environment=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@3af8976e[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.DeploymentOverallBean@75c34458[showProjects=false,successfulCount=0,topEnvironments=[],count=0,lastUpdated=,lastUpdatedTimestamp=]], repository=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@593b6046[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.CommitOverallBean@71ef0626[count=0,lastUpdated=,lastUpdatedTimestamp=]], branch=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@f52bc92[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BranchOverallBean@605cd8a4[count=0,lastUpdated=,lastUpdatedTimestamp=]]},configErrors=[],errors=[]], devSummaryJson={\"cachedValue\":{\"errors\":[],\"configErrors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":\"OPEN\",\"details\":{\"openCount\":0,\"mergedCount\":0,\"declinedCount\":0,\"total\":0},\"open\":true},\"byInstanceType\":{}},\"build\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"failedBuildCount\":0,\"successfulBuildCount\":0,\"unknownBuildCount\":0},\"byInstanceType\":{}},\"review\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":null,\"dueDate\":null,\"overDue\":false,\"completed\":false},\"byInstanceType\":{}},\"deployment-environment\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"topEnvironments\":[],\"showProjects\":false,\"successfulCount\":0},\"byInstanceType\":{}},\"repository\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}},\"branch\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}}}},\"isStale\":false}}", + "aggregateprogress": { + "progress": 0, + "total": 46800, + "percent": 0 + }, + "environment": null, + "duedate": null, + "progress": { + "progress": 0, + "total": 46800, + "percent": 0 + }, + "comment": { + "comments": [ + { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10019", + "id": "10019", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "is the issue fixed?", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-29T15:22:43.937+0100", + "updated": "2024-05-29T15:22:43.937+0100" + }, + { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10100", + "id": "10100", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "updating to Unassigned", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-31T14:46:23.440+0100", + "updated": "2024-05-31T14:46:23.440+0100" + }, + { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10101", + "id": "10101", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "Jira can be integrated with https://github.com/cdevents/webhook-adapter", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-31T15:14:29.285+0100", + "updated": "2024-05-31T15:14:29.285+0100" + } + ], + "maxResults": 3, + "total": 3, + "startAt": 0 + }, + "votes": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/votes", + "votes": 0, + "hasVoted": false + }, + "worklog": { + "startAt": 0, + "maxResults": 20, + "total": 0, + "worklogs": [] + }, + "archivedby": null + } + }, + "changelog": { + "id": "10106", + "items": [ + { + "field": "WorklogId", + "fieldtype": "jira", + "from": "10000", + "fromString": "10000", + "to": null, + "toString": null + }, + { + "field": "WorklogTimeSpent", + "fieldtype": "jira", + "from": "46800", + "fromString": "1 day, 5 hours", + "to": null, + "toString": null + }, + { + "field": "timeestimate", + "fieldtype": "jira", + "from": "0", + "fromString": "0", + "to": "46800", + "toString": "46800" + }, + { + "field": "timespent", + "fieldtype": "jira", + "from": "46800", + "fromString": "46800", + "to": "0", + "toString": "0" + } + ] + } + } + \ No newline at end of file diff --git a/pkg/jira/testdata/issue_worklog_updated.json b/pkg/jira/testdata/issue_worklog_updated.json new file mode 100644 index 0000000..ad4fd00 --- /dev/null +++ b/pkg/jira/testdata/issue_worklog_updated.json @@ -0,0 +1,303 @@ +{ + "timestamp": 1717164704071, + "webhookEvent": "jira:worklog_updated", + "issue_event_type_name": "issue_worklog_updated", + "user": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "issue": { + "id": "10023", + "self": "http://localhost:9090/rest/api/2/issue/10023", + "key": "ES-24", + "fields": { + "issuetype": { + "self": "http://localhost:9090/rest/api/2/issuetype/10002", + "id": "10002", + "description": "Created by Jira Software - do not edit or delete. Issue type for a user story.", + "iconUrl": "http://localhost:9090/images/icons/issuetypes/story.svg", + "name": "Story", + "subtask": false + }, + "timespent": 46800, + "project": { + "self": "http://localhost:9090/rest/api/2/project/10000", + "id": "10000", + "key": "ES", + "name": "EST-SeaTel", + "projectTypeKey": "software", + "avatarUrls": { + "48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324", + "24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324", + "16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324", + "32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324" + } + }, + "fixVersions": [], + "customfield_10110": "0i00053:", + "customfield_10111": null, + "aggregatetimespent": 46800, + "resolution": null, + "customfield_10107": null, + "customfield_10108": null, + "customfield_10109": null, + "resolutiondate": null, + "workratio": -1, + "lastViewed": "2024-05-31T15:11:44.055+0100", + "watches": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/watchers", + "watchCount": 1, + "isWatching": true + }, + "created": "2024-05-29T15:04:18.022+0100", + "priority": { + "self": "http://localhost:9090/rest/api/2/priority/3", + "iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg", + "name": "Medium", + "id": "3" + }, + "customfield_10100": null, + "customfield_10101": null, + "customfield_10102": null, + "labels": [], + "customfield_10103": null, + "timeestimate": 0, + "aggregatetimeoriginalestimate": null, + "versions": [], + "issuelinks": [], + "assignee": null, + "updated": "2024-05-31T15:11:44.070+0100", + "status": { + "self": "http://localhost:9090/rest/api/2/status/10000", + "description": "", + "iconUrl": "http://localhost:9090/", + "name": "To Do", + "id": "10000", + "statusCategory": { + "self": "http://localhost:9090/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "default", + "name": "To Do" + } + }, + "components": [], + "timeoriginalestimate": null, + "description": "CDEvents integration with Jira updates needed", + "timetracking": { + "remainingEstimate": "0m", + "timeSpent": "1d 5h", + "remainingEstimateSeconds": 0, + "timeSpentSeconds": 46800 + }, + "archiveddate": null, + "attachment": [], + "aggregatetimeestimate": 0, + "summary": "CDEvents integration with Jira", + "creator": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "subtasks": [], + "reporter": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "customfield_10000": "{summaryBean=com.atlassian.jira.plugin.devstatus.rest.SummaryBean@18f7b9f2[summary={pullrequest=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@2c60ebe6[byInstanceType={},overall=PullRequestOverallBean{stateCount=0, state='OPEN', details=PullRequestOverallDetails{openCount=0, mergedCount=0, declinedCount=0}}], build=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@38211d67[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BuildOverallBean@1335ee5e[failedBuildCount=0,successfulBuildCount=0,unknownBuildCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], review=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@1f3be9e9[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.ReviewsOverallBean@62027952[dueDate=,overDue=false,state=,stateCount=0,count=0,lastUpdated=,lastUpdatedTimestamp=]], deployment-environment=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@52788a50[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.DeploymentOverallBean@6e03d95f[showProjects=false,successfulCount=0,topEnvironments=[],count=0,lastUpdated=,lastUpdatedTimestamp=]], repository=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@2eb701d7[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.CommitOverallBean@5f8e35ba[count=0,lastUpdated=,lastUpdatedTimestamp=]], branch=com.atlassian.jira.plugin.devstatus.rest.SummaryItemBean@49321e11[byInstanceType={},overall=com.atlassian.jira.plugin.devstatus.summary.beans.BranchOverallBean@398bef49[count=0,lastUpdated=,lastUpdatedTimestamp=]]},configErrors=[],errors=[]], devSummaryJson={\"cachedValue\":{\"errors\":[],\"configErrors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":\"OPEN\",\"details\":{\"openCount\":0,\"mergedCount\":0,\"declinedCount\":0,\"total\":0},\"open\":true},\"byInstanceType\":{}},\"build\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"failedBuildCount\":0,\"successfulBuildCount\":0,\"unknownBuildCount\":0},\"byInstanceType\":{}},\"review\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"stateCount\":0,\"state\":null,\"dueDate\":null,\"overDue\":false,\"completed\":false},\"byInstanceType\":{}},\"deployment-environment\":{\"overall\":{\"count\":0,\"lastUpdated\":null,\"topEnvironments\":[],\"showProjects\":false,\"successfulCount\":0},\"byInstanceType\":{}},\"repository\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}},\"branch\":{\"overall\":{\"count\":0,\"lastUpdated\":null},\"byInstanceType\":{}}}},\"isStale\":false}}", + "aggregateprogress": { + "progress": 46800, + "total": 46800, + "percent": 100 + }, + "environment": null, + "duedate": null, + "progress": { + "progress": 46800, + "total": 46800, + "percent": 100 + }, + "comment": { + "comments": [ + { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10019", + "id": "10019", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "is the issue fixed?", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-29T15:22:43.937+0100", + "updated": "2024-05-29T15:22:43.937+0100" + }, + { + "self": "http://localhost:9090/rest/api/2/issue/10023/comment/10100", + "id": "10100", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "body": "updating to Unassigned", + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "created": "2024-05-31T14:46:23.440+0100", + "updated": "2024-05-31T14:46:23.440+0100" + } + ], + "maxResults": 2, + "total": 2, + "startAt": 0 + }, + "votes": { + "self": "http://localhost:9090/rest/api/2/issue/ES-24/votes", + "votes": 0, + "hasVoted": false + }, + "worklog": { + "startAt": 0, + "maxResults": 20, + "total": 1, + "worklogs": [ + { + "self": "http://localhost:9090/rest/api/2/issue/10023/worklog/10000", + "author": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "updateAuthor": { + "self": "http://localhost:9090/rest/api/2/user?username=rjalander", + "name": "rjalander", + "key": "JIRAUSER10000", + "emailAddress": "jalander.ramagiri@est.tech", + "avatarUrls": { + "48x48": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=48", + "24x24": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=24", + "16x16": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=16", + "32x32": "https://www.gravatar.com/avatar/acc35d47cafd5f3edca6b0f6db7f3921?d=mm&s=32" + }, + "displayName": "jalander.ramagiri@est.tech", + "active": true, + "timeZone": "Europe/Dublin" + }, + "comment": "Work in progress integrating CDEvents with Jira\r\nWorking with design integration", + "created": "2024-05-31T15:07:48.978+0100", + "updated": "2024-05-31T15:11:44.058+0100", + "started": "2024-05-30T15:06:00.000+0100", + "timeSpent": "1d 5h", + "timeSpentSeconds": 46800, + "id": "10000", + "issueId": "10023" + } + ] + }, + "archivedby": null + } + }, + "changelog": { + "id": "10102", + "items": [ + { + "field": "WorklogId", + "fieldtype": "jira", + "from": "10000", + "fromString": "10000", + "to": null, + "toString": null + } + ] + } + } + \ No newline at end of file diff --git a/pkg/jira/translate_events.go b/pkg/jira/translate_events.go new file mode 100644 index 0000000..bd1e9a5 --- /dev/null +++ b/pkg/jira/translate_events.go @@ -0,0 +1,97 @@ +/* +Copyright (C) 2024 Nordix Foundation. +For a full list of individual contributors, please see the commit history. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +SPDX-License-Identifier: Apache-2.0 +*/ + +package jira + +import ( + "encoding/json" +) + +func (pEvent *JiraEvent) HandleIssueCreatedEvent() (string, error) { + var issueCreatedEvent IssueCreatedEvent + err := json.Unmarshal([]byte(pEvent.Event), &issueCreatedEvent) + if err != nil { + Log().Error("Error occurred while Unmarshal issue_created JSON event into IssueCreatedEvent struct", err) + return "", err + } + Log().Infof("jira %s event translating to dev.cdevents.ticket.created", issueCreatedEvent.IssueEventTypeName) + cdEvent, err := issueCreatedEvent.TicketCreatedCDEvent() + if err != nil { + return "", err + } + return cdEvent, nil +} + +func (pEvent *JiraEvent) HandleIssueUpdatedEvent(eventType string) (string, error) { + var issueUpdatedEvent IssueUpdatedEvent + err := json.Unmarshal([]byte(pEvent.Event), &issueUpdatedEvent) + if err != nil { + Log().Errorf("Error occurred while Unmarshal %s JSON event into IssueUpdatedEvent struct, %v", eventType, err) + return "", err + } + Log().Infof("jira %s event translating to dev.cdevents.ticket.updated", issueUpdatedEvent.IssueEventTypeName) + cdEvent, err := issueUpdatedEvent.TicketUpdatedCDEvent() + if err != nil { + return "", err + } + return cdEvent, nil +} + +func (pEvent *JiraEvent) HandleIssueCommentedEvent() (string, error) { + var issueCommentedEvent IssueCommentedEvent + err := json.Unmarshal([]byte(pEvent.Event), &issueCommentedEvent) + if err != nil { + Log().Error("Error occurred while Unmarshal issue_commented JSON event into IssueCommentedEvent struct ", err) + return "", err + } + Log().Infof("jira %s event translating to dev.cdevents.ticket.updated", issueCommentedEvent.IssueEventTypeName) + cdEvent, err := issueCommentedEvent.TicketUpdatedCDEvent() + if err != nil { + return "", err + } + return cdEvent, nil +} + +func (pEvent *JiraEvent) HandleIssueAssignedEvent() (string, error) { + var issueAssignedEvent IssueAssignedEvent + err := json.Unmarshal([]byte(pEvent.Event), &issueAssignedEvent) + if err != nil { + Log().Error("Error occurred while Unmarshal issue_assigned JSON event into IssueAssignedEvent struct", err) + return "", err + } + Log().Infof("jira %s event translating to dev.cdevents.ticket.updated", issueAssignedEvent.IssueEventTypeName) + cdEvent, err := issueAssignedEvent.TicketUpdatedCDEvent() + if err != nil { + return "", err + } + return cdEvent, nil +} + +func (pEvent *JiraEvent) HandleIssueGenericEvent() (string, error) { + var issueGenericEvent IssueGenericEvent + err := json.Unmarshal([]byte(pEvent.Event), &issueGenericEvent) + if err != nil { + Log().Error("Error occurred while Unmarshal issue_generic JSON event into IssueGenericEvent struct", err) + return "", err + } + Log().Infof("jira %s event translating to dev.cdevents.ticket.closed", issueGenericEvent.IssueEventTypeName) + cdEvent, err := issueGenericEvent.TicketClosedCDEvent() + if err != nil { + return "", err + } + return cdEvent, nil +} diff --git a/pkg/translator.go b/pkg/translator.go new file mode 100644 index 0000000..206d344 --- /dev/null +++ b/pkg/translator.go @@ -0,0 +1,50 @@ +/* +Copyright (C) 2024 Nordix Foundation. +For a full list of individual contributors, please see the commit history. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +SPDX-License-Identifier: Apache-2.0 +*/ + +package main + +import ( + "net/http" + + "github.com/cdevents/jira-translator/pkg/jira" + "github.com/cdevents/webhook-adapter/pkg/cdevents" + "github.com/hashicorp/go-plugin" +) + +type EventTranslator struct{} + +// TranslateEvent Invoked from external application to translate Jira event into CDEvent +func (EventTranslator) TranslateEvent(event string, headers http.Header) (string, error) { + jira.Log().Info("Serving from jira-translator plugin") + cdEvent, err := jira.HandleTranslateJiraEvent(event, headers) + if err != nil { + return "", err + } + return cdEvent, nil +} + +func main() { + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: cdevents.Handshake, + Plugins: map[string]plugin.Plugin{ + "jira-translator-cdevents": &cdevents.TranslatorGRPCPlugin{Impl: &EventTranslator{}}, + }, + + // A non-nil value here enables gRPC serving for this plugin... + GRPCServer: plugin.DefaultGRPCServer, + }) +}