mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-27 12:13:29 -04:00
Merge 3a8e887afc into 25bf5edc4f
This commit is contained in:
commit
34b43b09eb
1246 changed files with 282391 additions and 0 deletions
557
.github/workflows/playbooks-ci.yml
vendored
Normal file
557
.github/workflows/playbooks-ci.yml
vendored
Normal file
|
|
@ -0,0 +1,557 @@
|
|||
name: Playbooks CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "core-plugins/mattermost-plugin-playbooks/**"
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "core-plugins/mattermost-plugin-playbooks/**"
|
||||
schedule:
|
||||
- cron: "0 03 * * 1-6" # Daily at 03:00 UTC from Monday through Saturday.
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: core-plugins/mattermost-plugin-playbooks
|
||||
|
||||
env:
|
||||
TERM: xterm
|
||||
IS_NOT_FORK: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
|
||||
jobs:
|
||||
go:
|
||||
name: Playbooks / Compute Go Version
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
version: ${{ steps.calculate.outputs.GO_VERSION }}
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
- name: Calculate version
|
||||
id: calculate
|
||||
run: echo GO_VERSION=$(grep -E '^go [0-9]+\.[0-9]+' go.mod | cut -d' ' -f2) >> "${GITHUB_OUTPUT}"
|
||||
|
||||
lint:
|
||||
name: Playbooks / Lint
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- go
|
||||
container:
|
||||
image: mattermostdevelopment/mattermost-build-server:${{ needs.go.outputs.version }}
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: ci/configure-git-safe-directory
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
|
||||
- name: ci/cache-node-modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: |
|
||||
core-plugins/mattermost-plugin-playbooks/webapp/node_modules
|
||||
core-plugins/mattermost-plugin-playbooks/e2e-tests/node_modules
|
||||
key: ${{ runner.os }}-playbooks-node-modules-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/webapp/package-lock.json') }}-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/e2e-tests/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-playbooks-node-modules-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/webapp/package-lock.json') }}-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/e2e-tests/package-lock.json') }}
|
||||
|
||||
- name: ci/setup-webapp-npm-deps
|
||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
env:
|
||||
NODE_ENV: development
|
||||
run: |
|
||||
cd webapp
|
||||
npm install --ignore-scripts --no-save --legacy-peer-deps
|
||||
|
||||
- name: ci/setup-e2e-npm-deps
|
||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
env:
|
||||
NODE_ENV: development
|
||||
run: |
|
||||
cd e2e-tests
|
||||
npm install --ignore-scripts --no-save --legacy-peer-deps
|
||||
|
||||
- name: ci/checking-code-style
|
||||
run: make check-style
|
||||
|
||||
- name: ci/go-tidy
|
||||
run: go mod tidy -v
|
||||
|
||||
- name: ci/check-diff-on-gomod
|
||||
run: git --no-pager diff --exit-code go.mod go.sum || (echo "Please run \"go mod tidy\" and commit the changes in go.mod and go.sum." && exit 1)
|
||||
|
||||
- name: ci/run-make-apply
|
||||
run: make apply
|
||||
|
||||
- name: ci/check-diff-on-generated-manifest-files
|
||||
run: git --no-pager diff --exit-code *manifest.* || (echo "Please run \"make apply\" and commit the changes in the generated manifests." && exit 1)
|
||||
|
||||
- name: ci/run-make-i18n-extract-webapp
|
||||
run: make i18n-extract-webapp
|
||||
|
||||
- name: ci/check-diff-on-webapp-i18n-files
|
||||
run: git --no-pager diff --exit-code webapp/i18n/en.json || (echo "Please run \"make i18n-extract\" and commit the changes in webapp/i18n/en.json." && exit 1)
|
||||
|
||||
- name: ci/run-make-graphql
|
||||
run: make graphql
|
||||
|
||||
- name: ci/check-diff-on-webapp-graphql-generated-files
|
||||
run: git --no-pager diff --exit-code webapp/src/graphql/generated/ || (echo "Please run \"make graphql\" and commit the changes." && exit 1)
|
||||
|
||||
dist:
|
||||
name: Playbooks / Dist
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- go
|
||||
- lint
|
||||
container:
|
||||
image: mattermostdevelopment/mattermost-build-server:${{ needs.go.outputs.version }}
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
- name: ci/configure-git-safe-directory
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
- name: ci/build-for-upload
|
||||
run: make dist
|
||||
- name: ci/upload-build-artifact
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: playbooks-bundle-${{ steps.short-sha.outputs.sha }}
|
||||
path: |
|
||||
core-plugins/mattermost-plugin-playbooks/dist/playbooks-*.tar.gz
|
||||
retention-days: 7 ## No need to keep build artifacts for more than 7 days
|
||||
- name: ci/ensure-build-on-all-platforms ## Verify builds on all platforms *after* ci/build-for-upload to keep artifact small
|
||||
run: make dist
|
||||
|
||||
dist-fips:
|
||||
name: Playbooks / Dist FIPS
|
||||
# Skip FIPS testing for forks, which won't have docker login credentials.
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- go
|
||||
- lint
|
||||
container:
|
||||
image: mattermostdevelopment/mattermost-build-server-fips:${{ needs.go.outputs.version }}
|
||||
credentials:
|
||||
username: ${{ secrets.DOCKERHUB_DEV_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_DEV_TOKEN }}
|
||||
env:
|
||||
FIPS_ENABLED: true
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
- name: ci/configure-git-safe-directory
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
- name: ci/build-for-upload
|
||||
run: make dist
|
||||
- name: ci/upload-build-artifact
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: playbooks-bundle-fips-${{ steps.short-sha.outputs.sha }}
|
||||
path: |
|
||||
core-plugins/mattermost-plugin-playbooks/dist/playbooks-*.tar.gz
|
||||
retention-days: 7 ## No need to keep build artifacts for more than 7 days
|
||||
- name: ci/ensure-build-on-all-platforms ## Verify builds on all platforms *after* ci/build-for-upload to keep artifact small
|
||||
run: make dist
|
||||
|
||||
upload:
|
||||
name: Playbooks / Upload
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- dist
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
- name: ci/download-build-artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: playbooks-bundle-${{ steps.short-sha.outputs.sha }}
|
||||
path: core-plugins/mattermost-plugin-playbooks/dist
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-east-1
|
||||
- name: Upload dist to S3
|
||||
run: |
|
||||
aws s3 cp dist/playbooks-*.tar.gz s3://${{ secrets.AWS_S3_BUCKET }}/mattermost-plugin-playbooks/mattermost-plugin-playbooks-${{ steps.short-sha.outputs.sha }}.tar.gz
|
||||
|
||||
upload-fips:
|
||||
name: Playbooks / Upload FIPS
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- dist-fips
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
- name: ci/download-build-artifact-fips
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: playbooks-bundle-fips-${{ steps.short-sha.outputs.sha }}
|
||||
path: core-plugins/mattermost-plugin-playbooks/dist-fips
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-east-1
|
||||
- name: Upload dist-fips to S3
|
||||
run: |
|
||||
aws s3 cp dist-fips/playbooks-*.tar.gz s3://${{ secrets.AWS_S3_BUCKET }}/mattermost-plugin-playbooks/mattermost-plugin-playbooks-fips-${{ steps.short-sha.outputs.sha }}.tar.gz
|
||||
|
||||
test:
|
||||
name: Playbooks / Test
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- lint
|
||||
- go
|
||||
container:
|
||||
image: mattermostdevelopment/mattermost-build-server:${{ needs.go.outputs.version }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
env:
|
||||
POSTGRES_USER: mmuser
|
||||
POSTGRES_DB: mattermost_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: ci/configure-git-safe-directory
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
|
||||
- name: ci/test-with-db
|
||||
uses: ./core-plugins/mattermost-plugin-playbooks/.github/actions/test-with-db
|
||||
|
||||
test-fips:
|
||||
name: Playbooks / Test FIPS
|
||||
# Skip FIPS testing for forks, which won't have docker login credentials.
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- lint
|
||||
- go
|
||||
container:
|
||||
image: mattermostdevelopment/mattermost-build-server-fips:${{ needs.go.outputs.version }}
|
||||
credentials:
|
||||
username: ${{ secrets.DOCKERHUB_DEV_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_DEV_TOKEN }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
env:
|
||||
POSTGRES_USER: mmuser
|
||||
POSTGRES_DB: mattermost_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: ci/configure-git-safe-directory
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
|
||||
- name: ci/test-with-db
|
||||
uses: ./core-plugins/mattermost-plugin-playbooks/.github/actions/test-with-db
|
||||
|
||||
generate-specs:
|
||||
name: Playbooks / Generate E2E Specs
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
specs: ${{ steps.generate-specs.outputs.specs }}
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: ci/generate-specs
|
||||
id: generate-specs
|
||||
uses: ./core-plugins/mattermost-plugin-playbooks/.github/actions/generate-specs
|
||||
with:
|
||||
parallelism: 3
|
||||
directory: core-plugins/mattermost-plugin-playbooks/e2e-tests
|
||||
search_path: tests/integration
|
||||
|
||||
e2e-cypress-tests:
|
||||
name: Playbooks / E2E Cypress (${{ matrix.runId }})
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- go
|
||||
- lint
|
||||
- generate-specs
|
||||
- dist
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.generate-specs.outputs.specs) }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15.3
|
||||
env:
|
||||
POSTGRES_USER: mmuser
|
||||
POSTGRES_PASSWORD: mostest
|
||||
POSTGRES_DB: mattermost_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2019-10-11T00-38-09Z
|
||||
env:
|
||||
MINIO_ACCESS_KEY: minioaccesskey
|
||||
MINIO_SECRET_KEY: miniosecretkey
|
||||
MINIO_SSE_MASTER_KEY: "my-minio-key:6368616e676520746869732070617373776f726420746f206120736563726574"
|
||||
inbucket:
|
||||
image: mattermost/inbucket:release-1.2.0
|
||||
ports:
|
||||
- 10080:10080
|
||||
- 10110:10110
|
||||
- 10025:10025
|
||||
elasticsearch:
|
||||
image: mattermost/mattermost-elasticsearch-docker:7.0.0
|
||||
env:
|
||||
http.host: "0.0.0.0"
|
||||
http.port: 9200
|
||||
http.cors.enabled: "true"
|
||||
http.cors.allow-origin: "http://localhost:1358,http://127.0.0.1:1358"
|
||||
http.cors.allow-headers: "X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization"
|
||||
http.cors.allow-credentials: "true"
|
||||
transport.host: "127.0.0.1"
|
||||
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
|
||||
ports:
|
||||
- 9200:9200
|
||||
mattermost-server:
|
||||
image: mattermostdevelopment/mattermost-enterprise-edition:master
|
||||
env:
|
||||
DB_HOST: postgres
|
||||
DB_PORT_NUMBER: 5432
|
||||
MM_DBNAME: mattermost_test
|
||||
MM_USERNAME: mmuser
|
||||
MM_PASSWORD: mostest
|
||||
CI_INBUCKET_HOST: inbucket
|
||||
CI_INBUCKET_PORT: 10080
|
||||
CI_MINIO_HOST: minio
|
||||
IS_CI: true
|
||||
MM_LICENSE: "${{ secrets.MM_E2E_TEST_LICENSE_ONPREM_ENT }}"
|
||||
MM_CLUSTERSETTINGS_READONLYCONFIG: false
|
||||
MM_EMAILSETTINGS_SMTPSERVER: inbucket
|
||||
MM_EMAILSETTINGS_SMTPPORT: 10025
|
||||
MM_ELASTICSEARCHSETTINGS_CONNECTIONURL: http://elasticsearch:9200
|
||||
MM_EXPERIMENTALSETTINGS_USENEWSAMLLIBRARY: true
|
||||
MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:mostest@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10"
|
||||
MM_SQLSETTINGS_DRIVERNAME: postgres
|
||||
MM_PLUGINSETTINGS_ENABLEUPLOADS: true
|
||||
MM_SERVICESETTINGS_SITEURL: http://localhost:8065
|
||||
MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS: true
|
||||
MM_ANNOUNCEMENTSETTINGS_ADMINNOTICESENABLED: false
|
||||
MM_SERVICESETTINGS_ENABLELEGACYSIDEBAR: true
|
||||
MM_TEAMSETTINGS_MAXUSERSPERTEAM: 10000
|
||||
MM_SERVICESETTINGS_ENABLEONBOARDINGFLOW: false
|
||||
MM_SERVICEENVIRONMENT: test
|
||||
MM_SERVICESETTINGS_EXPERIMENTALSTRICTCSRFENFORCEMENT: false
|
||||
MM_SERVICESETTINGS_STRICTCSRFENFORCEMENT: false
|
||||
ports:
|
||||
- 8065:8065
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: ci/download-build-artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: playbooks-bundle-${{ steps.short-sha.outputs.sha }}
|
||||
path: core-plugins/mattermost-plugin-playbooks/dist
|
||||
|
||||
- name: ci/e2e-test
|
||||
uses: ./core-plugins/mattermost-plugin-playbooks/.github/actions/e2e-test
|
||||
with:
|
||||
CYPRESS_serverEdition: E20
|
||||
SPECS: ${{ matrix.specs }}
|
||||
|
||||
e2e-cypress-tests-fips:
|
||||
name: Playbooks / E2E Cypress FIPS (${{ matrix.runId }})
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- go
|
||||
- lint
|
||||
- generate-specs
|
||||
- dist-fips
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.generate-specs.outputs.specs) }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15.3
|
||||
env:
|
||||
POSTGRES_USER: mmuser
|
||||
POSTGRES_PASSWORD: mostest
|
||||
POSTGRES_DB: mattermost_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2019-10-11T00-38-09Z
|
||||
env:
|
||||
MINIO_ACCESS_KEY: minioaccesskey
|
||||
MINIO_SECRET_KEY: miniosecretkey
|
||||
MINIO_SSE_MASTER_KEY: "my-minio-key:6368616e676520746869732070617373776f726420746f206120736563726574"
|
||||
inbucket:
|
||||
image: mattermost/inbucket:release-1.2.0
|
||||
ports:
|
||||
- 10080:10080
|
||||
- 10110:10110
|
||||
- 10025:10025
|
||||
elasticsearch:
|
||||
image: mattermost/mattermost-elasticsearch-docker:7.0.0
|
||||
env:
|
||||
http.host: "0.0.0.0"
|
||||
http.port: 9200
|
||||
http.cors.enabled: "true"
|
||||
http.cors.allow-origin: "http://localhost:1358,http://127.0.0.1:1358"
|
||||
http.cors.allow-headers: "X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization"
|
||||
http.cors.allow-credentials: "true"
|
||||
transport.host: "127.0.0.1"
|
||||
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
|
||||
ports:
|
||||
- 9200:9200
|
||||
mattermost-server:
|
||||
image: mattermostdevelopment/mattermost-enterprise-fips-edition:master
|
||||
env:
|
||||
DB_HOST: postgres
|
||||
DB_PORT_NUMBER: 5432
|
||||
MM_DBNAME: mattermost_test
|
||||
MM_USERNAME: mmuser
|
||||
MM_PASSWORD: mostest
|
||||
CI_INBUCKET_HOST: inbucket
|
||||
CI_INBUCKET_PORT: 10080
|
||||
CI_MINIO_HOST: minio
|
||||
IS_CI: true
|
||||
MM_LICENSE: "${{ secrets.MM_E2E_TEST_LICENSE_ONPREM_ENT }}"
|
||||
MM_CLUSTERSETTINGS_READONLYCONFIG: false
|
||||
MM_EMAILSETTINGS_SMTPSERVER: inbucket
|
||||
MM_EMAILSETTINGS_SMTPPORT: 10025
|
||||
MM_ELASTICSEARCHSETTINGS_CONNECTIONURL: http://elasticsearch:9200
|
||||
MM_EXPERIMENTALSETTINGS_USENEWSAMLLIBRARY: true
|
||||
MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:mostest@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10"
|
||||
MM_SQLSETTINGS_DRIVERNAME: postgres
|
||||
MM_PLUGINSETTINGS_ENABLEUPLOADS: true
|
||||
MM_SERVICESETTINGS_SITEURL: http://localhost:8065
|
||||
MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS: true
|
||||
MM_ANNOUNCEMENTSETTINGS_ADMINNOTICESENABLED: false
|
||||
MM_SERVICESETTINGS_ENABLELEGACYSIDEBAR: true
|
||||
MM_TEAMSETTINGS_MAXUSERSPERTEAM: 10000
|
||||
MM_SERVICESETTINGS_ENABLEONBOARDINGFLOW: false
|
||||
MM_SERVICEENVIRONMENT: test
|
||||
MM_SERVICESETTINGS_EXPERIMENTALSTRICTCSRFENFORCEMENT: false
|
||||
MM_SERVICESETTINGS_STRICTCSRFENFORCEMENT: false
|
||||
ports:
|
||||
- 8065:8065
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: ci/download-build-artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: playbooks-bundle-fips-${{ steps.short-sha.outputs.sha }}
|
||||
path: core-plugins/mattermost-plugin-playbooks/dist
|
||||
|
||||
- name: ci/e2e-test
|
||||
uses: ./core-plugins/mattermost-plugin-playbooks/.github/actions/e2e-test
|
||||
with:
|
||||
CYPRESS_serverEdition: E20
|
||||
SPECS: ${{ matrix.specs }}
|
||||
ARTIFACT_SUFFIX: -fips
|
||||
|
||||
report-test-results:
|
||||
name: Playbooks / Report Test Results
|
||||
if: always() && github.event.pull_request.head.repo.full_name == github.repository
|
||||
needs:
|
||||
- test
|
||||
- test-fips
|
||||
- e2e-cypress-tests
|
||||
- e2e-cypress-tests-fips
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
- name: ci/checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: ci/download-test-results
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
- name: ci/publish-results
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_SHA: ${{ github.sha }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
|
||||
run: python3 .github/scripts/junit_report.py
|
||||
50
.github/workflows/playbooks-codeql.yml
vendored
Normal file
50
.github/workflows/playbooks-codeql.yml
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
name: Playbooks CodeQL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- "core-plugins/mattermost-plugin-playbooks/**"
|
||||
pull_request:
|
||||
branches: [master]
|
||||
paths:
|
||||
- "core-plugins/mattermost-plugin-playbooks/**"
|
||||
schedule:
|
||||
- cron: "43 14 * * 2" # Weekly on Tuesdays
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: core-plugins/mattermost-plugin-playbooks
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
permissions:
|
||||
security-events: write # for github/codeql-action/autobuild to send a status report
|
||||
name: Playbooks / Analyze (${{ matrix.language }})
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ["go", "javascript"]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
debug: false
|
||||
config-file: ./core-plugins/mattermost-plugin-playbooks/.github/codeql/codeql-config.yml
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
27
core-plugins/mattermost-plugin-playbooks/.editorconfig
Normal file
27
core-plugins/mattermost-plugin-playbooks/.editorconfig
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# http://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
|
||||
[*.{js, jsx, ts, tsx, json, html}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[webapp/package.json]
|
||||
indent_size = 2
|
||||
|
||||
[{Makefile, *.mk}]
|
||||
indent_style = tab
|
||||
|
||||
[*.md]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = false
|
||||
30
core-plugins/mattermost-plugin-playbooks/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
30
core-plugins/mattermost-plugin-playbooks/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<!-- Thank you for contributing a pull request! Here are a few tips to help you:
|
||||
|
||||
1. If this is your first contribution, make sure you've read the Contribution Checklist https://developers.mattermost.com/contribute/getting-started/contribution-checklist/
|
||||
2. Read our blog post about "Submitting Great PRs" https://developers.mattermost.com/blog/2019-01-24-submitting-great-prs
|
||||
3. Take a look at other repository-specific documentation at https://developers.mattermost.com/contribute/getting-started/
|
||||
|
||||
REMEMBER TO:
|
||||
- Run `make i18n-extract` and commit changes to synchronize any new or removed messages
|
||||
- Run `make check-style` to check for style errors (required for all pull requests)
|
||||
- Run `make test` to ensure unit tests passed
|
||||
-->
|
||||
|
||||
## Summary
|
||||
<!--
|
||||
A description of what this pull request does
|
||||
-->
|
||||
|
||||
## Ticket Link
|
||||
<!--
|
||||
If this pull request addresses a Help Wanted ticket, please link the relevant GitHub issue, e.g.:
|
||||
|
||||
Fixes: https://github.com/mattermost/mattermost-server/issues/XXXXX
|
||||
|
||||
Otherwise, link the Jira ticket.
|
||||
-->
|
||||
|
||||
## Checklist
|
||||
<!-- Check off items as they are completed. ~~Strike through~~ items if they don't apply -->
|
||||
- [ ] Gated by experimental feature flag
|
||||
- [ ] Unit tests updated
|
||||
115
core-plugins/mattermost-plugin-playbooks/.github/actions/e2e-test/action.yaml
vendored
Normal file
115
core-plugins/mattermost-plugin-playbooks/.github/actions/e2e-test/action.yaml
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# Copyright 2022 Mattermost, Inc.
|
||||
name: "e2e-test"
|
||||
description: This action used to runs cypress e2e integration tests
|
||||
|
||||
inputs:
|
||||
CYPRESS_serverEdition:
|
||||
description: The cypress server edition
|
||||
required: true
|
||||
SPECS:
|
||||
description: The cypress specs to run
|
||||
required: true
|
||||
ARTIFACT_SUFFIX:
|
||||
description: Optional suffix for artifact names to avoid conflicts
|
||||
required: false
|
||||
default: ""
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: ci/setup-go
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: "${{ env.GO_VERSION }}"
|
||||
cache: false
|
||||
|
||||
- name: ci/setup-node
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version-file: "core-plugins/mattermost-plugin-playbooks/.nvmrc"
|
||||
cache: "npm"
|
||||
cache-dependency-path: |
|
||||
core-plugins/mattermost-plugin-playbooks/webapp/package-lock.json
|
||||
core-plugins/mattermost-plugin-playbooks/e2e-tests/package-lock.json
|
||||
|
||||
- name: ci/cache-node-modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: |
|
||||
core-plugins/mattermost-plugin-playbooks/webapp/node_modules
|
||||
core-plugins/mattermost-plugin-playbooks/e2e-tests/node_modules
|
||||
key: ${{ runner.os }}-playbooks-node-modules-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/webapp/package-lock.json') }}-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/e2e-tests/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-playbooks-node-modules-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/webapp/package-lock.json') }}-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/e2e-tests/package-lock.json') }}
|
||||
|
||||
- name: ci/disable-ALSA
|
||||
run: printf "pcm.!default {\n type plug\n slave.pcm \"null\"\n}\n" > ~/.asoundrc;
|
||||
shell: bash
|
||||
|
||||
- name: ci/restore-postresql
|
||||
uses: docker://postgres:14
|
||||
env:
|
||||
TEST_DATABASE_URL: postgres://mmuser:mostest@postgres:5432/mattermost_test
|
||||
with:
|
||||
entrypoint: ./core-plugins/mattermost-plugin-playbooks/.github/actions/e2e-test/generate-test-data.sh
|
||||
|
||||
- name: ci/installing-deps
|
||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/core-plugins/mattermost-plugin-playbooks
|
||||
env:
|
||||
NODE_ENV: development
|
||||
run: |
|
||||
cd webapp
|
||||
npm install --ignore-scripts --no-save --legacy-peer-deps
|
||||
cd ../e2e-tests
|
||||
npm install --ignore-scripts --no-save --legacy-peer-deps
|
||||
|
||||
- name: ci/installing-cypress-deps
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/core-plugins/mattermost-plugin-playbooks
|
||||
env:
|
||||
NODE_ENV: development
|
||||
run: |
|
||||
sudo apt-get install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb -y
|
||||
cd e2e-tests/
|
||||
npx cypress install
|
||||
|
||||
- name: ci/install-playbooks
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/core-plugins/mattermost-plugin-playbooks
|
||||
env:
|
||||
MM_SERVICESETTINGS_SITEURL: http://localhost:8065
|
||||
MM_ADMIN_USERNAME: sysadmin
|
||||
MM_ADMIN_PASSWORD: Sys@dmin-sample1
|
||||
run: make upload-to-server
|
||||
|
||||
- name: ci/run-cypress-tests
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/core-plugins/mattermost-plugin-playbooks
|
||||
env:
|
||||
TYPE: NONE
|
||||
PULL_REQUEST: ""
|
||||
BROWSER: chrome
|
||||
HEADLESS: "true"
|
||||
DASHBOARD_ENABLE: "false"
|
||||
FULL_REPORT: "false"
|
||||
MM_SERVICESETTINGS_SITEURL: http://localhost:8065
|
||||
MM_ADMIN_USERNAME: sysadmin
|
||||
MM_ADMIN_PASSWORD: Sys@dmin-sample1
|
||||
CYPRESS_serverEdition: ${{ inputs.CYPRESS_serverEdition }}
|
||||
MM_SERVICESETTINGS_EXPERIMENTALSTRICTCSRFENFORCEMENT: "false"
|
||||
MM_SERVICESETTINGS_STRICTCSRFENFORCEMENT: "false"
|
||||
TERM: xterm
|
||||
run: |
|
||||
cd e2e-tests
|
||||
npm run test -- --spec "${{ inputs.SPECS }}"
|
||||
|
||||
- name: ci/upload-cypress-test-results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: cypress-test-results-${{ matrix.runId }}${{ inputs.ARTIFACT_SUFFIX }}
|
||||
path: |
|
||||
core-plugins/mattermost-plugin-playbooks/e2e-tests/results/junit
|
||||
core-plugins/mattermost-plugin-playbooks/e2e-tests/tests/screenshots
|
||||
retention-days: 14 ## No need to keep test results more than 14 days
|
||||
5
core-plugins/mattermost-plugin-playbooks/.github/actions/e2e-test/generate-test-data.sh
vendored
Executable file
5
core-plugins/mattermost-plugin-playbooks/.github/actions/e2e-test/generate-test-data.sh
vendored
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
psql -d $TEST_DATABASE_URL -v "ON_ERROR_STOP=1" -c "CREATE DATABASE migrated;";
|
||||
psql -d $TEST_DATABASE_URL -v "ON_ERROR_STOP=1" -c "CREATE DATABASE latest;";
|
||||
psql -d $TEST_DATABASE_URL -v "ON_ERROR_STOP=1" mattermost_test < core-plugins/mattermost-plugin-playbooks/e2e-tests/db-setup/mattermost.sql;
|
||||
31
core-plugins/mattermost-plugin-playbooks/.github/actions/generate-specs/action.yaml
vendored
Normal file
31
core-plugins/mattermost-plugin-playbooks/.github/actions/generate-specs/action.yaml
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Copyright 2022 Mattermost, Inc.
|
||||
name: "generate-specs"
|
||||
description: This action used to split cypress integration tests based on the parallelism provided
|
||||
|
||||
inputs:
|
||||
directory:
|
||||
description: The directory of the test suite
|
||||
required: true
|
||||
search_path:
|
||||
description: The path to look for from within the directory
|
||||
required: true
|
||||
parallelism:
|
||||
description: The parallelism for the tests
|
||||
required: true
|
||||
outputs:
|
||||
specs:
|
||||
description: The specs generated for the strategy
|
||||
value: ${{ steps.generate-specs.outputs.specs }}
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: ci/generate-specs
|
||||
id: generate-specs
|
||||
env:
|
||||
PARALLELISM: ${{ inputs.parallelism }}
|
||||
SEARCH_PATH: ${{ inputs.search_path }}
|
||||
DIRECTORY: ${{ inputs.directory }}
|
||||
run: |
|
||||
go run ${{ github.action_path }}/split-tests.go > output.json
|
||||
echo "specs=$(cat output.json)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
116
core-plugins/mattermost-plugin-playbooks/.github/actions/generate-specs/split-tests.go
vendored
Normal file
116
core-plugins/mattermost-plugin-playbooks/.github/actions/generate-specs/split-tests.go
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Specs struct {
|
||||
searchPath string
|
||||
directory string
|
||||
parallelism int
|
||||
rawFiles []string
|
||||
groupedFiles []SpecGroup
|
||||
}
|
||||
|
||||
type SpecGroup struct {
|
||||
RunID string `json:"runId"`
|
||||
Specs string `json:"specs"`
|
||||
}
|
||||
|
||||
type Output struct {
|
||||
Include []SpecGroup `json:"include"`
|
||||
}
|
||||
|
||||
func newSpecGroup(runId string, specs string) *SpecGroup {
|
||||
return &SpecGroup{
|
||||
RunID: runId,
|
||||
Specs: specs,
|
||||
}
|
||||
}
|
||||
|
||||
func newSpecs(directory string, searchPath string, parallelism int) *Specs {
|
||||
return &Specs{
|
||||
directory: directory,
|
||||
searchPath: searchPath,
|
||||
parallelism: parallelism,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Specs) findFiles() {
|
||||
fileSystem := os.DirFS(filepath.Join(s.directory, s.searchPath))
|
||||
|
||||
err := fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Find all files matching _spec.(js|ts)
|
||||
r := regexp.MustCompile(".*_spec.(js|ts)$")
|
||||
if r.MatchString(path) {
|
||||
s.rawFiles = append(s.rawFiles, filepath.Join(s.searchPath, path))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Specs) generateSplits() {
|
||||
// Split to chunks based on the parallelism provided
|
||||
chunkSize := int(math.Ceil(float64(len(s.rawFiles)) / float64(s.parallelism)))
|
||||
runNo := 1
|
||||
// We can figure out a more sophisticated way to split the tests
|
||||
// We can use metadata in order to group them manually
|
||||
for i := 0; i <= len(s.rawFiles); i += chunkSize {
|
||||
end := i + chunkSize
|
||||
if end > len(s.rawFiles) {
|
||||
end = len(s.rawFiles)
|
||||
}
|
||||
|
||||
fileGroup := strings.Join(s.rawFiles[i:end], ",")
|
||||
|
||||
specGroup := newSpecGroup(fmt.Sprintf("%d", runNo), fileGroup)
|
||||
s.groupedFiles = append(s.groupedFiles, *specGroup)
|
||||
|
||||
// Break when we reach the end to avoid duplicate groups
|
||||
if end == len(s.rawFiles) {
|
||||
break
|
||||
}
|
||||
|
||||
runNo++
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Specs) dumpSplits() {
|
||||
// Dump json format for GitHub actions
|
||||
o := &Output{
|
||||
Include: s.groupedFiles,
|
||||
}
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.Stdout.Write(b)
|
||||
}
|
||||
|
||||
func main() {
|
||||
searchPath := os.Getenv("SEARCH_PATH")
|
||||
directory := os.Getenv("DIRECTORY")
|
||||
parallelism, _ := strconv.Atoi(os.Getenv("PARALLELISM"))
|
||||
|
||||
specs := newSpecs(directory, searchPath, parallelism)
|
||||
specs.findFiles()
|
||||
specs.generateSplits()
|
||||
specs.dumpSplits()
|
||||
}
|
||||
54
core-plugins/mattermost-plugin-playbooks/.github/actions/test-with-db/action.yaml
vendored
Normal file
54
core-plugins/mattermost-plugin-playbooks/.github/actions/test-with-db/action.yaml
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Copyright 2022 Mattermost, Inc.
|
||||
name: "test-with-db"
|
||||
description: This action used to runs tests with db integration
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: ci/cache-node-modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: |
|
||||
core-plugins/mattermost-plugin-playbooks/webapp/node_modules
|
||||
core-plugins/mattermost-plugin-playbooks/e2e-tests/node_modules
|
||||
key: ${{ runner.os }}-playbooks-node-modules-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/webapp/package-lock.json') }}-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/e2e-tests/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-playbooks-node-modules-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/webapp/package-lock.json') }}-${{ hashFiles('core-plugins/mattermost-plugin-playbooks/e2e-tests/package-lock.json') }}
|
||||
|
||||
- name: ci/setup-webapp-npm-deps
|
||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/core-plugins/mattermost-plugin-playbooks
|
||||
env:
|
||||
NODE_ENV: development
|
||||
run: |
|
||||
cd webapp
|
||||
npm install --ignore-scripts --no-save --legacy-peer-deps
|
||||
|
||||
- name: ci/test-db-integration
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/core-plugins/mattermost-plugin-playbooks
|
||||
env:
|
||||
IS_CI: true
|
||||
POSTGRES_USER: mmuser
|
||||
POSTGRES_DB: mattermost_test
|
||||
MYSQL_ROOT_PASSWORD: mostest
|
||||
MYSQL_DATABASE: mattermost_test
|
||||
MYSQL_USER: mmuser
|
||||
MYSQL_PASSWORD: mostest
|
||||
MARIADB_ROOT_PASSWORD: mostest
|
||||
MARIADB_DATABASE: mattermost_test
|
||||
MARIADB_USER: mmuser
|
||||
MARIADB_PASSWORD: mostest
|
||||
run: make test
|
||||
|
||||
- name: ci/upload-test-results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: ${{ github.job }}-results
|
||||
path: |
|
||||
core-plugins/mattermost-plugin-playbooks/report.xml
|
||||
core-plugins/mattermost-plugin-playbooks/client/report.xml
|
||||
core-plugins/mattermost-plugin-playbooks/build/report.xml
|
||||
retention-days: 5 ## No need to keep test results for more than 5 days
|
||||
1
core-plugins/mattermost-plugin-playbooks/.github/codecov.yml
vendored
Normal file
1
core-plugins/mattermost-plugin-playbooks/.github/codecov.yml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
comment: false
|
||||
14
core-plugins/mattermost-plugin-playbooks/.github/codeql/codeql-config.yml
vendored
Normal file
14
core-plugins/mattermost-plugin-playbooks/.github/codeql/codeql-config.yml
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
name: "CodeQL config"
|
||||
|
||||
query-filters:
|
||||
- exclude:
|
||||
problem.severity:
|
||||
- warning
|
||||
- recommendation
|
||||
- exclude:
|
||||
id: go/log-injection
|
||||
|
||||
paths-ignore:
|
||||
- e2e-tests
|
||||
- '**/*_test.go'
|
||||
- '**/*.test.*'
|
||||
102
core-plugins/mattermost-plugin-playbooks/.github/scripts/junit_report.py
vendored
Normal file
102
core-plugins/mattermost-plugin-playbooks/.github/scripts/junit_report.py
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Parse JUnit XML artifacts and publish a GitHub Check Run with the results."""
|
||||
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
def parse_results(pattern="**/*.xml"):
|
||||
total = failed = skipped = 0
|
||||
annotations = []
|
||||
|
||||
for f in glob.glob(pattern, recursive=True):
|
||||
try:
|
||||
root = ET.parse(f).getroot()
|
||||
for tc in root.iter("testcase"):
|
||||
total += 1
|
||||
if tc.find("skipped") is not None:
|
||||
skipped += 1
|
||||
elif tc.find("failure") is not None or tc.find("error") is not None:
|
||||
el = tc.find("failure") if tc.find("failure") is not None else tc.find("error")
|
||||
failed += 1
|
||||
name = f"{tc.get('classname', '')}.{tc.get('name', '')}".strip(".")
|
||||
message = (el.get("message") or el.text or "").strip()[:500]
|
||||
annotations.append({"name": name, "message": message})
|
||||
except Exception as e:
|
||||
print(f"Warning: could not parse {f}: {e}", file=sys.stderr)
|
||||
|
||||
passed = total - failed - skipped
|
||||
return total, passed, failed, skipped, annotations
|
||||
|
||||
|
||||
def build_summary(total, passed, failed, skipped, annotations):
|
||||
icon = "✅" if failed == 0 else "❌"
|
||||
lines = [
|
||||
"| Tests | Passed | Failed | Skipped |",
|
||||
"|-------|--------|--------|---------|",
|
||||
f"| {total} | {passed} | {failed} | {skipped} |",
|
||||
]
|
||||
if annotations:
|
||||
lines += [
|
||||
"",
|
||||
"<details><summary>Failed tests</summary>",
|
||||
"",
|
||||
]
|
||||
for a in annotations[:50]:
|
||||
first_line = next(iter(a.get("message", "").splitlines()), "")[:120]
|
||||
lines.append(f"- **{a['name']}**: {first_line}")
|
||||
lines.append("</details>")
|
||||
if len(annotations) > 50:
|
||||
lines.append(f"_...and {len(annotations) - 50} more_")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def create_check_run(owner, repo, sha, title, summary, conclusion):
|
||||
payload = {
|
||||
"name": "JUnit Test Report",
|
||||
"head_sha": sha,
|
||||
"status": "completed",
|
||||
"conclusion": conclusion,
|
||||
"output": {
|
||||
"title": title,
|
||||
"summary": summary,
|
||||
},
|
||||
}
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["gh", "api", f"repos/{owner}/{repo}/check-runs",
|
||||
"--method", "POST",
|
||||
"--input", "-"],
|
||||
input=json.dumps(payload).encode(),
|
||||
capture_output=True,
|
||||
timeout=30,
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
print("Error: timed out waiting for GitHub API", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if result.returncode != 0:
|
||||
print(f"Error creating check run: {result.stderr.decode()}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
owner = os.environ["GITHUB_REPOSITORY_OWNER"]
|
||||
repo = os.environ["GITHUB_REPOSITORY"].split("/")[1]
|
||||
sha = os.environ["GITHUB_SHA"]
|
||||
|
||||
total, passed, failed, skipped, annotations = parse_results()
|
||||
|
||||
title = f"{total} tests run, {passed} passed, {skipped} skipped, {failed} failed."
|
||||
summary = build_summary(total, passed, failed, skipped, annotations)
|
||||
conclusion = "success" if failed == 0 else "failure"
|
||||
|
||||
print(title)
|
||||
create_check_run(owner, repo, sha, title, summary, conclusion)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
542
core-plugins/mattermost-plugin-playbooks/.github/workflows/ci.yaml
vendored
Normal file
542
core-plugins/mattermost-plugin-playbooks/.github/workflows/ci.yaml
vendored
Normal file
|
|
@ -0,0 +1,542 @@
|
|||
name: ci
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: "0 03 * * 1-6" # Daily at 03:00 UTC from Monday through Saturday.
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
TERM: xterm
|
||||
IS_NOT_FORK: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
|
||||
jobs:
|
||||
go:
|
||||
name: Compute Go Version
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
version: ${{ steps.calculate.outputs.GO_VERSION }}
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
- name: Calculate version
|
||||
id: calculate
|
||||
run: echo GO_VERSION=$(grep -E '^go [0-9]+\.[0-9]+' go.mod | cut -d' ' -f2) >> "${GITHUB_OUTPUT}"
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- go
|
||||
container:
|
||||
image: mattermostdevelopment/mattermost-build-server:${{ needs.go.outputs.version }}
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: ci/configure-git-safe-directory
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
|
||||
- name: ci/cache-node-modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: |
|
||||
webapp/node_modules
|
||||
e2e-tests/node_modules
|
||||
key: ${{ runner.os }}-node-modules-${{ hashFiles('webapp/package-lock.json') }}-${{ hashFiles('e2e-tests/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-node-modules-${{ hashFiles('webapp/package-lock.json') }}-${{ hashFiles('e2e-tests/package-lock.json') }}
|
||||
|
||||
- name: ci/setup-webapp-npm-deps
|
||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
env:
|
||||
NODE_ENV: development
|
||||
run: |
|
||||
cd webapp
|
||||
npm install --ignore-scripts --no-save --legacy-peer-deps
|
||||
|
||||
- name: ci/setup-e2e-npm-deps
|
||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
env:
|
||||
NODE_ENV: development
|
||||
run: |
|
||||
cd e2e-tests
|
||||
npm install --ignore-scripts --no-save --legacy-peer-deps
|
||||
|
||||
- name: ci/checking-code-style
|
||||
run: make check-style
|
||||
|
||||
- name: ci/go-tidy
|
||||
run: go mod tidy -v
|
||||
|
||||
- name: ci/check-diff-on-gomod
|
||||
run: git --no-pager diff --exit-code go.mod go.sum || (echo "Please run \"go mod tidy\" and commit the changes in go.mod and go.sum." && exit 1)
|
||||
|
||||
- name: ci/run-make-apply
|
||||
run: make apply
|
||||
|
||||
- name: ci/check-diff-on-generated-manifest-files
|
||||
run: git --no-pager diff --exit-code *manifest.* || (echo "Please run \"make apply\" and commit the changes in the generated manifests." && exit 1)
|
||||
|
||||
- name: ci/run-make-i18n-extract-webapp
|
||||
run: make i18n-extract-webapp
|
||||
|
||||
- name: ci/check-diff-on-webapp-i18n-files
|
||||
run: git --no-pager diff --exit-code webapp/i18n/en.json || (echo "Please run \"make i18n-extract\" and commit the changes in webapp/i18n/en.json." && exit 1)
|
||||
|
||||
- name: ci/run-make-graphql
|
||||
run: make graphql
|
||||
|
||||
- name: ci/check-diff-on-webapp-graphql-generated-files
|
||||
run: git --no-pager diff --exit-code webapp/src/graphql/generated/ || (echo "Please run \"make graphql\" and commit the changes." && exit 1)
|
||||
|
||||
dist:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- go
|
||||
- lint
|
||||
container:
|
||||
image: mattermostdevelopment/mattermost-build-server:${{ needs.go.outputs.version }}
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
- name: ci/configure-git-safe-directory
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
- name: ci/build-for-upload
|
||||
run: make dist
|
||||
- name: ci/upload-build-artifact
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: playbooks-bundle-${{ steps.short-sha.outputs.sha }}
|
||||
path: |
|
||||
dist/playbooks-*.tar.gz
|
||||
retention-days: 7 ## No need to keep build artifacts for more than 7 days
|
||||
- name: ci/ensure-build-on-all-platforms ## Verify builds on all platforms *after* ci/build-for-upload to keep artifact small
|
||||
run: make dist
|
||||
|
||||
dist-fips:
|
||||
# Skip FIPS testing for forks, which won't have docker login credentials.
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- go
|
||||
- lint
|
||||
container:
|
||||
image: mattermostdevelopment/mattermost-build-server-fips:${{ needs.go.outputs.version }}
|
||||
credentials:
|
||||
username: ${{ secrets.DOCKERHUB_DEV_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_DEV_TOKEN }}
|
||||
env:
|
||||
FIPS_ENABLED: true
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
- name: ci/configure-git-safe-directory
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
- name: ci/build-for-upload
|
||||
run: make dist
|
||||
- name: ci/upload-build-artifact
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: playbooks-bundle-fips-${{ steps.short-sha.outputs.sha }}
|
||||
path: |
|
||||
dist/playbooks-*.tar.gz
|
||||
retention-days: 7 ## No need to keep build artifacts for more than 7 days
|
||||
- name: ci/ensure-build-on-all-platforms ## Verify builds on all platforms *after* ci/build-for-upload to keep artifact small
|
||||
run: make dist
|
||||
|
||||
upload:
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- dist
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
- name: ci/download-build-artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: playbooks-bundle-${{ steps.short-sha.outputs.sha }}
|
||||
path: dist
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-east-1
|
||||
- name: Upload dist to S3
|
||||
run: |
|
||||
aws s3 cp dist/playbooks-*.tar.gz s3://${{ secrets.AWS_S3_BUCKET }}/mattermost-plugin-playbooks/mattermost-plugin-playbooks-${{ steps.short-sha.outputs.sha }}.tar.gz
|
||||
|
||||
upload-fips:
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- dist-fips
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
- name: ci/download-build-artifact-fips
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: playbooks-bundle-fips-${{ steps.short-sha.outputs.sha }}
|
||||
path: dist-fips
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-east-1
|
||||
- name: Upload dist-fips to S3
|
||||
run: |
|
||||
aws s3 cp dist-fips/playbooks-*.tar.gz s3://${{ secrets.AWS_S3_BUCKET }}/mattermost-plugin-playbooks/mattermost-plugin-playbooks-fips-${{ steps.short-sha.outputs.sha }}.tar.gz
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- lint
|
||||
- go
|
||||
container:
|
||||
image: mattermostdevelopment/mattermost-build-server:${{ needs.go.outputs.version }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
env:
|
||||
POSTGRES_USER: mmuser
|
||||
POSTGRES_DB: mattermost_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: ci/configure-git-safe-directory
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
|
||||
- name: ci/test-with-db
|
||||
uses: ./.github/actions/test-with-db
|
||||
|
||||
test-fips:
|
||||
# Skip FIPS testing for forks, which won't have docker login credentials.
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- lint
|
||||
- go
|
||||
container:
|
||||
image: mattermostdevelopment/mattermost-build-server-fips:${{ needs.go.outputs.version }}
|
||||
credentials:
|
||||
username: ${{ secrets.DOCKERHUB_DEV_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_DEV_TOKEN }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
env:
|
||||
POSTGRES_USER: mmuser
|
||||
POSTGRES_DB: mattermost_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: ci/configure-git-safe-directory
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
|
||||
- name: ci/test-with-db
|
||||
uses: ./.github/actions/test-with-db
|
||||
|
||||
generate-specs:
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
specs: ${{ steps.generate-specs.outputs.specs }}
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: ci/generate-specs
|
||||
id: generate-specs
|
||||
uses: ./.github/actions/generate-specs
|
||||
with:
|
||||
parallelism: 3
|
||||
directory: e2e-tests
|
||||
search_path: tests/integration
|
||||
|
||||
e2e-cypress-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- go
|
||||
- lint
|
||||
- generate-specs
|
||||
- dist
|
||||
name: e2e-cypress-tests-run-${{ matrix.runId }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.generate-specs.outputs.specs) }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15.3
|
||||
env:
|
||||
POSTGRES_USER: mmuser
|
||||
POSTGRES_PASSWORD: mostest
|
||||
POSTGRES_DB: mattermost_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2019-10-11T00-38-09Z
|
||||
env:
|
||||
MINIO_ACCESS_KEY: minioaccesskey
|
||||
MINIO_SECRET_KEY: miniosecretkey
|
||||
MINIO_SSE_MASTER_KEY: "my-minio-key:6368616e676520746869732070617373776f726420746f206120736563726574"
|
||||
inbucket:
|
||||
image: mattermost/inbucket:release-1.2.0
|
||||
ports:
|
||||
- 10080:10080
|
||||
- 10110:10110
|
||||
- 10025:10025
|
||||
elasticsearch:
|
||||
image: mattermost/mattermost-elasticsearch-docker:7.0.0
|
||||
env:
|
||||
http.host: "0.0.0.0"
|
||||
http.port: 9200
|
||||
http.cors.enabled: "true"
|
||||
http.cors.allow-origin: "http://localhost:1358,http://127.0.0.1:1358"
|
||||
http.cors.allow-headers: "X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization"
|
||||
http.cors.allow-credentials: "true"
|
||||
transport.host: "127.0.0.1"
|
||||
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
|
||||
ports:
|
||||
- 9200:9200
|
||||
mattermost-server:
|
||||
image: mattermostdevelopment/mattermost-enterprise-edition:master
|
||||
env:
|
||||
DB_HOST: postgres
|
||||
DB_PORT_NUMBER: 5432
|
||||
MM_DBNAME: mattermost_test
|
||||
MM_USERNAME: mmuser
|
||||
MM_PASSWORD: mostest
|
||||
CI_INBUCKET_HOST: inbucket
|
||||
CI_INBUCKET_PORT: 10080
|
||||
CI_MINIO_HOST: minio
|
||||
IS_CI: true
|
||||
MM_LICENSE: "${{ secrets.MM_E2E_TEST_LICENSE_ONPREM_ENT }}"
|
||||
MM_CLUSTERSETTINGS_READONLYCONFIG: false
|
||||
MM_EMAILSETTINGS_SMTPSERVER: inbucket
|
||||
MM_EMAILSETTINGS_SMTPPORT: 10025
|
||||
MM_ELASTICSEARCHSETTINGS_CONNECTIONURL: http://elasticsearch:9200
|
||||
MM_EXPERIMENTALSETTINGS_USENEWSAMLLIBRARY: true
|
||||
MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:mostest@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10"
|
||||
MM_SQLSETTINGS_DRIVERNAME: postgres
|
||||
MM_PLUGINSETTINGS_ENABLEUPLOADS: true
|
||||
MM_SERVICESETTINGS_SITEURL: http://localhost:8065
|
||||
MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS: true
|
||||
MM_ANNOUNCEMENTSETTINGS_ADMINNOTICESENABLED: false
|
||||
MM_SERVICESETTINGS_ENABLELEGACYSIDEBAR: true
|
||||
MM_TEAMSETTINGS_MAXUSERSPERTEAM: 10000
|
||||
MM_SERVICESETTINGS_ENABLEONBOARDINGFLOW: false
|
||||
MM_SERVICEENVIRONMENT: test
|
||||
MM_SERVICESETTINGS_EXPERIMENTALSTRICTCSRFENFORCEMENT: false
|
||||
MM_SERVICESETTINGS_STRICTCSRFENFORCEMENT: false
|
||||
ports:
|
||||
- 8065:8065
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: ci/download-build-artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: playbooks-bundle-${{ steps.short-sha.outputs.sha }}
|
||||
path: dist
|
||||
|
||||
- name: ci/e2e-test
|
||||
uses: ./.github/actions/e2e-test
|
||||
with:
|
||||
CYPRESS_serverEdition: E20
|
||||
SPECS: ${{ matrix.specs }}
|
||||
|
||||
e2e-cypress-tests-fips:
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- go
|
||||
- lint
|
||||
- generate-specs
|
||||
- dist-fips
|
||||
name: e2e-cypress-tests-fips-run-${{ matrix.runId }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.generate-specs.outputs.specs) }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15.3
|
||||
env:
|
||||
POSTGRES_USER: mmuser
|
||||
POSTGRES_PASSWORD: mostest
|
||||
POSTGRES_DB: mattermost_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2019-10-11T00-38-09Z
|
||||
env:
|
||||
MINIO_ACCESS_KEY: minioaccesskey
|
||||
MINIO_SECRET_KEY: miniosecretkey
|
||||
MINIO_SSE_MASTER_KEY: "my-minio-key:6368616e676520746869732070617373776f726420746f206120736563726574"
|
||||
inbucket:
|
||||
image: mattermost/inbucket:release-1.2.0
|
||||
ports:
|
||||
- 10080:10080
|
||||
- 10110:10110
|
||||
- 10025:10025
|
||||
elasticsearch:
|
||||
image: mattermost/mattermost-elasticsearch-docker:7.0.0
|
||||
env:
|
||||
http.host: "0.0.0.0"
|
||||
http.port: 9200
|
||||
http.cors.enabled: "true"
|
||||
http.cors.allow-origin: "http://localhost:1358,http://127.0.0.1:1358"
|
||||
http.cors.allow-headers: "X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization"
|
||||
http.cors.allow-credentials: "true"
|
||||
transport.host: "127.0.0.1"
|
||||
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
|
||||
ports:
|
||||
- 9200:9200
|
||||
mattermost-server:
|
||||
image: mattermostdevelopment/mattermost-enterprise-fips-edition:master
|
||||
env:
|
||||
DB_HOST: postgres
|
||||
DB_PORT_NUMBER: 5432
|
||||
MM_DBNAME: mattermost_test
|
||||
MM_USERNAME: mmuser
|
||||
MM_PASSWORD: mostest
|
||||
CI_INBUCKET_HOST: inbucket
|
||||
CI_INBUCKET_PORT: 10080
|
||||
CI_MINIO_HOST: minio
|
||||
IS_CI: true
|
||||
MM_LICENSE: "${{ secrets.MM_E2E_TEST_LICENSE_ONPREM_ENT }}"
|
||||
MM_CLUSTERSETTINGS_READONLYCONFIG: false
|
||||
MM_EMAILSETTINGS_SMTPSERVER: inbucket
|
||||
MM_EMAILSETTINGS_SMTPPORT: 10025
|
||||
MM_ELASTICSEARCHSETTINGS_CONNECTIONURL: http://elasticsearch:9200
|
||||
MM_EXPERIMENTALSETTINGS_USENEWSAMLLIBRARY: true
|
||||
MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:mostest@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10"
|
||||
MM_SQLSETTINGS_DRIVERNAME: postgres
|
||||
MM_PLUGINSETTINGS_ENABLEUPLOADS: true
|
||||
MM_SERVICESETTINGS_SITEURL: http://localhost:8065
|
||||
MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS: true
|
||||
MM_ANNOUNCEMENTSETTINGS_ADMINNOTICESENABLED: false
|
||||
MM_SERVICESETTINGS_ENABLELEGACYSIDEBAR: true
|
||||
MM_TEAMSETTINGS_MAXUSERSPERTEAM: 10000
|
||||
MM_SERVICESETTINGS_ENABLEONBOARDINGFLOW: false
|
||||
MM_SERVICEENVIRONMENT: test
|
||||
MM_SERVICESETTINGS_EXPERIMENTALSTRICTCSRFENFORCEMENT: false
|
||||
MM_SERVICESETTINGS_STRICTCSRFENFORCEMENT: false
|
||||
ports:
|
||||
- 8065:8065
|
||||
steps:
|
||||
- name: ci/checkout-repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: "0"
|
||||
|
||||
- name: ci/get-short-sha
|
||||
id: short-sha
|
||||
run: echo "sha=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: ci/download-build-artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: playbooks-bundle-fips-${{ steps.short-sha.outputs.sha }}
|
||||
path: dist
|
||||
|
||||
- name: ci/e2e-test
|
||||
uses: ./.github/actions/e2e-test
|
||||
with:
|
||||
CYPRESS_serverEdition: E20
|
||||
SPECS: ${{ matrix.specs }}
|
||||
ARTIFACT_SUFFIX: -fips
|
||||
|
||||
report-test-results:
|
||||
if: always() && github.event.pull_request.head.repo.full_name == github.repository
|
||||
needs:
|
||||
- e2e-cypress-tests
|
||||
- e2e-cypress-tests-fips
|
||||
- test
|
||||
- test-fips
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
- name: ci/checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: ci/download-test-results
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
- name: ci/publish-results
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_SHA: ${{ github.sha }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
|
||||
run: python3 .github/scripts/junit_report.py
|
||||
45
core-plugins/mattermost-plugin-playbooks/.github/workflows/codeql-analysis.yml
vendored
Normal file
45
core-plugins/mattermost-plugin-playbooks/.github/workflows/codeql-analysis.yml
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '43 14 * * 2'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
permissions:
|
||||
security-events: write # for github/codeql-action/autobuild to send a status report
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go', 'javascript' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
debug: false
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
|
||||
# Autobuild attempts to build any compiled languages
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
# Perform Analysis
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
34
core-plugins/mattermost-plugin-playbooks/.gitignore
vendored
Normal file
34
core-plugins/mattermost-plugin-playbooks/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
.eslintcache
|
||||
.stylelintcache
|
||||
report.xml
|
||||
bin/
|
||||
dist/
|
||||
webapp/src/manifest.ts
|
||||
server/manifest.go
|
||||
server/data/
|
||||
server/e2etest.config.json
|
||||
server/logs/
|
||||
mprocs.yaml
|
||||
|
||||
# Mac
|
||||
.DS_Store
|
||||
|
||||
# Jetbrains
|
||||
.idea/
|
||||
|
||||
# VS Code
|
||||
.vscode
|
||||
|
||||
# Zed
|
||||
.zed
|
||||
|
||||
# Notice
|
||||
.notice-work
|
||||
.aider*
|
||||
.env
|
||||
|
||||
# Claude
|
||||
**/CLAUDE.local.md
|
||||
.claude
|
||||
CLAUDE.md
|
||||
.cursorrules
|
||||
4
core-plugins/mattermost-plugin-playbooks/.gitlab-ci.yml
Normal file
4
core-plugins/mattermost-plugin-playbooks/.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
include:
|
||||
- project: mattermost/ci/$CI_PROJECT_NAME
|
||||
ref: main
|
||||
file: private.yml
|
||||
1
core-plugins/mattermost-plugin-playbooks/.gitpod.yml
Normal file
1
core-plugins/mattermost-plugin-playbooks/.gitpod.yml
Normal file
|
|
@ -0,0 +1 @@
|
|||
mainConfiguration: https://github.com/mattermost/mattermost-gitpod-config
|
||||
77
core-plugins/mattermost-plugin-playbooks/.golangci.yml
Normal file
77
core-plugins/mattermost-plugin-playbooks/.golangci.yml
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
version: "2"
|
||||
linters:
|
||||
default: none
|
||||
enable:
|
||||
- copyloopvar
|
||||
- errcheck
|
||||
- gocheckcompilerdirectives
|
||||
- gosec
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- revive
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unused
|
||||
settings:
|
||||
govet:
|
||||
disable:
|
||||
- fieldalignment
|
||||
- unusedwrite
|
||||
enable-all: true
|
||||
revive:
|
||||
rules:
|
||||
- name: redefines-builtin-id
|
||||
disabled: true
|
||||
staticcheck:
|
||||
checks: ["all", "-QF1008"]
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- goconst
|
||||
- gosec
|
||||
- unparam
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- gocritic
|
||||
- revive
|
||||
path: server/bot/logger.go
|
||||
- linters:
|
||||
- revive
|
||||
text: unused-parameter
|
||||
- linters:
|
||||
- gosec
|
||||
text: G115
|
||||
- path: (.+)\.go$
|
||||
text: G404
|
||||
- path: (.+)\.go$
|
||||
text: 'shadow: declaration of "err" shadows declaration at'
|
||||
paths:
|
||||
- example.*.go
|
||||
- server/manifest.go
|
||||
- build/
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
settings:
|
||||
gofmt:
|
||||
simplify: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- example.*.go
|
||||
- server/manifest.go
|
||||
- build/
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
1
core-plugins/mattermost-plugin-playbooks/.nvmrc
Normal file
1
core-plugins/mattermost-plugin-playbooks/.nvmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
24.11
|
||||
3
core-plugins/mattermost-plugin-playbooks/.ts-prunerc
Normal file
3
core-plugins/mattermost-plugin-playbooks/.ts-prunerc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"ignore": "used in module|graphql/generated"
|
||||
}
|
||||
1
core-plugins/mattermost-plugin-playbooks/CODEOWNERS
Normal file
1
core-plugins/mattermost-plugin-playbooks/CODEOWNERS
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
39
core-plugins/mattermost-plugin-playbooks/LICENSE.enterprise
Normal file
39
core-plugins/mattermost-plugin-playbooks/LICENSE.enterprise
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
The Mattermost Source Available License license (the “Source Available License”)
|
||||
Copyright (c) 2015-present Mattermost
|
||||
|
||||
With regard to the Mattermost Software:
|
||||
|
||||
This software and associated documentation files (the "Software") may only be
|
||||
used in production, if you (and any entity that you represent) have agreed to,
|
||||
and are in compliance with, the Mattermost Terms of Service, available at
|
||||
https://mattermost.com/enterprise-edition-terms/ (the “EE Terms”), or other
|
||||
agreement governing the use of the Software, as agreed by you and Mattermost,
|
||||
and otherwise have a valid Mattermost Enterprise E20 subscription for the
|
||||
correct number of user seats. Subject to the foregoing sentence, you are free
|
||||
to modify this Software and publish patches to the Software. You agree that
|
||||
Mattermost and/or its licensors (as applicable) retain all right, title and
|
||||
interest in and to all such modifications and/or patches, and all such
|
||||
modifications and/or patches may only be used, copied, modified, displayed,
|
||||
distributed, or otherwise exploited with a valid Mattermost Enterprise E20
|
||||
Edition subscription for the correct number of user seats. Notwithstanding
|
||||
the foregoing, you may copy and modify the Software for development and testing
|
||||
purposes, without requiring a subscription. You agree that Mattermost and/or
|
||||
its licensors (as applicable) retain all right, title and interest in and to
|
||||
all such modifications. You are not granted any other rights beyond what is
|
||||
expressly stated herein. Subject to the foregoing, it is forbidden to copy,
|
||||
merge, publish, distribute, sublicense, and/or sell the Software.
|
||||
|
||||
The full text of this EE License shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
For all third party components incorporated into the Mattermost Software, those
|
||||
components are licensed under the original license provided by the owner of the
|
||||
applicable component.
|
||||
202
core-plugins/mattermost-plugin-playbooks/LICENSE.txt
Normal file
202
core-plugins/mattermost-plugin-playbooks/LICENSE.txt
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
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.
|
||||
404
core-plugins/mattermost-plugin-playbooks/Makefile
Normal file
404
core-plugins/mattermost-plugin-playbooks/Makefile
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
GO ?= $(shell command -v go 2> /dev/null)
|
||||
GOFLAGS ?= $(GOFLAGS:)
|
||||
NPM ?= $(shell command -v npm 2> /dev/null)
|
||||
CURL ?= $(shell command -v curl 2> /dev/null)
|
||||
MM_DEBUG ?=
|
||||
GOPATH ?= $(shell go env GOPATH)
|
||||
GO_TEST_FLAGS ?= -race
|
||||
GO_BUILD_FLAGS ?=
|
||||
MM_UTILITIES_DIR ?= ../mattermost-utilities
|
||||
DLV_DEBUG_PORT := 2346
|
||||
DEFAULT_GOOS ?= $(shell go env GOOS)
|
||||
DEFAULT_GOARCH ?= $(shell go env GOARCH)
|
||||
|
||||
export GO111MODULE=on
|
||||
|
||||
# We need to export GOBIN to allow it to be set
|
||||
# for processes spawned from the Makefile
|
||||
export GOBIN ?= $(PWD)/bin
|
||||
|
||||
# You can include assets this directory into the bundle. This can be e.g. used to include profile pictures.
|
||||
ASSETS_DIR ?= assets
|
||||
|
||||
## Define the default target (make all)
|
||||
.PHONY: default
|
||||
default: all
|
||||
|
||||
# Verify environment, and define PLUGIN_ID, PLUGIN_VERSION, HAS_SERVER and HAS_WEBAPP as needed.
|
||||
include build/setup.mk
|
||||
|
||||
BUNDLE_NAME ?= $(PLUGIN_ID)-$(PLUGIN_VERSION).tar.gz
|
||||
|
||||
# Include custom makefile, if present
|
||||
ifneq ($(wildcard build/custom.mk),)
|
||||
include build/custom.mk
|
||||
endif
|
||||
|
||||
ifneq ($(MM_DEBUG),)
|
||||
GO_BUILD_GCFLAGS = -gcflags "all=-N -l"
|
||||
else
|
||||
GO_BUILD_GCFLAGS =
|
||||
endif
|
||||
|
||||
# ====================================================================================
|
||||
# Semver release tagging
|
||||
# Usage: make tag-release [bump-type] [DRY_RUN=1] [FORCE=1] [VERSION=X.Y.Z] [RELEASE_ARGS="..."]
|
||||
# Examples:
|
||||
# make tag-release # Interactive mode
|
||||
# make tag-release patch # Bump patch version
|
||||
# make tag-release minor-rc # Start minor RC cycle
|
||||
# make tag-release rc-finalize # Finalize RC to stable
|
||||
# DRY_RUN=1 make tag-release patch # Dry run
|
||||
# FORCE=1 make tag-release patch # Force (skip validation errors)
|
||||
# VERSION=2.6.2 make tag-release # Explicit version
|
||||
# make tag-release RELEASE_ARGS="--version=2.6.2" # Explicit version (alternative)
|
||||
TAG_RELEASE_BUMP := $(word 2,$(MAKECMDGOALS))
|
||||
ifneq ($(filter tag-release,$(MAKECMDGOALS)),)
|
||||
ifneq ($(TAG_RELEASE_BUMP),)
|
||||
$(eval $(TAG_RELEASE_BUMP):;@:)
|
||||
endif
|
||||
endif
|
||||
RELEASE_ARGS ?=
|
||||
RELEASE_FLAGS := $(RELEASE_ARGS)
|
||||
ifneq ($(DRY_RUN),)
|
||||
RELEASE_FLAGS += --dry-run
|
||||
endif
|
||||
ifneq ($(FORCE),)
|
||||
RELEASE_FLAGS += --force
|
||||
endif
|
||||
ifneq ($(VERSION),)
|
||||
RELEASE_FLAGS += --version=$(VERSION)
|
||||
endif
|
||||
# ====================================================================================
|
||||
|
||||
.PHONY: tag-release
|
||||
## Tag a semver release interactively or with bump type (DRY_RUN=1, FORCE=1)
|
||||
tag-release:
|
||||
./build/bin/release $(TAG_RELEASE_BUMP) $(RELEASE_FLAGS)
|
||||
|
||||
## Checks the code style, tests, builds and bundles the plugin.
|
||||
.PHONY: all
|
||||
all: check-style test dist
|
||||
|
||||
## Ensures the plugin manifest is valid
|
||||
.PHONY: manifest-check
|
||||
manifest-check:
|
||||
./build/bin/manifest check
|
||||
|
||||
## Propagates plugin manifest information into the server/ and webapp/ folders.
|
||||
.PHONY: apply
|
||||
apply:
|
||||
./build/bin/manifest apply
|
||||
|
||||
## Install go tools
|
||||
install-go-tools:
|
||||
@echo Installing go tools
|
||||
$(GO) install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6
|
||||
$(GO) install github.com/golang/mock/mockgen@v1.6.0
|
||||
$(GO) install gotest.tools/gotestsum@v1.7.0
|
||||
$(GO) install github.com/cortesi/modd/cmd/modd@latest
|
||||
$(GO) install github.com/mattermost/mattermost-govet/v2@3f08281c344327ac09364f196b15f9a81c7eff08
|
||||
|
||||
## Runs eslint and golangci-lint
|
||||
.PHONY: check-style
|
||||
check-style: manifest-check apply webapp/node_modules e2e-tests/node_modules install-go-tools
|
||||
@echo Checking for style guide compliance
|
||||
|
||||
ifneq ($(HAS_WEBAPP),)
|
||||
cd webapp && npm run lint
|
||||
cd webapp && npm run check-types
|
||||
endif
|
||||
|
||||
cd e2e-tests && npm run check
|
||||
|
||||
# It's highly recommended to run go-vet first
|
||||
# to find potential compile errors that could introduce
|
||||
# weird reports at golangci-lint step
|
||||
ifneq ($(HAS_SERVER),)
|
||||
@echo Running golangci-lint
|
||||
$(GO) vet ./...
|
||||
$(GOBIN)/golangci-lint run ./...
|
||||
$(GO) vet -vettool=$(GOBIN)/mattermost-govet -license -license.year=2020 -license.ignore=server/graphql/models.go ./...
|
||||
endif
|
||||
|
||||
## Fix JS file ESLint issues
|
||||
.PHONY: fix-style
|
||||
fix-style: apply webapp/node_modules e2e-tests/node_modules
|
||||
@echo Fixing lint issues to follow style guide
|
||||
|
||||
ifneq ($(HAS_WEBAPP),)
|
||||
cd webapp && npm run fix
|
||||
endif
|
||||
cd e2e-tests && npm run fix
|
||||
|
||||
|
||||
## Builds the server, if it exists, for all supported architectures, unless MM_SERVICESETTINGS_ENABLEDEVELOPER is set
|
||||
.PHONY: server
|
||||
server:
|
||||
ifneq ($(HAS_SERVER),)
|
||||
ifneq ($(MM_DEBUG),)
|
||||
$(info DEBUG mode is on; to disable, unset MM_DEBUG)
|
||||
endif
|
||||
mkdir -p server/dist;
|
||||
ifneq ($(MM_SERVICESETTINGS_ENABLEDEVELOPER),)
|
||||
@echo Building plugin only for $(DEFAULT_GOOS)-$(DEFAULT_GOARCH) because MM_SERVICESETTINGS_ENABLEDEVELOPER is enabled
|
||||
cd server && env GOOS=$(DEFAULT_GOOS) GOARCH=$(DEFAULT_GOARCH) $(GO) build $(GO_BUILD_FLAGS) $(GO_BUILD_GCFLAGS) -trimpath -o dist/plugin-$(DEFAULT_GOOS)-$(DEFAULT_GOARCH);
|
||||
|
||||
ifneq ($(MM_DEBUG),)
|
||||
cd server && ./dist/plugin-$(DEFAULT_GOOS)-$(DEFAULT_GOARCH) graphqlcheck
|
||||
endif
|
||||
else
|
||||
cd server && env GOOS=linux GOARCH=amd64 $(GO) build $(GO_BUILD_FLAGS) $(GO_BUILD_GCFLAGS) -trimpath -o dist/plugin-linux-amd64;
|
||||
ifeq ($(FIPS_ENABLED),true)
|
||||
@echo Only building linux-amd64 for FIPS
|
||||
else
|
||||
cd server && env GOOS=linux GOARCH=arm64 $(GO) build $(GO_BUILD_FLAGS) $(GO_BUILD_GCFLAGS) -trimpath -o dist/plugin-linux-arm64;
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
## Ensures NPM dependencies are installed without having to run this all the time.
|
||||
webapp/node_modules: $(wildcard webapp/package.json)
|
||||
ifneq ($(HAS_WEBAPP),)
|
||||
cd webapp && $(NPM) install --ignore-scripts --legacy-peer-deps
|
||||
touch $@
|
||||
endif
|
||||
|
||||
## Ensures NPM dependencies are installed without having to run this all the time.
|
||||
e2e-tests/node_modules: $(wildcard e2e-tests/package.json)
|
||||
ifneq ($(HAS_WEBAPP),)
|
||||
cd e2e-tests && $(NPM) install
|
||||
touch $@
|
||||
endif
|
||||
|
||||
## Builds the webapp, if it exists.
|
||||
.PHONY: webapp
|
||||
webapp: webapp/node_modules
|
||||
ifneq ($(HAS_WEBAPP),)
|
||||
cd webapp && $(NPM) run graphql;
|
||||
ifeq ($(MM_DEBUG),)
|
||||
cd webapp && $(NPM) run build;
|
||||
else
|
||||
cd webapp && $(NPM) run debug;
|
||||
endif
|
||||
endif
|
||||
|
||||
## Generates a tar bundle of the plugin for install.
|
||||
.PHONY: bundle
|
||||
bundle:
|
||||
rm -rf dist/
|
||||
mkdir -p dist/$(PLUGIN_ID)
|
||||
./build/bin/manifest dist
|
||||
ifneq ($(wildcard LICENSE.txt),)
|
||||
cp -r LICENSE.txt dist/$(PLUGIN_ID)/
|
||||
endif
|
||||
ifneq ($(wildcard NOTICE.txt),)
|
||||
cp -r NOTICE.txt dist/$(PLUGIN_ID)/
|
||||
endif
|
||||
ifneq ($(wildcard $(ASSETS_DIR)/.),)
|
||||
cp -r $(ASSETS_DIR) dist/$(PLUGIN_ID)/
|
||||
endif
|
||||
ifneq ($(HAS_PUBLIC),)
|
||||
cp -r public dist/$(PLUGIN_ID)/public/
|
||||
endif
|
||||
ifneq ($(HAS_SERVER),)
|
||||
mkdir -p dist/$(PLUGIN_ID)/server
|
||||
cp -r server/dist dist/$(PLUGIN_ID)/server/
|
||||
endif
|
||||
ifneq ($(HAS_WEBAPP),)
|
||||
mkdir -p dist/$(PLUGIN_ID)/webapp
|
||||
cp -r webapp/dist dist/$(PLUGIN_ID)/webapp/
|
||||
endif
|
||||
ifeq ($(shell uname),Darwin)
|
||||
cd dist && tar --disable-copyfile -cvzf $(BUNDLE_NAME) $(PLUGIN_ID)
|
||||
else
|
||||
cd dist && tar -cvzf $(BUNDLE_NAME) $(PLUGIN_ID)
|
||||
endif
|
||||
|
||||
@echo plugin built at: dist/$(BUNDLE_NAME)
|
||||
|
||||
## Builds and bundles the plugin.
|
||||
.PHONY: dist
|
||||
dist: apply server webapp bundle
|
||||
|
||||
## Builds and installs the plugin to a server.
|
||||
.PHONY: deploy
|
||||
deploy: dist upload-to-server
|
||||
|
||||
## Builds and installs the plugin to a server, updating the plugin automatically when changed.
|
||||
.PHONY: watch
|
||||
watch: apply install-go-tools bundle upload-to-server
|
||||
$(GOBIN)/modd
|
||||
|
||||
## Watch mode for webapp side
|
||||
.PHONY: watch-webapp
|
||||
watch-webapp:
|
||||
ifeq ($(MM_DEBUG),)
|
||||
cd webapp && $(NPM) run build:watch
|
||||
else
|
||||
cd webapp && $(NPM) run debug:watch
|
||||
endif
|
||||
|
||||
## Builds and installs the plugin to a server, then starts the webpack dev server on 9005
|
||||
.PHONY: dev
|
||||
dev: apply server bundle webapp/node_modules
|
||||
cd webapp && $(NPM) run dev-server
|
||||
|
||||
## Installs a previous built plugin with updated webpack assets to a server.
|
||||
.PHONY: deploy-from-watch
|
||||
deploy-from-watch: bundle upload-to-server
|
||||
|
||||
.PHONY: upload-to-server
|
||||
upload-to-server:
|
||||
./build/bin/pluginctl deploy $(PLUGIN_ID) dist/$(BUNDLE_NAME)
|
||||
|
||||
## Setup dlv for attaching, identifying the plugin PID for other targets.
|
||||
.PHONY: setup-attach
|
||||
setup-attach:
|
||||
$(eval PLUGIN_PID := $(shell ps aux | grep "plugins/${PLUGIN_ID}" | grep -v "grep" | awk -F " " '{print $$2}'))
|
||||
$(eval NUM_PID := $(shell echo -n ${PLUGIN_PID} | wc -w))
|
||||
|
||||
@if [ ${NUM_PID} -gt 2 ]; then \
|
||||
echo "** There is more than 1 plugin process running. Run 'make kill reset' to restart just one."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
## Check if setup-attach succeeded.
|
||||
.PHONY: check-attach
|
||||
check-attach:
|
||||
@if [ -z ${PLUGIN_PID} ]; then \
|
||||
echo "Could not find plugin PID; the plugin is not running. Exiting."; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Located Plugin running with PID: ${PLUGIN_PID}"; \
|
||||
fi
|
||||
|
||||
## Attach dlv to an existing plugin instance.
|
||||
.PHONY: attach
|
||||
attach: setup-attach check-attach
|
||||
dlv attach ${PLUGIN_PID}
|
||||
|
||||
## Attach dlv to an existing plugin instance, exposing a headless instance on $DLV_DEBUG_PORT.
|
||||
.PHONY: attach-headless
|
||||
attach-headless: setup-attach check-attach
|
||||
dlv attach ${PLUGIN_PID} --listen :$(DLV_DEBUG_PORT) --headless=true --api-version=2 --accept-multiclient
|
||||
|
||||
## Detach dlv from an existing plugin instance, if previously attached.
|
||||
.PHONY: detach
|
||||
detach: setup-attach
|
||||
@DELVE_PID=$(shell ps aux | grep "dlv attach ${PLUGIN_PID}" | grep -v "grep" | awk -F " " '{print $$2}') && \
|
||||
if [ "$$DELVE_PID" -gt 0 ] > /dev/null 2>&1 ; then \
|
||||
echo "Located existing delve process running with PID: $$DELVE_PID. Killing." ; \
|
||||
kill -9 $$DELVE_PID ; \
|
||||
fi
|
||||
|
||||
## Runs any lints and unit tests defined for the server and webapp, if they exist.
|
||||
.PHONY: test
|
||||
test: apply webapp/node_modules install-go-tools
|
||||
ifneq ($(HAS_SERVER),)
|
||||
$(GOBIN)/gotestsum --format standard-verbose --junitfile report.xml -- ./...
|
||||
endif
|
||||
ifneq ($(HAS_WEBAPP),)
|
||||
cd webapp && $(NPM) run test;
|
||||
endif
|
||||
@echo "Running submodule tests..."
|
||||
cd client && $(GOBIN)/gotestsum --format standard-verbose --junitfile report.xml -- ./...
|
||||
cd build && $(GOBIN)/gotestsum --format standard-verbose --junitfile report.xml -- ./...
|
||||
|
||||
## Creates a coverage report for the server code.
|
||||
.PHONY: coverage
|
||||
coverage: apply webapp/node_modules
|
||||
ifneq ($(HAS_SERVER),)
|
||||
$(GO) test $(GO_TEST_FLAGS) -coverprofile=server/coverage.txt ./server/...
|
||||
$(GO) tool cover -html=server/coverage.txt
|
||||
endif
|
||||
|
||||
## Extract strings for translation from the source code.
|
||||
.PHONY: i18n-extract
|
||||
i18n-extract: i18n-extract-webapp i18n-extract-server
|
||||
|
||||
i18n-extract-webapp:
|
||||
ifneq ($(HAS_WEBAPP),)
|
||||
cd webapp && $(NPM) run extract
|
||||
endif
|
||||
|
||||
i18n-extract-server:
|
||||
ifneq ($(HAS_SERVER),)
|
||||
$(GO) install -modfile=go.tools.mod github.com/mattermost/mattermost-utilities/mmgotool
|
||||
mkdir -p server/i18n
|
||||
cp assets/i18n/en.json server/i18n/en.json
|
||||
cd server && $(GOBIN)/mmgotool i18n extract --portal-dir="" --skip-dynamic
|
||||
mv server/i18n/en.json assets/i18n/en.json
|
||||
rmdir server/i18n
|
||||
endif
|
||||
|
||||
## Exit on empty translation strings and translation source strings
|
||||
i18n-check:
|
||||
ifneq ($(HAS_SERVER),)
|
||||
$(GO) install -modfile=go.tools.mod github.com/mattermost/mattermost-utilities/mmgotool
|
||||
mkdir -p server/i18n
|
||||
cp assets/i18n/en.json server/i18n/en.json
|
||||
cd server && $(GOBIN)/mmgotool i18n clean-empty --portal-dir="" --check
|
||||
cd server && $(GOBIN)/mmgotool i18n check-empty-src --portal-dir=""
|
||||
rmdir server/i18n
|
||||
endif
|
||||
|
||||
## Disable the plugin.
|
||||
.PHONY: disable
|
||||
disable: detach
|
||||
./build/bin/pluginctl disable $(PLUGIN_ID)
|
||||
|
||||
## Enable the plugin.
|
||||
.PHONY: enable
|
||||
enable:
|
||||
./build/bin/pluginctl enable $(PLUGIN_ID)
|
||||
|
||||
## Generate derived types from schema files
|
||||
.PHONY: graphql
|
||||
graphql:
|
||||
cd webapp && npm run graphql
|
||||
$(GO) install github.com/jkrajniak/graphql-codegen-go@v1.2.1
|
||||
cd server && $(GOBIN)/graphql-codegen-go -schemas api/schema.graphqls -packageName graphql -out graphql/models.go
|
||||
|
||||
|
||||
## Reset the plugin, effectively disabling and re-enabling it on the server.
|
||||
.PHONY: reset
|
||||
reset: detach
|
||||
./build/bin/pluginctl reset $(PLUGIN_ID)
|
||||
|
||||
## Kill all instances of the plugin, detaching any existing dlv instance.
|
||||
.PHONY: kill
|
||||
kill: detach
|
||||
$(eval PLUGIN_PID := $(shell ps aux | grep "plugins/${PLUGIN_ID}" | grep -v "grep" | awk -F " " '{print $$2}'))
|
||||
|
||||
@for PID in ${PLUGIN_PID}; do \
|
||||
echo "Killing plugin pid $$PID"; \
|
||||
kill -9 $$PID; \
|
||||
done; \
|
||||
|
||||
## Clean removes all build artifacts.
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -fr dist/
|
||||
ifneq ($(HAS_SERVER),)
|
||||
rm -fr server/coverage.txt
|
||||
rm -fr server/dist
|
||||
endif
|
||||
ifneq ($(HAS_WEBAPP),)
|
||||
rm -fr webapp/junit.xml
|
||||
rm -fr webapp/dist
|
||||
rm -fr webapp/node_modules
|
||||
endif
|
||||
rm -fr build/bin/
|
||||
|
||||
.PHONY: logs
|
||||
logs:
|
||||
./build/bin/pluginctl logs $(PLUGIN_ID)
|
||||
|
||||
.PHONY: logs-watch
|
||||
logs-watch:
|
||||
./build/bin/pluginctl logs-watch $(PLUGIN_ID)
|
||||
|
||||
# Help documentation à la https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
|
||||
help:
|
||||
@cat Makefile build/*.mk | grep -v '\.PHONY' | grep -v '\help:' | grep -B1 -E '^[a-zA-Z0-9_.-]+:.*' | sed -e "s/:.*//" | sed -e "s/^## //" | grep -v '\-\-' | sed '1!G;h;$$!d' | awk 'NR%2{printf "\033[36m%-30s\033[0m",$$0;next;}1' | sort
|
||||
9127
core-plugins/mattermost-plugin-playbooks/NOTICE.txt
Normal file
9127
core-plugins/mattermost-plugin-playbooks/NOTICE.txt
Normal file
File diff suppressed because it is too large
Load diff
152
core-plugins/mattermost-plugin-playbooks/README.md
Normal file
152
core-plugins/mattermost-plugin-playbooks/README.md
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
# Mattermost Playbooks
|
||||
|
||||
[](https://github.com/mattermost/mattermost-plugin-playbooks/releases/latest)
|
||||
|
||||
Mattermost Playbooks allows your team to create and run playbooks from within Mattermost. For configuration and administration information visit our [documentation](https://docs.mattermost.com/guides/playbooks.html).
|
||||
|
||||

|
||||
|
||||
## Development Builds
|
||||
In your `mattermost-server` configuration (`config/config.json`), set the following values:
|
||||
|
||||
`ServiceSettings.EnableLocalMode: true`
|
||||
|
||||
`PluginSettings.EnableUploads: true`
|
||||
|
||||
and restart the server. Once done, the relevant `make` commands should be able to install builds. Those commands are:
|
||||
|
||||
`make deploy` - builds and installs the plugin a single time
|
||||
|
||||
`make watch` - continuously builds and installs when files change
|
||||
|
||||
which are run from the repo root.
|
||||
|
||||
## License
|
||||
|
||||
This repository is licensed under the Apache 2.0 License, except for the [server/enterprise](server/enterprise) directory which is licensed under the [Mattermost Source Available License](LICENSE.enterprise). See [Mattermost Source Available License](https://docs.mattermost.com/overview/faq.html#mattermost-source-available-license) to learn more.
|
||||
|
||||
Although a valid Mattermost Enterprise license is required to access all features if using this plugin in production, the [Mattermost Source Available License](LICENSE.txt) allows you to compile and test this plugin in development and testing environments without a Mattermost Enterprise license. As such, we welcome community contributions to this plugin.
|
||||
|
||||
If you're running Mattermost Starter and don't already have a valid license, you can obtain a trial license from **System Console > Edition and License**. If you're running the Team Edition of Mattermost, including when you run the server directly from source, you may instead configure your server to enable both testing (`ServiceSettings.EnableTesting`) and developer mode (`ServiceSettings.EnableDeveloper`). These settings are not recommended in production environments. See [Contributing](#contributing) to learn more about how to set up your development environment.
|
||||
|
||||
## Generating test data
|
||||
|
||||
To quickly test Mattermost Playbooks, use the following test commands to create playbook runs populated with random data:
|
||||
|
||||
- `/playbook test create-playbooks [total playbooks]` - Provide a number of total playbooks that will be created. The command creates one or more playbooks based on the given parameter.
|
||||
|
||||
* An example command looks like: `/playbook test create-playbooks 5`
|
||||
|
||||
- `/playbook test create-playbook-run [playbook ID] [timestamp] [playbook run name]` - Provide the ID of an existing playbook to which the current user has access, a timestamp, and a playbook run name. The command creates an ongoing playbook run with the creation date set to the specified timestamp.
|
||||
|
||||
* An example command looks like: `/playbook test create-playbook-run 6utgh6qg7p8ndeef9edc583cpc 2020-11-23 PR-Testing`
|
||||
|
||||
- `/playbook test bulk-data [ongoing] [ended] [days] [seed]` - Provide a number of ongoing and ended playbook runs, a number of days, and an optional random seed. The command creates the given number of ongoing and ended playbook runs, with creation dates randomly between `n` days ago and the day when the command was issued. The seed may be used to reproduce the same outcome on multiple invocations. Names are generated randomly.
|
||||
|
||||
* An example command looks like: `/playbook test bulk-data 10 3 342 2`
|
||||
|
||||
## Running E2E tests
|
||||
|
||||
When running E2E tests, the local `mattermost-server` configuration may be unexpectedly modified if either `on_prem_default_config.json` or `cloud_default_default_config.json` (depending on the server edition) has conflicting values for the same keys. This can be avoided by setting `CYPRESS_developerMode=true` when calling Cypress scripts. For example: `CYPRESS_developerMode=true npm run cypress:open`.
|
||||
|
||||
## How to Release
|
||||
|
||||
Run `make tag-release` to launch an interactive TUI for creating releases. The tool validates branch requirements, checks for conflicts, and creates signed tags.
|
||||
|
||||
### Standard Release Flow (with RC cycle)
|
||||
|
||||
The typical release process uses release candidates for testing before final release:
|
||||
|
||||
```bash
|
||||
# 1. Start RC cycle from master
|
||||
make tag-release minor-rc # Creates v2.7.0-rc1
|
||||
|
||||
# 2. Test, fix bugs, increment RC as needed
|
||||
make tag-release rc # Creates v2.7.0-rc2
|
||||
make tag-release rc # Creates v2.7.0-rc3
|
||||
|
||||
# 3. Finalize when ready
|
||||
make tag-release rc-finalize # Creates v2.7.0
|
||||
|
||||
# 4. Create release branch for future patches
|
||||
git branch release-2.7
|
||||
git push origin release-2.7
|
||||
```
|
||||
|
||||
### Patch Releases (hotfixes)
|
||||
|
||||
For hotfixes on existing releases, work from the release branch:
|
||||
|
||||
```bash
|
||||
git checkout release-2.6
|
||||
# ... fix bug ...
|
||||
make tag-release patch # Creates v2.6.2
|
||||
```
|
||||
|
||||
### Quick Reference
|
||||
|
||||
| Scenario | Branch | Command | Example |
|
||||
|----------|--------|---------|---------|
|
||||
| New minor | master | `minor` | v2.6.0 → v2.7.0 |
|
||||
| Start RC cycle | master | `minor-rc` | v2.6.0 → v2.7.0-rc1 |
|
||||
| Bump RC | master | `rc` | v2.7.0-rc1 → v2.7.0-rc2 |
|
||||
| Finalize RC | master | `rc-finalize` | v2.7.0-rc2 → v2.7.0 |
|
||||
| Hotfix | release-X.Y | `patch` | v2.6.1 → v2.6.2 |
|
||||
| Major release | master | `major` | v2.9.0 → v3.0.0 |
|
||||
|
||||
### Options
|
||||
|
||||
- **Interactive mode**: `make tag-release` (no arguments) launches a TUI menu
|
||||
- **Explicit version**: `VERSION=2.7.0 make tag-release`
|
||||
- **Dry run**: `DRY_RUN=1 make tag-release` to preview without executing
|
||||
- **Force mode**: `FORCE=1 make tag-release` to bypass validation errors (shows warnings instead)
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
This plugin contains both a server and web app portion. Read our documentation about the [Developer Workflow](https://developers.mattermost.com/extend/plugins/developer-workflow/) and [Developer Setup](https://developers.mattermost.com/extend/plugins/developer-setup/) for more information about developing and extending plugins.
|
||||
|
||||
For more information about contributing to Mattermost, and the different ways you can contribute, see [https://www.mattermost.org/contribute-to-mattermost](https://mattermost.com/contribute/?redirect_source=mm-org).
|
||||
|
||||
### Logging
|
||||
|
||||
Logging should use the logrus package (not `pluginAPI.Log`, `mlog`, or `log`). The standard logger is automatically wired into the pluginAPI and proxied through the server:
|
||||
|
||||
```go
|
||||
logger := logrus.WithField("playbook_run_id", playbookRunID)
|
||||
|
||||
err := findUserForPlaybookRunAndTeam(playbookRunID, userID, teamID)
|
||||
if err != nil {
|
||||
logrus.WithError(err).WithFields(logrus.Fields{
|
||||
"user_id": userID,
|
||||
"team_id": teamID,
|
||||
}).Warn("Failed to find user for playbook run and team")
|
||||
}
|
||||
```
|
||||
|
||||
A few guidelines when logging:
|
||||
* Use the appropriate level:
|
||||
* Error: an error log should require some human action to fix something upon receipt
|
||||
* Warn: a warning log might require investigation if it occurs in bulk, but does not require human action
|
||||
* Info: a information log provides context that will typically be logged by default
|
||||
* Debug: a debug log provides context that will typically be logged only on demand
|
||||
* Write static log messages (`Failed to find user for playbook run and team`) instead of interpolating parameters into the log message itself (`Failed to find user %s for playbook run %s and team %s`)
|
||||
* Use snake case when naming fields. Try to name these fields consistently with other usage.
|
||||
* Pass errors using `WithError`.
|
||||
* Use `WithFields` when passing more than one field that is not an `err`.
|
||||
* Common fields can be set once instead of being passed for every log
|
||||
|
||||
### DB Migrations
|
||||
|
||||
DB migrations should be placed in `sqlstore/migrations.go` as they are the ones being run at the moment.
|
||||
|
||||
After transitioning to a new migration schema, the `sqlstore/migrations/future` folder will be utilised.
|
||||
It would ease the transition if migrations are also added there for both drivers (mysql, postgres).
|
||||
All migrations in the `future` folder should have both migration directions - `up` and `down`.
|
||||
|
||||
## Popular searches for Help Wanted issues:
|
||||
|
||||
* [Help wanted tickets currently up for grab]([https://github.com/mattermost/mattermost-server/issues?q=is%3Aopen+is%3Aissue+label%3AArea%2FPlaybooks+label%3A%22Up+For+Grabs%22](https://github.com/mattermost/mattermost-plugin-playbooks/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22+label%3A%22Up+For+Grabs%22))
|
||||
* [Good first issue tickets]([https://github.com/mattermost/mattermost-server/issues?q=is%3Aopen+is%3Aissue+label%3AArea%2FPlaybooks+label%3A%22Good+First+Issue%22+label%3A%22Up+For+Grabs%22](https://github.com/mattermost/mattermost-plugin-playbooks/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+First+Issue%22))
|
||||
|
||||
For more information, join the discussion in the [`Developers: Playbooks` channel](https://community.mattermost.com/core/channels/developers-playbooks).
|
||||
222
core-plugins/mattermost-plugin-playbooks/assets/i18n/cs.json
Normal file
222
core-plugins/mattermost-plugin-playbooks/assets/i18n/cs.json
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} zapnul aktualizace statusu pro [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} vypnul aktualizace stavu pro běh [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here — @{{.Name}} vyžádal aktualizaci stavu pro [{{.RunName}}]({{.RunURL}}). \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} je účastník spuštění a chce se přidat k tomuto kanálu. Kterýkoliv člen kanálu ho může přizvat.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "Máte 0 přiřazených úkolů."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "Máte {{.Count}} přiřazený úkol, který má být odevzdán:",
|
||||
"few": "Máte {{.Count}} přiřazené úkoly, které mají být odevzdány:",
|
||||
"other": "Máte {{.Count}} přiřazených úkolů, které mají být odevzdány:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "Máte {{.Count}} přiřazený úkol:",
|
||||
"few": "Máte {{.Count}} přiřazených úkolů:",
|
||||
"other": "Máte {{.Count}} přiřazených úkolů:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "K odevzdání před {{.Count}} dny"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Vaše přiřazené úkoly"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "K odevzdání včera"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "K odevzdání dnes"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "K odevzdání za {{.Count}} den",
|
||||
"few": "K odevzdání za {{.Count}} dnů",
|
||||
"other": "K odevzdání za {{.Count}} dnů"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "Máte **{{.Count}} přiřazený úkol k odevzdání po dnešku**.",
|
||||
"few": "Máte **{{.Count}} přiřazených úkolů k odevzdání po dnešku**.",
|
||||
"other": "Máte **{{.Count}} přiřazených úkolů k odevzdání po dnešku**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Prosím použijte `/playbook todo` k zobrazení všech Vašich úkolů."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Máte 0 aktuálních běhů."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "Máte {{.Count}} aktuální běh:",
|
||||
"few": "Máte {{.Count}} aktuální běhy:",
|
||||
"other": "Máte {{.Count}} aktuálních běhů:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Aktuální běhy"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "Máte 0 běhů po termínu."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "Máte {{.Count}} běh po termínu pro aktualizaci statusu:",
|
||||
"few": "Máte {{.Count}} běhů po termínu pro status update:",
|
||||
"other": "Máte {{.Count}} běhů po termínu pro status update:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Aktualizace statusu po termínu"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Není možné provést příkaz."
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Dokončit"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Krátký souhrn zobrazený na časové ose"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Přidat do časové osy běhu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "Existuje {{.Count}} nevyřízený úkol. Opravdu chcete dokončit *{{.RunName}} pro všechny účastníky?",
|
||||
"few": "Existují **{{.Count}} nevyřízené úkoly**. Opravdu chcete dokončit *{{.RunName}}* pro všechny účastníky?",
|
||||
"other": "Existuje **{{.Count}} nevyřízených úkoly**. Opravdu chcete dokončit *{{.RunName}}* pro všechny účastníky?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Vlastník** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Název běhu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Spustit běh"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Spustit playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Popis"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Název"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Přidat úkol"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Přidat nový úkol"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbook běh"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Přidat do časové osy běhu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Souhrn"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Max. 64 znaků"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Dokončit běh"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Také označit jako dokončené"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Potvrdit dokončení"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Změn od poslední aktualizace stavu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Poskytnout aktualizaci pro zúčastněné. Tato zpráva bude odeslána do {{.Count}} kanálu.",
|
||||
"few": "Poskytnout aktualizaci pro zúčastněné. Tato zpráva bude odeslána do {{.Count}} kanálů.",
|
||||
"other": "Poskytnout aktualizaci pro zúčastněné. Tato zpráva bude odeslána do {{.Count}} kanálů."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Připomínka pro další aktualizaci"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Příspěvek"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Aktualizace stavu"
|
||||
},
|
||||
{
|
||||
"id": "playbooks.checklist.condition.reason.modified",
|
||||
"translation": "zobrazeno, protože úkol byl upraven"
|
||||
}
|
||||
]
|
||||
234
core-plugins/mattermost-plugin-playbooks/assets/i18n/de.json
Normal file
234
core-plugins/mattermost-plugin-playbooks/assets/i18n/de.json
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "Du hast eine zugewiesene Aufgabe:",
|
||||
"other": "Du hast insgesamt {{.Count}} zugewiesene Aufgaben:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "Du hast keine zugewiesene Aufgabe."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "Du hast eine zugewiesene Aufgabe, die jetzt fällig ist:",
|
||||
"other": "Du hast {{.Count}} zugewiesene Aufgaben, die jetzt fällig sind:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Deine zugewiesenen Aufgaben"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Gestern fällig"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Fällig vor {{.Count}} Tagen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Fällig heute"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Fällig morgen",
|
||||
"other": "Fällig in {{.Count}} Tagen"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "Du hast **eine zugewiesene Aufgabe, die nach dem heutige Tag fällig ist**.",
|
||||
"other": "Du hast **{{.Count}} zugewiesene Aufgaben, die nach dem heutige Tag fällig sind**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Bitte benutze `/playbook todo` um alle deine Aufgaben anzuzeigen."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Du hast keinen aktiven Durchlauf."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "Du hast einen aktiven Durchlauf:",
|
||||
"other": "Du hast {{.Count}} aktive Durchläufe:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Aktive Durchläufe"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "Du hast keine überfälligen Durchläufe."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "Du hast einen überfälligen Durchlauf für einen Statusaktualisierung:",
|
||||
"other": "Du hast {{.Count}} überfällige Durchläufe für einen Statusaktualisierung:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Überfällige Statusaktualisierungen"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Kann Befehl nicht ausführen."
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here — @{{.Name}} hat eine Statusaktualisierung für [{{.RunName}}]({{.RunURL}}) angefordert. \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_get_involved",
|
||||
"translation": "@here - @{{.Name}} möchten an diesem Durchlauf teilnehmen. Um sie als Teilnehmer hinzuzufügen, füge sie bitte diesem Kanal hinzu.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.retro_publish",
|
||||
"translation": "@{{.Name}} hat eine Retrospektive veröffentlicht\n[Ganze Retrospektive ansehen]({{.URL}})\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_participate",
|
||||
"translation": "@{{.Name}} nimmt am Durchlauf teil. @{{.Name}} wurde nicht automatisch zu diesem privaten Kanal hinzugefügt, aber jedes Mitglied kann @{{.Name}} hinzufügen.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_add_participant",
|
||||
"translation": "@{{.Name}} wurde zum Durchlauf von @{{.RequesterName}} hinzugefügt. @{{.Name}} wurde nicht zum Kanal hinzugefügt, da @{{.RequesterName}} kein Mitglied des privaten Kanals ist.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} ist ein Teilnehmer und möchte diesem Kanal beitreten. Jedes Mitglied des Kanals kann ihn einladen.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} hat die Statusaktualisierungen für [{{.RunName}}]({{.RunURL}}) aktiviert"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} hat die Statusaktualisierungen für [{{.RunName}}]({{.RunURL}}) deaktiviert"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Statusaktualisierung"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Nachricht"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Erinnerung an das nächste Update"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Bringe die Beteiligten auf den neuesten Stand. Dieser Beitrag wird in einem Kanal veröffentlicht.",
|
||||
"other": "Bringe die Beteiligten auf den neuesten Stand. Dieser Beitrag wird in {{.Count}} Kanälen veröffentlicht."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Markiere den Durchlauf auch als beendet"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Durchlauf beenden"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Änderung seit der letzten Aktualisierung"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Beenden des Durchlaufs bestätigen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Durchlauf beenden"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "Es gibt eine **{{.Count}} offene Aufgabe**. Bist du sicher, dass du den Durchlauf *{{.RunName}}* für alle Teilnehmer beenden willst?",
|
||||
"other": "Es gibt **{{.Count}} offene Aufgaben**. Bist du sicher, dass du den Durchlauf *{{.RunName}}* für alle Teilnehmer beenden willst?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Zur Zeitleiste hinzufügen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Kurze Zusammenfassung auf der Zeitachse"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Max. 64 Zeichen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Zusammenfassung"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Zur Zeitleiste hinzufügen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbook-Durchlauf"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Neue Aufgabe hinzufügen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Aufgabe hinzufügen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Name"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Beschreibung"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Playbook starten"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Starte Durchlauf"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Durchlaufname"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.new_playbook",
|
||||
"translation": "[Klicke hier]({{.RunURL}}), um dein eigenes Playbook zu erstellen."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Eigentümer** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "playbooks.checklist.condition.reason.modified",
|
||||
"translation": "angezeigt, weil die Aufgabe geändert wurde"
|
||||
}
|
||||
]
|
||||
214
core-plugins/mattermost-plugin-playbooks/assets/i18n/en.json
Normal file
214
core-plugins/mattermost-plugin-playbooks/assets/i18n/en.json
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
[
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Unable to execute command."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Overdue Status Updates"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "You have {{.Count}} run overdue for a status update:",
|
||||
"other": "You have {{.Count}} runs overdue for a status update:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "You have 0 runs overdue."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Runs in Progress"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "You have {{.Count}} run currently in progress:",
|
||||
"other": "You have {{.Count}} runs currently in progress:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "You have 0 runs currently in progress."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Please use `/playbook todo` to see all your tasks."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "You have **{{.Count}} assigned task due after today**.",
|
||||
"other": "You have **{{.Count}} assigned tasks due after today**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Due in {{.Count}} day",
|
||||
"other": "Due in {{.Count}} days"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Due today"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Due {{.Count}} days ago"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Due yesterday"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Your assigned tasks"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "You have {{.Count}} assigned task:",
|
||||
"other": "You have {{.Count}} total assigned tasks:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "You have {{.Count}} assigned task that is now due:",
|
||||
"other": "You have {{.Count}} assigned tasks that are now due:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "You have 0 assigned tasks."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Owner** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Run name"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Start run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Run playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Description"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Name"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Add task"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Add new task"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbook Run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Add to run timeline"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Summary"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Max 64 chars"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Short summary shown in the timeline"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Add to run timeline"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "There is **{{.Count}} outstanding task**. Are you sure you want to finish *{{.RunName}}* for all participants?",
|
||||
"other": "There are **{{.Count}} outstanding tasks**. Are you sure you want to finish *{{.RunName}}* for all participants?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Finish"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Confirm finish"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} is a run participant and wants join this channel. Any member of the channel can invite them.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here — @{{.Name}} requested a status update for [{{.RunName}}]({{.RunURL}}). \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} disabled the status updates for [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} enabled the status updates for [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Change since last update"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Finish run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Also mark the run as finished"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Provide an update to the stakeholders. This post will be broadcasted to {{.Count}} channel.",
|
||||
"other": "Provide an update to the stakeholders. This post will be broadcasted to {{.Count}} channels."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Reminder for next update"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Post"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Status update"
|
||||
},
|
||||
{
|
||||
"id": "playbooks.checklist.condition.reason.modified",
|
||||
"translation": "shown because the task was modified"
|
||||
}
|
||||
]
|
||||
210
core-plugins/mattermost-plugin-playbooks/assets/i18n/fa.json
Normal file
210
core-plugins/mattermost-plugin-playbooks/assets/i18n/fa.json
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Due in {{.Count}} day",
|
||||
"other": "Due in {{.Count}} days"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "You have 0 runs overdue."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Runs in Progress"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "You have 0 runs currently in progress."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Please use `/playbook todo` to see all your tasks."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Due today"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Due {{.Count}} days ago"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Due yesterday"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Your assigned tasks"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "You have 0 assigned tasks."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Owner** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Unable to execute command."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Overdue Status Updates"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbook Run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Start run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Run playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Add to run timeline"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Summary"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Max 64 chars"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Short summary shown in the timeline"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Add to run timeline"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "There is **{{.Count}} outstanding task**. Are you sure you want to finish the run *{{.RunName}}* for all participants?",
|
||||
"other": "There are **{{.Count}} outstanding tasks**. Are you sure you want to finish the run *{{.RunName}}* for all participants?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Finish run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "You have {{.Count}} assigned task that is now due:",
|
||||
"other": "You have {{.Count}} assigned tasks that are now due:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} is a run participant and wants join this channel. Any member of the channel can invite them.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here — @{{.Name}} requested a status update for [{{.RunName}}]({{.RunURL}}). \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} disabled the status updates for [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} enabled the status updates for [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Change since last update"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Finish run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Also mark the run as finished"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "You have {{.Count}} run overdue for a status update:",
|
||||
"other": "You have {{.Count}} runs overdue for a status update:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "You have {{.Count}} run currently in progress:",
|
||||
"other": "You have {{.Count}} runs currently in progress:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "You have **{{.Count}} assigned task due after today**.",
|
||||
"other": "You have **{{.Count}} assigned tasks due after today**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Description"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Name"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Add task"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Add new task"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Reminder for next update"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Update status"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Status update"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "You have {{.Count}} assigned task:",
|
||||
"other": "You have {{.Count}} total assigned tasks:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Run name"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Confirm finish run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Provide an update to the stakeholders. This post will be broadcasted to {{.Count}} channel.",
|
||||
"other": "Provide an update to the stakeholders. This post will be broadcasted to {{.Count}} channels."
|
||||
}
|
||||
}
|
||||
]
|
||||
250
core-plugins/mattermost-plugin-playbooks/assets/i18n/hr.json
Normal file
250
core-plugins/mattermost-plugin-playbooks/assets/i18n/hr.json
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_outstanding",
|
||||
"translation": "Imaš 0 neobavljenih zadataka."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_outstanding",
|
||||
"translation": {
|
||||
"one": "Imaš {{.Count}} neobavljeni zadatak:",
|
||||
"few": "Imaš {{.Count}} neobavljena zadatka:",
|
||||
"other": "Imaš {{.Count}} neobavljenih zadataka:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Tebi dodijeljeni zadaci"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Imaš 0 izvođenja trenutačno u tijeku."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "Imaš {{.Count}} izvođenje trenutačno u tijeku:",
|
||||
"few": "Imaš {{.Count}} izvođenja trenutačno u tijeku:",
|
||||
"other": "Imaš {{.Count}} izvođenja trenutačno u tijeku:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Izvođenja u tijeku"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "Imaš 0 izvođenja s prekoračenjem roka."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "Imaš {{.Count}} izvođenje s prekoračenjem roka za aktualiziranje stanja:",
|
||||
"few": "Imaš {{.Count}} izvođenja s prekoračenjem roka za aktualiziranje stanja:",
|
||||
"other": "Imaš {{.Count}} izvođenja s prekoračenjem roka za aktualiziranje stanja:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Aktualiziranja stanja prekoračenja roka"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Nije moguće izvršiti naredbu."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "Nemaš nijedan dodijeljeni zadatak."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "Imaš {{.Count}} dodijeljen zadatak:",
|
||||
"few": "Imaš {{.Count}} dodijeljena zadatka:",
|
||||
"other": "Imaš {{.Count}} dodijeljenih zadataka:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Koristi `/playbook todo` za prikaz svih tvojih zadataka."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "Imaš {{.Count}} dodijeljen zadatak čiji je rok sada:",
|
||||
"few": "Imaš {{.Count}} dodijeljena zadatka čiji su rokovi sada:",
|
||||
"other": "Imaš {{.Count}} dodijeljenih zadataka čiji su rokovi sada:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Rok: jučer"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Rok: prije {{.Count}} dana"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Rok je danas"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Rok: {{.Count}} dan",
|
||||
"few": "Rok: {{.Count}} dana",
|
||||
"other": "Rok: {{.Count}} dana"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "Imaš **{{.Count}} dodijeljen zadatak s rokom nakon danas**.",
|
||||
"few": "Imaš **{{.Count}} dodijeljena zadatka s rokom nakon danas**.",
|
||||
"other": "Imaš **{{.Count}} dodijeljenih zadataka s rokom nakon danas**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here – @{{.Name}} je zatražio/la aktualiziranje stanja za [{{.RunName}}]({{.RunURL}}). \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_get_involved",
|
||||
"translation": "@here – @{{.Name}} želi sudjelovati u ovom izvođenju. Da bi postali članovi izvođenja, dodaj ih ovom kanalu.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_add_participant",
|
||||
"translation": "@{{.RequesterName}} je dodao/la @{{.Name}} u izvođenje . Nisu automatski dodani u kanal jer @{{.RequesterName}} nije član kanala, a kanal je privatan.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_participate",
|
||||
"translation": "@{{.Name}} se pridružio/la izvođenju. Nisu automatski dodani na ovaj privatni kanal, ali bilo koji član kanala ih može pozvati da se pridruže.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} je sudionik izvođenja i želi se pridružiti ovom kanalu. Svaki član kanala ih može pozvati.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} je aktivirao/la aktualiziranja stanja za [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} je deaktivirao/la aktualiziranja stanja za [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Aktualiziranje stanja"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Objava"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Podsjetnik za sljedeće aktualiziranje"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Obavijesti sudionike o aktualnom stanju. Ova će se objava poslati na {{.Count}} kanal.",
|
||||
"few": "Obavijesti sudionike o aktualnom stanju. Ova će se objava poslati na {{.Count}} kanala.",
|
||||
"other": "Obavijesti sudionike o aktualnom stanju. Ova će se objava poslati na {{.Count}} kanala."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Također, označi izvođenje kao završeno"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Završi izvođenje"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Promjena od zadnjeg aktualiziranja"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Potvrdi završavanje"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Završi"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "Postoji **{{.Count}} neobavljen zadatak**. Stvarno želiš završiti *{{.RunName}}* za sve sudionike?",
|
||||
"few": "Postoje **{{.Count}} neobavljena zadatka**. Stvarno želiš završiti *{{.RunName}}* za sve sudionike?",
|
||||
"other": "Postoji **{{.Count}} neobavljenih zadataka**. Stvarno želiš završiti *{{.RunName}}* za sve sudionike?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Dodaj u vremensku crtu izvođenja"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Kratak sažetak prikazan na vremenskoj crti"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Maks. 64 znakova"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Sažetak"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Dodaj u vremensku crtu izvođenja"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Izvođenje priručnika"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Dodaj novi zadatak"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Dodaj zadatak"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Ime"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Opis"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Priručnik izvođenja"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Pokreni izvođenje"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Ime izvođenja"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.new_playbook",
|
||||
"translation": "[Pritisni ovdje]({{.RunURL}}) za izradu vlastitog priručnika."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Vlasnik** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "playbooks.checklist.condition.reason.modified",
|
||||
"translation": "prikazano, jer je zadatak promijenjen"
|
||||
}
|
||||
]
|
||||
230
core-plugins/mattermost-plugin-playbooks/assets/i18n/hu.json
Normal file
230
core-plugins/mattermost-plugin-playbooks/assets/i18n/hu.json
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "Önnek van **{{.Count}} Önhöz rendelt feladat aminek a határideje ma után van**.",
|
||||
"other": "Önnek van **{{.Count}} Önhöz rendelt feladat aminek a határideje ma után van**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "Önhöz {{.Count}} feladat van hozzárendelve:",
|
||||
"other": "Önhöz {{.Count}} feladat van hozzárendelve:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Határidő lejár {{.Count}} nap múlva",
|
||||
"other": "Határidő lejár {{.Count}} nap múlva"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "Önnek {{.Count}} Önhöz rendelt feladata van késedelemben:",
|
||||
"other": "Önnek {{.Count}} Önhöz rendelt feladata van késedelemben:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_get_involved",
|
||||
"translation": "@here — @{{.Name}} részt szeretne venni ebben a futásban. Ahhoz, hogy résztvevők lehessenek, adja hozzá őket ehhez a csatornához.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here — @{{.Name}} kért egy állapot frissítést a [{{.RunName}}]({{.RunURL}}) futásról. \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "Önhöz 0 feladat van hozzárendelve."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Önhöz rendelt feladatok"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Tegnap lejárt"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Határidő lejárt {{.Count}} nappal ezelőtt"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Határidő ma"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Kérem használja a `/playbook todo` parancsot az összes feladat megtekintéséhez."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Önnek 0 futása van éppen folyamatban."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "Önnek {{.Count}} futása van lejárt állapot frissítéssel:",
|
||||
"other": "Önnek {{.Count}} futása van lejárt állapot frissítéssel:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "Önnek {{.Count}} futása van folyamatban:",
|
||||
"other": "Önnek {{.Count}} futása van folyamatban:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "Önnek 0 futása van késedelemben."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Késedelmes állapot frissítések"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Nem sikerült végrehajtani a parancsot."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Futások folyamatban"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.retro_publish",
|
||||
"translation": "@{{.Name}} közzétett egy visszatekintőt\n[A teljes visszatekintő megtekintése]({{.URL}})\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} egy futás résztvevője és csatlakozni szeretne ehhez a csatornához. A csatorna bármelyik tagja meghívhatja.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_add_participant",
|
||||
"translation": "@{{.Name}} a @{{.RequesterName}} által hozzá lett adva a futáshoz. Nem kerültek automatikusan hozzá a csatornához, mivel @{{.RequesterName}} nem tagja a csatornának, és a csatorna privát.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_participate",
|
||||
"translation": "@{{.Name}} csatlakozott a futáshoz. Nem kerültek automatikusan hozzádásra a privát csatornához, de a csatorna bármelyik tagja meghívhatja őket, hogy csatlakozzanak.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} engedélyezte az állapot frissítést a [{{.RunName}}]({{.RunURL}}) futáshoz"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} letiltotta az állapot frissítést a [{{.RunName}}]({{.RunURL}}) futáshoz"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Tulajdonos** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Feladat hozzáadása"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Rövid összefoglaló az idővonalon"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "Van **{{.Count}} elmaradt feladat**. Biztos, hogy be kívánja fejezni a *{{.RunName}}* futást minden résztvevő számára?",
|
||||
"other": "Van **{{.Count}} elmaradt feladat**. Biztos, hogy be kívánja fejezni a *{{.RunName}}* futást minden résztvevő számára?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Tájékoztassa az érdekelt feleket. Ezt a bejegyzést {{.Count}} csatornában fogjuk közzétenni.",
|
||||
"other": "Tájékoztassa az érdekelt feleket. Ezt a bejegyzést {{.Count}} csatornában fogjuk közzétenni."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Futás neve"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Futás indítása"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Forgatókönyv indítása"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Leírás"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Név"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Új feladat hozzáadása"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Forgatókönyv futás"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Hozzáadás a futás idővonalához"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Összefoglaló"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Max 64 karakter"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Hozzáadás a futás idővonalához"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Befejezés"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Befejezés jóváhagyása"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Módosítások az utolsó frissítés óta"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Futás befejezése"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Továbbá a futás megjelölése befejezettként"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Emlékeztető a következő frissítéshez"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Küldés"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Állapot frissítés"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Forgatókönyv"
|
||||
},
|
||||
{
|
||||
"id": "playbooks.checklist.condition.reason.modified",
|
||||
"translation": "megjelenítve mert a feladat módosult"
|
||||
}
|
||||
]
|
||||
202
core-plugins/mattermost-plugin-playbooks/assets/i18n/id.json
Normal file
202
core-plugins/mattermost-plugin-playbooks/assets/i18n/id.json
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
[
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Tidak dapat menjalankan perintah."
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Selesai menjalankan"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Konfirmasi selesai menjalankan"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Menambahkan tugas baru"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Anda memiliki 0 run yang sedang berjalan saat ini."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Gunakan `/playbook todo` untuk melihat semua tugas Anda."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Jatuh tempo hari ini"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Karena kemarin"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Jatuh tempo {{.Count}} hari yang lalu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Tugas yang Anda berikan"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "Anda memiliki 0 tugas yang diberikan."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Pemilik** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Jalankan nama"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Mulai jalankan"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Menjalankan buku pedoman"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Deskripsi"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Nama"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Tambahkan tugas"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Pembaruan Status yang Terlambat"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "Anda memiliki 0 tunggakan."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Berjalan dalam Proses"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbook Run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Menambahkan ke timeline yang sedang berjalan"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Ringkasan"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Maksimal 64 karakter"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Rangkuman singkat yang ditampilkan di timeline"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Menambahkan ke timeline yang sedang berjalan"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} adalah peserta lari dan ingin bergabung dengan saluran ini. Setiap anggota saluran dapat mengundang mereka.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here - @{{.Name}} meminta pembaruan status untuk [{{.RunName}}]({{.RunURL}}). \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} menonaktifkan pembaruan status untuk [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} mengaktifkan pembaruan status untuk [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Perubahan sejak pembaruan terakhir"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Selesai menjalankan"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Juga tandai lari sebagai selesai"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Pengingat untuk pembaruan berikutnya"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Perbarui status"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Pembaruan status"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"other": "There is **{{.Count}} outstanding task**. Are you sure you want to finish the run *{{.RunName}}* for all participants?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"other": "You have {{.Count}} run currently in progress:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"other": "Due in {{.Count}} day"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"other": "You have **{{.Count}} assigned task due after today**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"other": "You have {{.Count}} assigned task that is now due:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"other": "You have {{.Count}} assigned task:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"other": "You have {{.Count}} run overdue for a status update:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"other": "Provide an update to the stakeholders. This post will be broadcasted to {{.Count}} channel."
|
||||
}
|
||||
}
|
||||
]
|
||||
206
core-plugins/mattermost-plugin-playbooks/assets/i18n/ja.json
Normal file
206
core-plugins/mattermost-plugin-playbooks/assets/i18n/ja.json
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "ステータスの更新"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "投稿"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "次回更新のお知らせ"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"other": "関係者に最新情報を提供します。この投稿は {{.Count}} チャンネルにブロードキャストされます。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "また、実行を終了としてマークする"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "実行を終了する"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} は [{{.RunName}}]({{.RunURL}}) のステータス更新を有効化しました"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "前回更新時からの変更点"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} は [{{.RunName}}]({{.RunURL}}) のステータス更新を無効化しました"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here — @{{.Name}} は [{{.RunName}}]({{.RunURL}}) のステータス変更を要求しました。 \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} は実行の参加者で、このチャンネルに参加したいようです。チャンネルのメンバーなら誰でも招待できます。\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "終了の確認"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "実行を終了する"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"other": "**{{.Count}} 個の未処理のタスク**があります。 すべての参加者の実行 *{{.RunName}}* を終了してもよろしいですか?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "実行タイムラインに追加する"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "タイムラインに表示される短い要約"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "最大 64 文字"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "説明"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "概要"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "実行タイムラインに追加する"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbookを実行"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "新しいタスクを追加"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "タスクを追加"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "名前"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Playbookを実行"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "実行開始"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "実行名"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.new_playbook",
|
||||
"translation": "[ここ]({{.RunURL}}) をクリックして、独自のPlaybookを作成することができます。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**オーナー** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "割り当てられたタスクは0件です。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"other": "割り当てられたタスクは合計で {{.Count}} 件です:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"other": "現在、期限切れのタスクが {{.Count}} 件割り当てられています:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "あなたに割り当てられたタスク"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "期限は昨日まででした"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "期限 {{.Count}} 日前"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "期限は本日までです"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"other": "{{.Count}} 日後に期限切れになります"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"other": "今日以降に期限切れとなるタスクが **{{.Count}} 件割り当てられています**。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "`/playbook todo`を使用して、すべてのタスクを見ることができます。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "進行中の実行は 0 です。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"other": "現在、 {{.Count}} の実行が進行中です:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "進行中の実行"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"other": "ステータス更新の期日が過ぎた実行が {{.Count}} あります:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "期限切れの実行は 0 です。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "期限切れステータスの更新"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "コマンドを実行できませんでした。"
|
||||
}
|
||||
]
|
||||
202
core-plugins/mattermost-plugin-playbooks/assets/i18n/ko.json
Normal file
202
core-plugins/mattermost-plugin-playbooks/assets/i18n/ko.json
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "오늘 마감"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "실행 완료"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "실행 완료 확인"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "여기 - @{{.Name}}이 [{{.RunName}}]({{.RunURL}})에 대한 상태 업데이트를 요청했습니다. \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "만료 {{.Count}}일 전"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "어제 마감"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "할당된 작업"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "할당된 작업이 0개입니다."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "플레이북"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "실행 이름"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "실행 시작"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "플레이북 실행하기"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "설명"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "이름"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "작업 추가"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "새 작업 추가"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "플레이북 실행"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "실행 타임라인에 추가"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "요약"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "마지막 업데이트 이후 변경 사항"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "실행 완료"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "최대 64자"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "타임라인에 표시되는 짧은 요약"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "실행 타임라인에 추가"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "또한 실행을 완료로 표시합니다."
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} disabled the status updates for [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} enabled the status updates for [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} is a run participant and wants join this channel. Any member of the channel can invite them.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**소유자** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "현재 진행 중인 실행이 0건입니다."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "모든 작업을 보려면 `/플레이북 할 일`을 사용하세요."
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "다음 업데이트 알림"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "업데이트 상태"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "상태 업데이트"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "명령을 실행할 수 없습니다."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "연체 상태 업데이트"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "연체된 런이 0개입니다."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "진행 중 실행"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"other": "Due in {{.Count}} day"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"other": "You have {{.Count}} assigned task:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"other": "You have {{.Count}} assigned task that is now due:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"other": "There is **{{.Count}} outstanding task**. Are you sure you want to finish the run *{{.RunName}}* for all participants?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"other": "Provide an update to the stakeholders. This post will be broadcasted to {{.Count}} channel."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"other": "You have {{.Count}} run currently in progress:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"other": "You have **{{.Count}} assigned task due after today**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"other": "You have {{.Count}} run overdue for a status update:"
|
||||
}
|
||||
}
|
||||
]
|
||||
210
core-plugins/mattermost-plugin-playbooks/assets/i18n/mn.json
Normal file
210
core-plugins/mattermost-plugin-playbooks/assets/i18n/mn.json
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "Танд {{.Count}}-н хариуцсан даалгавар байна:",
|
||||
"other": "Танд нийт {{.Count}}-н хариуцсан даалгавар байна:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Таны хариусан даалгавар"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Өчигдөр эцсийн хугацаа"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Эцсийн хугацаа {{.Count}} өдрийн өмнө"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Өнөөдөр эцсийн хугацаа"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Ганц\nЭцсийн хугацаа {{.Count}} хоног",
|
||||
"other": "Олон\nЭцсийн хугацаа {{.Count}} хоногт"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "Ганц\nТанд **өнөөдрөөс хойш хугацаа хэтрэх томилсон даалгавар {{.Count}} байна**.",
|
||||
"other": "Олон\nТанд **өнөөдрөөс хойш хугацаа хэтрэх томилсон даалгавар {{.Count}} байна**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "'/playbook todo' -г ашиглаж өөрийн бүх таскаа харна уу."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Танд одоогоор боловсруулагдаж байгаа үйлдэл 0 байна."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "Ганц үйлдэл\nТаны {{.Count}} үйлдэл процеслогдож байна:",
|
||||
"other": "Олон үйлдэл\nТаны {{.Count}} үйлдэл процеслогдож байна:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Үйлдэл хийгдэж байна"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "Танд хугацаа хэтэрсэн шинэчлэл үйлдэл 0 байна."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "Ганц тоо\nТанд {{.Count}} төлөвийн шинэчлэлтийн хугацаа хэтэрсэн байна:",
|
||||
"other": "Олон тоо\nТанд {{.Count}} төлөвийн шинэчлэлтийн хугацаа хэтэрсэн байна:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Хугацаа хэтэрсэн төлөвийн мэдээлэл"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Командыг биелүүлэх боломжгүй."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "You have {{.Count}} assigned task that is now due:",
|
||||
"other": "You have {{.Count}} assigned tasks that are now due:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "You have 0 assigned tasks."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Owner** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Run name"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Start run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbook Run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Add to run timeline"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Summary"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Max 64 chars"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Short summary shown in the timeline"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Add to run timeline"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "There is **{{.Count}} outstanding task**. Are you sure you want to finish the run *{{.RunName}}* for all participants?",
|
||||
"other": "There are **{{.Count}} outstanding tasks**. Are you sure you want to finish the run *{{.RunName}}* for all participants?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Finish run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Confirm finish run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here — @{{.Name}} requested a status update for [{{.RunName}}]({{.RunURL}}). \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} disabled the status updates for [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} enabled the status updates for [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Change since last update"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Finish run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Also mark the run as finished"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Provide an update to the stakeholders. This post will be broadcasted to {{.Count}} channel.",
|
||||
"other": "Provide an update to the stakeholders. This post will be broadcasted to {{.Count}} channels."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Reminder for next update"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Run playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Description"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Name"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Add task"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Add new task"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} is a run participant and wants join this channel. Any member of the channel can invite them.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Update status"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Status update"
|
||||
}
|
||||
]
|
||||
210
core-plugins/mattermost-plugin-playbooks/assets/i18n/nb_NO.json
Normal file
210
core-plugins/mattermost-plugin-playbooks/assets/i18n/nb_NO.json
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
[
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Kunne ikke utføre kommandoen."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Forfalte statusoppdateringer"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "Du har {{.Count}} overskredet fristen for en statusoppdatering:",
|
||||
"other": "Du har {{.Count}} kjøringer som er forsinket for en statusoppdatering:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "Du har 0 kjøringer som er forsinket."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Du har 0 kjøringer som pågår for øyeblikket."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Bruk `/playbook todo` for å se alle oppgavene dine."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Forfall i dag"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Forfall {{.Count}} dager siden"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Forfall i går"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Dine tildelte oppgaver"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "Du har {{.Count}} tildelt en oppgave:",
|
||||
"other": "Du har {{.Count}} totalt tildelte oppgaver:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "Du har 0 tildelte oppgaver."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Eier** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Kjør navn"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Beskrivelse"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Navn"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Legg til oppgave"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Legg til ny oppgave"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Legg til i tidslinjen for kjøring"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Sammendrag"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Maks 64 tegn"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Legg til i tidslinjen for kjøring"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Fullfør løpet"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Bekreft målkjøring"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} er en løpsdeltaker og ønsker å bli med i denne kanalen. Alle medlemmer av kanalen kan invitere dem.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here - @{{.Name}} ba om en statusoppdatering for [{{.RunName}}]({{.RunURL}}). \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} deaktiverte statusoppdateringene for [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Endring siden forrige oppdatering"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Fullfør løpet"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Merk også kjøringen som fullført"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Påminnelse om neste oppdatering"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Oppdater status"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Statusoppdatering"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "Du har {{.Count}} kjøring som pågår for øyeblikket:",
|
||||
"other": "Du har {{.Count}} kjøringer som pågår for øyeblikket:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Forfall om {{.Count}} dag",
|
||||
"other": "Forfall om {{.Count}} dager"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "Du har {{.Count}} tildelt en oppgave som nå forfaller:",
|
||||
"other": "Du har {{.Count}} tildelte oppgaver som nå forfaller:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "Det er **{{.Count}} utestående oppgave**. Er du sikker på at du vil fullføre løpet *{{.RunName}}* for alle deltakerne?",
|
||||
"other": "Det er **{{.Count}} utestående oppgaver**. Er du sikker på at du vil fullføre løpet *{{.RunName}}* for alle deltakerne?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Pågående løp"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "Du har **{{.Count}} tildelte oppgaver som forfaller etter i dag**.",
|
||||
"other": "Du har **{{.Count}} tildelte oppgaver som forfaller etter i dag**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Start kjøring"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Kjør Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbook Run"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Kort sammendrag vist i tidslinjen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} aktiverte statusoppdateringene for [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Gi en oppdatering til interessentene. Dette innlegget vil bli sendt til kanalen {{.Count}}.",
|
||||
"other": "Gi en oppdatering til interessentene. Dette innlegget vil bli sendt til {{.Count}} kanaler."
|
||||
}
|
||||
}
|
||||
]
|
||||
218
core-plugins/mattermost-plugin-playbooks/assets/i18n/nl.json
Normal file
218
core-plugins/mattermost-plugin-playbooks/assets/i18n/nl.json
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "Je hebt **{{.Count}} taak toegewezen gekregen na vandaag**.",
|
||||
"other": "Je hebt **{{.Count}} taken toegewezen gekregen na vandaag**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Gebruik `/playbook todo` om al jouw taken te zien."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Je hebt 0 lopende uitvoeringen."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "Je hebt {{.Count}} uitvoering lopen:",
|
||||
"other": "Je hebt {{.Count}} uitvoeringen lopen:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Lopende uitvoeringen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "Je hebt 0 achterstallige uitvoeringen."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "Je hebt {{.Count}} uitvoering die een status update nodig hebben:",
|
||||
"other": "Je hebt {{.Count}} uitvoeringen die een status update nodig hebben:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Samenvatting"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Voeg nieuwe taak toe"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Voeg taak toe"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Naam"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Achterstallige statusupdates"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Kan commando niet uitvoeren."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Verloopt binnen {{.Count}} dag",
|
||||
"other": "Verloopt binnen {{.Count}} dagen"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Status bijwerken"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Verzenden"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Herinnering voor de volgende update"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Geef een update aan de belanghebbenden. Dit bericht zal worden uitgezonden naar {{.Count}} kanaal.",
|
||||
"other": "Geef een update aan de belanghebbenden. Dit bericht zal worden uitgezonden naar {{.Count}} kanalen."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "De uitvoering ook markeren als beëindigd"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Uitvoering beëindigen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Wijziging sinds laatste update"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} heeft de statusupdates voor [{{.RunName}}]({{.RunURL}}) uitgeschakeld"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} heeft de statusupdates voor [{{.RunName}}]({{.RunURL}}) uitgeschakeld"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@hier - @{{.Name}} vroeg een statusupdate aan voor [{{.RunName}}]({{.RunURL}}). \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} is een deelnemer aan de uitvoering en wil lid worden van dit kanaal. Elk lid van het kanaal kan hen uitnodigen.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Beëindigen bevestigen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "Er is **{{.Count}} openstaande taak**. Weet je zeker dat je *{{.RunName}}* voor alle deelnemer wilt beëindigen?",
|
||||
"other": "Er is **{{.Count}} openstaande taken**. Weet je zeker dat je *{{.RunName}}* voor alle deelnemer wilt beëindigen?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Voltooien"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Toevoegen aan uitvoeringstijdlijn"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Korte samenvatting in de tijdlijn"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Maximaal 64 tekens"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Toevoegen aan uitvoeringstijdlijn"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbook Uitvoering"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Omschrijving"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Playbook uitvoeren"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Uitvoering starten"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Naam van de uitvoering"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.new_playbook",
|
||||
"translation": "[Klik hier]({{.RunURL}}) om je eigen playbook te maken."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Eigenaar** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "Je hebt 0 toegewezen taken."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "Je hebt {{.Count}} een taak toegewezen gekregen die nu verlopen:",
|
||||
"other": "Je hebt {{.Count}} taken toegewezen gekregen die nu verlopen:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "Je hebt {{.Count}} toegewezen taak:",
|
||||
"other": "Je hebt {{.Count}} toegewezen taken:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Jouw toegewezen taken"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Verliep gisteren"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Verliep {{.Count}} dagen geleden"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Verloopt vandaag"
|
||||
},
|
||||
{
|
||||
"id": "playbooks.checklist.condition.reason.modified",
|
||||
"translation": "weergegeven omdat de taak gewijzigd werd"
|
||||
}
|
||||
]
|
||||
242
core-plugins/mattermost-plugin-playbooks/assets/i18n/pl.json
Normal file
242
core-plugins/mattermost-plugin-playbooks/assets/i18n/pl.json
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "Masz 0 przydzielonych zadań."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "Masz {{.Count}} przydzielone zadanie, którego termin wykonania właśnie upływa:",
|
||||
"few": "Masz {{.Count}} przydzielone zadania, których termin wykonania właśnie upływa:",
|
||||
"many": "Masz {{.Count}} przydzielonych zadań, których termin wykonania właśnie upływa:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "Masz {{.Count}} przydzielone zadanie:",
|
||||
"few": "Masz {{.Count}} przydzielone zadania:",
|
||||
"many": "Masz {{.Count}} przydzielonych zadań:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Twoje przydzielone zadania"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Termin na wczoraj"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Termin {{.Count}} dni temu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Do zapłaty dzisiaj"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Termin płatności za {{.Count}} dzień",
|
||||
"few": "Termin płatności za {{.Count}} dni",
|
||||
"many": "Termin płatności za {{.Count}} dni"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "Masz **{{.Count}} przydzielone zadanie, którego termin wykonania wypada po dzisiejszym dniu**.",
|
||||
"few": "Masz **{{.Count}} przydzielone zadania, których termin wykonania wypada po dzisiejszym dniu**.",
|
||||
"many": "Masz **{{.Count}} przydzielonych zadań, których termin wykonania wypada po dzisiejszym dniu**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Użyj `/playbook todo`, aby zobaczyć wszystkie swoje zadania."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Obecnie masz 0 uruchomień w trakcie."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "Masz {{.Count}} uruchomienie w toku:",
|
||||
"few": "Masz {{.Count}} uruchomienia w toku:",
|
||||
"many": "Masz {{.Count}} uruchomień w toku:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Uruchomienia w Trakcie"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "Masz 0 zaległych uruchomień."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "Masz {{.Count}} zaległość w aktualizacji statusu:",
|
||||
"few": "Masz {{.Count}} zaległości w aktualizacji statusu:",
|
||||
"many": "Masz {{.Count}} zaległości w aktualizacji statusu:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Zaległe aktualizacje statusu"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Nie można wykonać polecenia."
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here - @{{.Name}} zażądał aktualizacji statusu dla [{{.RunName}}]({{.RunURL}}). \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_get_involved",
|
||||
"translation": "@here - @{.Name}} chce wziąć udział w tym uruchomieniu. Aby uwzględnić ich jako uczestników, dodaj ich do tego kanału.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.retro_publish",
|
||||
"translation": "@{.Name}} opublikował retrospektywę\n[Zobacz pełną retrospektywę]({{.URL}}).\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_participate",
|
||||
"translation": "@{{.Name}} dołączył do przebiegu. Nie zostali oni automatycznie dodani do tego prywatnego kanału, ale każdy członek kanału może ich zaprosić do przyłączenia się.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_add_participant",
|
||||
"translation": "@{{.Name}} został dodany do przebiegu przez @{{.RequesterName}}. Nie zostali oni automatycznie dodani do kanału, ponieważ @{{.RequesterName}} nie jest członkiem kanału, a kanał jest prywatny.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} jest uczestnikiem uruchomienia i chce dołączyć do tego kanału. Każdy członek kanału może go zaprosić.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} włączył aktualizacje statusu dla [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} wyłączył aktualizacje statusu dla [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbok"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Aktualizacje statusu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Post"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Przypomnienie o następnej aktualizacji"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Przedstaw aktualizację dla interesariuszy. Ten post będzie transmitowany na {{.Count}} kanał.",
|
||||
"few": "Przedstawić aktualizację dla interesariuszy. Ten post będzie transmitowany na {{.Count}} kanały.",
|
||||
"many": "Przedstawić aktualizację dla interesariuszy. Ten post będzie transmitowany na {{.Count}} kanałów."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Oznacz również uruchomienie jako zakończone"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Zakończ uruchomienie"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Zmiana od ostatniej aktualizacji"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Potwierdź zakończenie"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Zakończ"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "Istnieje **{{.Count}} zaległe zadanie**. Czy na pewno chcesz ukończyć *{{.RunName}}* dla wszystkich uczestników?",
|
||||
"few": "Istnieją **{{.Count}} zaległe zadania**. Czy na pewno chcesz ukończyć *{{.RunName}}* dla wszystkich uczestników?",
|
||||
"many": "Istnieje **{{.Count}} zaległych zadań**. Czy na pewno chcesz ukończyć *{{.RunName}}* dla wszystkich uczestników?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Dodaj do osi czasu uruchomienia"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Krótkie podsumowanie widoczne na osi czasu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Maksymalnie 64 znaki"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Podsumowanie"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Dodaj do osi czasu uruchomienia"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Uruchomienie Playbooka"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Dodaj nowe zadanie"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Dodaj zadanie"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Nazwa"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Opis"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Uruchom playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Rozpocznij uruchomienie"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Nazwa uruchomienia"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.new_playbook",
|
||||
"translation": "[Kliknij tutaj]({{.RunURL}}), aby stworzyć własny playbook."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Właściciel** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "playbooks.checklist.condition.reason.modified",
|
||||
"translation": "ponieważ zadanie zostało zmodyfikowane"
|
||||
}
|
||||
]
|
||||
230
core-plugins/mattermost-plugin-playbooks/assets/i18n/ru.json
Normal file
230
core-plugins/mattermost-plugin-playbooks/assets/i18n/ru.json
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_outstanding",
|
||||
"translation": "У вас 0 нерешенных задач."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_outstanding",
|
||||
"translation": {
|
||||
"one": "У вас {{.Count}} нерешенная задача:",
|
||||
"few": "У вас {{.Count}} нерешенных задачи:",
|
||||
"many": "У вас {{.Count}} нерешенных задач:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Ваши назначенные задачи"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "В настоящее время у вас нет запусков."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "У вас запущен {{.Count}}:",
|
||||
"few": "У вас запущено {{.Count}}:",
|
||||
"many": "У вас запущены {{.Count}}:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Выполняется"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "У вас просрочено 0 запусков."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "У вас есть {{.Count}} просроченный запуск для обновления статуса:",
|
||||
"few": "У вас есть {{.Count}} просроченных запуска для обновления статуса:",
|
||||
"many": "У вас есть {{.Count}} просроченных запусков для обновления статуса:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Просроченные обновления статуса"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Невозможно выполнить команду."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Вчера"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Срок выполнения {{.Count}} дн. назад"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Сегодня"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Через {{.Count}} день",
|
||||
"few": "Через {{.Count}} дня",
|
||||
"many": "Через {{.Count}} дней"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "У вас есть **{{.Count}} назначенная задача, которую необходимо выполнить после сегодняшнего дня**.",
|
||||
"few": "У вас есть **{{.Count}} назначенные задачи, которые необходимо выполнить после сегодняшнего дня**.",
|
||||
"many": "У вас есть **{{.Count}} назначенных задач, которые необходимо выполнить после сегодняшнего дня**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Пожалуйста, используйте `/playbook todo`, чтобы увидеть все свои задачи."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "У вас есть {{.Count}} назначенная задача:",
|
||||
"few": "У вас есть {{.Count}} назначенные задачи:",
|
||||
"many": "У вас есть {{.Count}} назначенных задач:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "У вас есть {{.Count}} назначенная задача, которая должна быть выполнена сейчас:",
|
||||
"few": "У вас есть {{.Count}} назначенные задачи, которые должны быть выполнены сейчас:",
|
||||
"many": "У вас есть {{.Count}} назначенных задач, которые должны быть выполнены сейчас:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "У вас нет назначенных задач."
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} включил обновление статуса для этого запуска"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} отключил обновление статуса для этого запуска"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here - @{{.Name}} запросил обновление статуса. \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} является участником запуска и хочет присоединиться к этому каналу. Любой участник канала может пригласить его.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Изменения с момента последнего обновления"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "",
|
||||
"few": "",
|
||||
"many": "Предоставь обновленную информацию заинтересованным сторонам. Этот пост будет транслироваться на каналах {{.Count}}."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Обновление статуса"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Подтверди финишную пробежку"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Владелец** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Пособие"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Финишный рывок"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Также пометьте выполнение как завершенное"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Напоминание о следующем обновлении"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Обновление статуса"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Название бега"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Стартовый забег"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Запусти сценарий"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Описание"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Имя"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Добавь задание"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Добавь новое задание"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Пособие \"Бег"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Добавить в график выполнения"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Резюме"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Максимум 64 символа"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Краткое содержание, отображаемое на временной шкале"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Добавить в график выполнения"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "",
|
||||
"few": "",
|
||||
"many": "Есть **{{.Count}} невыполненных заданий**. Ты уверен, что хочешь завершить забег *{{.RunName}}* для всех участников?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Финишный рывок"
|
||||
}
|
||||
]
|
||||
230
core-plugins/mattermost-plugin-playbooks/assets/i18n/sv.json
Normal file
230
core-plugins/mattermost-plugin-playbooks/assets/i18n/sv.json
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
[
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Kunde inte utföra kommandot."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Status om förseningar"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "Du har 0 tilldelade uppgifter."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "Du har {{.Count}} tilldelad uppgift som nu är försenad:",
|
||||
"other": "Du har {{.Count}} tilldelade uppgifter som nu är försenade:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "Du har tilldelats {{.Count}} uppgift:",
|
||||
"other": "Du har tilldelats totalt {{.Count}} uppgifter:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Dina tilldelade uppgifter"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Skulle utförts igår"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Försenad {{.Count}} dagar"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Ska utföras idag"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "Ska utföras inom {{.Count}} dag",
|
||||
"other": "Ska utföras inom {{.Count}} dagar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "Du har **{{.Count}} tilldelad uppgift som ska utföras idag**.",
|
||||
"other": "Du har **{{.Count}} tilldelade uppgifter som ska utföras idag**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Använd `/playbook todo` för att se alla dina uppgifter."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Du har 0 pågående körningar."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "Du har {{.Count}} körning som för närvarande pågår:",
|
||||
"other": "Du har {{.Count}} körningar som för närvarande pågår:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Körningar som pågår"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "Du har 0 försenade körningar."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "Du har {{.Count}} körning som borde ha fått en statusuppdatering:",
|
||||
"other": "Du har {{.Count}} körningar som borde ha fått en statusuppdatering:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here - @{{.Name}} begärde en statusuppdatering för [{{.RunName}}]({{.RunURL}}). \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_get_involved",
|
||||
"translation": "@here - @{{.Name}} vill delta i denna körning. Om du vill inkludera dem som deltagare lägger du till dem i den här kanalen.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_participate",
|
||||
"translation": "@{{.Name}} har anslutit till körningen. De har inte lagts till i den privata kanalen men vilken medlem om helst kan bjuda in dem att ansluta.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_add_participant",
|
||||
"translation": "@{{.Name}} har lagts till i körningen av @{{.RequesterName}}. De har inte blivit automatiskt tillagda i kanalen eftersom @{{.RequesterName}} inte är en kanalmedlem och detta är en privat kanal.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} aktiverade statusuppdateringar för [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} inaktiverade statusuppdateringarna för [{{.RunName}}]({{.RunURL}})"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} är en deltagare i en körning och vill gå med i den här kanalen. Alla medlemmar i kanalen kan bjuda in dem.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Statusuppdateringar"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Skicka"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Påminnelse om nästa uppdatering"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Ge en uppdatering till intressenterna. Detta inlägg kommer att sändas till {{.Count}} kanal.",
|
||||
"other": "Ge en uppdatering till intressenterna. Detta inlägg kommer att sändas till {{.Count}} kanaler."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Markera också körningen som avslutad"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Slutför körningen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Förändring sedan den senaste uppdateringen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Bekräfta att avsluta körningen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Slutför körningen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "Det finns **{{.Count}} utestående uppgift**. Är du säker på att du vill avsluta körningen *{{.RunName}}* för alla deltagare?",
|
||||
"other": "Det finns **{{.Count}} utestående uppgifter**. Är du säker på att du vill avsluta körningen *{{.RunName}}* för alla deltagare?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Lägg till i tidslinjen för körning"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Kort sammanfattning som visas i tidslinjen"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "Max 64 tecken"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Sammanfattning"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Lägg till i tidslinjen för körning"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Kör Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Lägg till en ny uppgift"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Lägg till uppgift"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Namn"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Beskrivning"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Kör playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Starta körning"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Namn på körning"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.new_playbook",
|
||||
"translation": "[Klicka här]({{.RunURL}}) för att skapa din egen playbook."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Ägare** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "playbooks.checklist.condition.reason.modified",
|
||||
"translation": "visas eftersom uppgiften ändrades"
|
||||
}
|
||||
]
|
||||
230
core-plugins/mattermost-plugin-playbooks/assets/i18n/tr.json
Normal file
230
core-plugins/mattermost-plugin-playbooks/assets/i18n/tr.json
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
[
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "Atanmış bir göreviniz yok."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"one": "Süresi dolmuş {{.Count}} atanmış göreviniz var:",
|
||||
"other": "Süresi dolmuş {{.Count}} atanmış göreviniz var:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"one": "{{.Count}} atanmış göreviniz var:",
|
||||
"other": "{{.Count}} atanmış göreviniz var:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "Atanmış görevleriniz"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "Süresi dün doldu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "Süresi {{.Count}} gün önce doldu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "Süresi bugün dolacak"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"one": "{{.Count}} gün içinde süresi dolacak",
|
||||
"other": "{{.Count}} gün içinde süresi dolacak"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"one": "Bugünden sonra süresi dolacak **{{.Count}} göreviniz var**.",
|
||||
"other": "Bugünden sonra süresi dolacak **{{.Count}} göreviniz var**."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "Tüm görevlerinizi görüntülemek için `/playbook todo` kullanın."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "Süren 0 çalışmanız var."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"one": "Süren {{.Count}} çalışmanız var:",
|
||||
"other": "Süren {{.Count}} çalışmanız var:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "Süren çalışmalar"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "0 çalışma gecikmeniz var."
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"one": "Bir durum güncellemesi için {{.Count}} çalışma gecikmeniz var:",
|
||||
"other": "Bir durum güncellemesi için {{.Count}} çalışma gecikmeniz var:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "Gecikmiş durum güncellemeleri"
|
||||
},
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "Komut yürütülemedi."
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here — @{{.Name}}, [{{.RunName}}]({{.RunURL}}) için bir durum güncellemesi istedi. \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_get_involved",
|
||||
"translation": "@here — @{{.Name}} bu oyuna katılmak istiyor. Oyuna katılabilmesi için lütfen onu bu kanala ekleyin.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.retro_publish",
|
||||
"translation": "@{{.Name}} bir geçmiş değerlendirmesi yayınladı\n[Geçmiş değerlendirmesine bakın]({{.URL}})\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_participate",
|
||||
"translation": "@{{.Name}} oyuna katıldı. Bu özel kanala otomatik olarak eklenmedi, ancak kanalın herhangi bir üyesi onu katılmaya davet edebilir.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.joined_run_channel_private_add_participant",
|
||||
"translation": "@{{.Name}}, @{{.RequesterName}} tarafından oyuna eklendi. @{{.RequesterName}} bir kanal üyesi olmadığından ve kanal özel olduğundan otomatik olarak kanala eklenmedi.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} bir oyun katılımcısı ve bu kanala katılmak istiyor. Kanalın herhangi bir üyesi onu çağırabilir.\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}}, [{{.RunName}}]({{.RunURL}}) için durum güncellemelerini etkinleştirdi"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}}, [{{.RunName}}]({{.RunURL}}) için durum güncellemelerini devre dışı bıraktı"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "Oyun zaman akışına ekle"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "Zaman akışında görüntülenecek kısa açıklama"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "En fazla 64 karakter"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "Özet"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "Oyun zaman akışı ekle"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Senaryo oyunu"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "Yeni görev ekle"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "Görev ekle"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "Ad"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "Açıklama"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "Senaryoyu oyna"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "Oyunu başlat"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "Oyun adı"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Senaryo"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.new_playbook",
|
||||
"translation": "Kendi senaryonuzu oluşturmak için [buraya tıklayın]({{.RunURL}})."
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**Sahibi** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"one": "Bekleyen **{{.Count}} görev** var. *{{.RunName}}* oyununu tüm katılımcılar için tamamlamak istediğinize emin misiniz?",
|
||||
"other": "Bekleyen **{{.Count}} görev** var. *{{.RunName}}* oyununu tüm katılımcılar için tamamlamak istediğinize emin misiniz?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "Ayrıca oyunu da tamamlanmış olarak işaretle"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "Oyunu tamamla"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "Oyunu tamamlamayı onayla"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "Oyunu tamamla"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "Durum güncellemesi"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "Gönder"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "Sonraki güncelleme anımsatıcısı"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"one": "Paydaşlara bir güncelleme duyurun. Bu gönderi {{.Count}} kanalında yayınlanacak.",
|
||||
"other": "Paydaşlara bir güncelleme duyurun. Bu gönderi {{.Count}} kanalında yayınlanacak."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "Son güncellemeden sonraki değişiklik"
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
[
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "无法执行命令。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "逾期状态更新"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"other": "您的 {{.Count}} 流程已逾期,无法更新状态:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "您有 0 次流程逾期。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "您目前有 0 个流程正在进行中。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "请使用 `/playbook todo` 查看所有任务。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"other": "您有**{{.Count}}项分配的任务在今天之后到期**。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"other": "{{.Count}}天后到期"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "今天到期"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"other": "您有 {{.Count}} 分配的任务,现在到期:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "您有 0 项指定任务。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "**所有者** {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "流程名称"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "开始流程"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "流程 Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "说明"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "名称"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "摘要"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "最多 64 个字符"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "时间轴中显示的简短摘要"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "添加到流程时间轴"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "完成流程"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"other": "有**{{.Count}}项任务尚未完成**。您确定要完成所有参与者的流程 *{{.RunName}}* 吗?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "确认完成流程"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} 启用 [{{.RunName}}]({{.RunURL}}) 的状态更新"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "自上次更新以来的变化"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "完成流程"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "同时将流程标记为已完成"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "更新状态"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "流程进行中"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"other": "您当前正在进行 {{.Count}} 流程:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "{{.Count}}天前到期"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "昨天到期"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "您的指定任务"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"other": "您共有 {{.Count}} 项已分配任务:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "添加任务"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "添加新任务"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbook 流程"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "添加到流程时间轴"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} 是一名流程参与者,希望加入此频道。频道的任何成员都可以邀请他们。\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here - @{{.Name}} 请求更新 [{{.RunName}}]({{.RunURL}}) 的状态。 \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} 禁用了 [{{.RunName}}]({{.RunURL}}) 的状态更新"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"other": "向利益相关者提供最新信息。本职位将在 {{.Count}} 频道上发布。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "下次更新提醒"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "状态更新"
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
[
|
||||
{
|
||||
"id": "app.command.execute.error",
|
||||
"translation": "無法執行指令。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.zero_overdue",
|
||||
"translation": "您有 0 次逾期。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.heading",
|
||||
"translation": "執行中"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.num_in_progress",
|
||||
"translation": {
|
||||
"other": "你目前有 {{.Count}} 項正在進行中:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.runs_in_progress.zero_in_progress",
|
||||
"translation": "你目前有0項正在執行中。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.all_tasks_command",
|
||||
"translation": "請使用 `/playbook todo`查看你所有的任務。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_in_x_days",
|
||||
"translation": {
|
||||
"other": "{{.Count}} 天後到期"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_today",
|
||||
"translation": "今天到期"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_x_days_ago",
|
||||
"translation": "{{.Count}} 天前到期"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_yesterday",
|
||||
"translation": "昨天到期"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.heading",
|
||||
"translation": "您分配的任務"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.description",
|
||||
"translation": "描述"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.zero_assigned",
|
||||
"translation": "你目前沒有任何指派的任務。"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.intro",
|
||||
"translation": "擁有者 {{.Username}}"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.playbook",
|
||||
"translation": "Playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.run_name",
|
||||
"translation": "執行名稱"
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.title",
|
||||
"translation": "執行playbook"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.name",
|
||||
"translation": "名稱"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.title",
|
||||
"translation": "增加新任務"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned",
|
||||
"translation": {
|
||||
"other": "你總共分配了 {{.Count}} 個任務:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.heading",
|
||||
"translation": "過期狀態更新"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.num_assigned_due_until_today",
|
||||
"translation": {
|
||||
"other": "你有 {{.Count}} 個指派的任務現在已經到期:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.overdue_status_updates.num_overdue",
|
||||
"translation": {
|
||||
"other": "您的狀態更新已逾期 {{.Count}} 次:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.new_run.submit_label",
|
||||
"translation": "開始執行"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_checklist_item.submit_label",
|
||||
"translation": "新增任務"
|
||||
},
|
||||
{
|
||||
"id": "app.user.digest.tasks.due_after_today",
|
||||
"translation": {
|
||||
"other": "您有 **{{.Count}} 個分配的任務在今天之後到期**。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.playbook_run",
|
||||
"translation": "Playbook 執行"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.submit_label",
|
||||
"translation": "新增到執行時間軸"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary",
|
||||
"translation": "摘要"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.help",
|
||||
"translation": "最多 64 個字元"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.summary.placeholder",
|
||||
"translation": "在時間軸中顯示簡短摘要"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.add_to_timeline.title",
|
||||
"translation": "新增至執行時間軸"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.submit_label",
|
||||
"translation": "執行完成"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.title",
|
||||
"translation": "確認完程執行"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run",
|
||||
"translation": "執行完成"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.finish_run.placeholder",
|
||||
"translation": "同時將執行標記為已完成"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_join_channel",
|
||||
"translation": "@{{.Name}} 是執行參與者,想要加入此頻道。該頻道的任何成員都可以邀請他們。\n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.request_update",
|
||||
"translation": "@here — @{{.Name}} 請求 [{{.RunName}}]({{.RunURL}}) 的狀態更新。 \n"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_disable",
|
||||
"translation": "@{{.Username}} 禁用了 [{{.RunName}}]({{.RunURL}}) 的狀態更新"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.status_enable",
|
||||
"translation": "@{{.Username}} 啟用了 [{{.RunName}}]({{.RunURL}}) 的狀態更新"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.title",
|
||||
"translation": "更新狀態"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.change_since_last_update",
|
||||
"translation": "自上次更新以來的異動"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.reminder_for_next_update",
|
||||
"translation": "下次更新提醒"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.submit_label",
|
||||
"translation": "更新狀態"
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.confirm_finish.num_outstanding",
|
||||
"translation": {
|
||||
"other": "有 **{{.Count}} 個未完成的任務**。您確定要為所有參與者完成跑步 *{{.RunName}}* 嗎?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.user.run.update_status.num_channel",
|
||||
"translation": {
|
||||
"other": "向利益相關者提供更新。此文章將被廣播到 {{.Count}} 個頻道。"
|
||||
}
|
||||
}
|
||||
]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 193 KiB |
|
|
@ -0,0 +1,27 @@
|
|||
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_15_126" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="3" y="5" width="87" height="81">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M89.786 5H3V85.512H57.23C55.9387 82.364 55.2762 78.9936 55.28 75.591C55.28 61.154 66.984 49.451 81.421 49.451C84.346 49.451 87.16 49.931 89.786 50.818V5Z" fill="#D9D9D9"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_15_126)">
|
||||
<path d="M61.5361 37.4767H80.9221C81.8017 37.4767 82.6453 37.8261 83.2672 38.448C83.8892 39.07 84.2386 39.9135 84.2386 40.7931V58.4513C84.2386 65.1828 78.7818 70.6388 72.0503 70.6388H71.9932C65.2617 70.6405 59.804 65.1845 59.8031 58.453V39.2097C59.8031 38.2524 60.5788 37.4767 61.5361 37.4767ZM74.6386 33.9864C76.7217 33.9864 78.7195 33.1588 80.1925 31.6858C81.6655 30.2128 82.493 28.215 82.493 26.1319C82.493 24.0488 81.6655 22.051 80.1925 20.578C78.7195 19.105 76.7217 18.2774 74.6386 18.2774C72.5554 18.2774 70.5576 19.105 69.0846 20.578C67.6116 22.051 66.7841 24.0488 66.7841 26.1319C66.7841 28.215 67.6116 30.2128 69.0846 31.6858C70.5576 33.1588 72.5554 33.9864 74.6386 33.9864Z" fill="#5059C9"/>
|
||||
<path d="M50.2039 33.9864C56.4693 33.9864 61.5482 28.9066 61.5482 22.6403C61.5482 16.3758 56.4693 11.296 50.2039 11.296C43.9385 11.296 38.8596 16.3749 38.8596 22.6412C38.8596 28.9066 43.9385 33.9864 50.2039 33.9864ZM65.3308 37.4767H33.331C32.4618 37.498 31.6366 37.8634 31.0367 38.4927C30.4367 39.122 30.1111 39.9637 30.1313 40.8329V60.9739C29.8788 71.833 38.4705 80.8459 49.3305 81.1114C60.1905 80.8459 68.7831 71.833 68.5297 60.973V40.8329C68.5497 39.9638 68.2239 39.1224 67.624 38.4932C67.0241 37.8641 66.199 37.4988 65.33 37.4775L65.3308 37.4767Z" fill="#7B83EB"/>
|
||||
<path opacity="0.1" d="M51.0765 37.4767V65.6999C51.0722 66.3328 50.881 66.9503 50.527 67.475C50.173 67.9996 49.6719 68.408 49.0866 68.6489C48.7061 68.8106 48.2962 68.8936 47.882 68.8936H31.6672C31.4377 68.3213 31.234 67.739 31.0566 67.1485C30.4455 65.1456 30.1337 63.0635 30.1313 60.9695V40.8286C30.1109 39.9606 30.4358 39.12 31.0348 38.4915C31.6338 37.863 32.4579 37.498 33.3258 37.4767H51.0765Z" fill="black"/>
|
||||
<path opacity="0.2" d="M49.3305 37.4767V67.4451C49.3305 67.8585 49.2475 68.2684 49.0866 68.6489C48.8456 69.2342 48.4371 69.7353 47.9123 70.0893C47.3875 70.4433 46.7698 70.6345 46.1368 70.6388H32.4878C32.1896 70.069 31.9158 69.4867 31.6672 68.8936C31.4327 68.3232 31.2289 67.7406 31.0566 67.1485C30.4455 65.1456 30.1337 63.0635 30.1313 60.9695V40.8286C30.1109 39.9606 30.4358 39.12 31.0348 38.4915C31.6338 37.863 32.4579 37.498 33.3258 37.4767H49.3305Z" fill="black"/>
|
||||
<path opacity="0.2" d="M49.3305 37.4767V63.9539C49.3242 64.7991 48.9857 65.6078 48.3881 66.2055C47.7906 66.8032 46.982 67.1419 46.1368 67.1485H31.0566C30.4455 65.1456 30.1337 63.0635 30.1313 60.9695V40.8286C30.1109 39.9606 30.4358 39.12 31.0348 38.4915C31.6338 37.863 32.4579 37.498 33.3258 37.4767H49.3305Z" fill="black"/>
|
||||
<path opacity="0.2" d="M47.5854 37.4767V63.9539C47.579 64.7991 47.2405 65.6078 46.643 66.2055C46.0455 66.8032 45.2368 67.1419 44.3917 67.1485H31.0566C30.4455 65.1456 30.1337 63.0635 30.1313 60.9695V40.8286C30.1109 39.9606 30.4358 39.12 31.0348 38.4915C31.6338 37.863 32.4579 37.498 33.3258 37.4767H47.5854Z" fill="black"/>
|
||||
<path opacity="0.1" d="M51.0765 28.4534V33.9509C50.779 33.9682 50.5006 33.9855 50.2031 33.9855C49.9065 33.9855 49.6271 33.9682 49.3305 33.9509C48.7414 33.9118 48.1572 33.8183 47.5854 33.6716C45.8429 33.2589 44.2229 32.4396 42.8579 31.2807C41.4928 30.1219 40.4214 28.6564 39.7313 27.004C39.4905 26.4413 39.3034 25.8569 39.1727 25.2589H47.882C48.7282 25.2621 49.5389 25.5997 50.1373 26.1981C50.7357 26.7965 51.0733 27.6072 51.0765 28.4534Z" fill="black"/>
|
||||
<path opacity="0.2" d="M49.3305 30.1986V33.9518C48.7414 33.9124 48.1572 33.8186 47.5854 33.6716C45.8429 33.2589 44.2229 32.4396 42.8579 31.2807C41.4928 30.1219 40.4214 28.6564 39.7313 27.004H46.1368C46.983 27.0072 47.7936 27.3449 48.3919 27.9433C48.9901 28.5417 49.3275 29.3524 49.3305 30.1986Z" fill="black"/>
|
||||
<path opacity="0.2" d="M49.3305 30.1986V33.9518C48.7414 33.9124 48.1572 33.8186 47.5854 33.6716C45.8429 33.2589 44.2229 32.4396 42.8579 31.2807C41.4928 30.1219 40.4214 28.6564 39.7313 27.004H46.1368C46.983 27.0072 47.7936 27.3449 48.3919 27.9433C48.9901 28.5417 49.3275 29.3524 49.3305 30.1986Z" fill="black"/>
|
||||
<path opacity="0.2" d="M47.5854 30.1986V33.6716C45.843 33.2588 44.223 32.4395 42.8579 31.2807C41.4929 30.1218 40.4215 28.6563 39.7313 27.004H44.3917C45.2378 27.0075 46.0483 27.3452 46.6465 27.9435C47.2447 28.5419 47.5822 29.3525 47.5854 30.1986Z" fill="black"/>
|
||||
<path d="M12.3867 27.0049H44.3856C45.2341 27.0049 46.0478 27.3419 46.6479 27.9418C47.2479 28.5417 47.5851 29.3553 47.5854 30.2038V62.2036C47.5854 63.0522 47.2482 63.8661 46.6482 64.4661C46.0481 65.0662 45.2343 65.4033 44.3856 65.4033H12.3867C11.5381 65.4033 10.7242 65.0662 10.1241 64.4661C9.52406 63.8661 9.18695 63.0522 9.18695 62.2036V30.2038C9.18695 29.3551 9.52406 28.5413 10.1241 27.9412C10.7242 27.3412 11.5381 27.004 12.3867 27.004V27.0049Z" fill="url(#paint0_linear_15_126)"/>
|
||||
<path d="M36.8057 39.1837H30.4089V56.6023H26.334V39.1837H19.9666V35.805H36.8057V39.1837Z" fill="white"/>
|
||||
</g>
|
||||
<path d="M74.984 86.31V91.295L75.368 90.92L81.724 84.713L81.889 84.551L81.724 84.39L75.369 78.145L74.984 77.767V82.752C73.4216 82.7276 71.8898 82.3145 70.527 81.55C69.1388 80.7452 67.9762 79.6029 67.147 78.229C66.3343 76.834 65.9113 75.2464 65.922 73.632C65.922 72.073 66.282 70.653 66.997 69.368L67.081 69.217L66.958 69.097L64.637 66.815L64.436 66.618L64.286 66.857C62.971 68.961 62.311 71.221 62.311 73.632C62.311 75.916 62.893 78.05 64.055 80.029L64.057 80.032C65.201 81.9165 66.7977 83.4854 68.702 84.596L68.705 84.598C70.6189 85.6928 72.7803 86.2813 74.985 86.308L74.984 86.31ZM75.436 68.96V64.514C76.9907 64.5373 78.5127 64.9639 79.853 65.752L79.857 65.754C81.2658 66.53 82.4343 67.6781 83.235 69.073L83.237 69.077C84.0746 70.4481 84.5117 72.0263 84.499 73.633C84.499 75.193 84.139 76.613 83.423 77.898L83.339 78.05L83.464 78.17L85.785 80.414L85.985 80.608L86.134 80.372C86.7559 79.3777 87.2381 78.3027 87.567 77.177C87.929 76.033 88.109 74.852 88.109 73.633C88.1182 71.3825 87.5158 69.1717 86.366 67.237L86.364 67.234C85.22 65.3495 83.6233 63.7807 81.719 62.67L81.715 62.668C79.8014 61.5734 77.6404 60.9849 75.436 60.958V55.97L75.052 56.345L68.697 62.552L68.532 62.713L68.696 62.875L75.052 69.12L75.436 69.497V68.96Z" fill="#4850B9" stroke="#4850B9" stroke-width="0.523"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_15_126" x1="15.8571" y1="24.5048" x2="40.9144" y2="67.9026" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#5A62C3"/>
|
||||
<stop offset="0.5" stop-color="#4D55BD"/>
|
||||
<stop offset="1" stop-color="#3940AB"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
BIN
core-plugins/mattermost-plugin-playbooks/assets/plugin_icon.png
Normal file
BIN
core-plugins/mattermost-plugin-playbooks/assets/plugin_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="64" height="64" fill="white"/>
|
||||
<path d="M23.984 15.208H20C18.912 15.208 17.968 15.592 17.168 16.36C16.4 17.128 16.016 18.072 16.016 19.192V46.216C16.016 47.304 16.4 48.232 17.168 49C17.968 49.8 18.912 50.2 20 50.2H44C45.12 50.2 46.064 49.8 46.832 49C47.6 48.232 47.984 47.304 47.984 46.216V19.192C47.984 18.072 47.6 17.128 46.832 16.36C46.064 15.592 45.12 15.208 44 15.208H40.016V18.184H42.992C43.536 18.184 44 18.392 44.384 18.808C44.8 19.192 45.008 19.656 45.008 20.2V45.208C45.008 45.752 44.8 46.216 44.384 46.6C44 46.984 43.536 47.176 42.992 47.176H21.008C20.464 47.176 19.984 46.984 19.568 46.6C19.184 46.216 18.992 45.752 18.992 45.208V20.2C18.992 19.656 19.184 19.192 19.568 18.808C19.984 18.392 20.464 18.184 21.008 18.184H23.984V15.208ZM29.984 12.184C29.728 12.184 29.504 12.296 29.312 12.52C29.12 12.712 29.024 12.936 29.024 13.192V14.2H26V19.192H38V14.2H35.024V13.192C35.024 12.936 34.912 12.712 34.688 12.52C34.496 12.296 34.272 12.184 34.016 12.184H29.984ZM29.024 40.216H41.984V42.184H29.024V40.216ZM23.024 39.688H26V42.712H23.024V39.688ZM29.024 32.2H41.984V34.216H29.024V32.2ZM23.024 31.72H26V34.696H23.024V31.72ZM29.024 24.184H41.984V26.2H29.024V24.184ZM22.832 26.392L21.824 25.384L22.832 24.376L23.84 25.384L26 23.176L27.008 24.184L23.84 27.4L22.832 26.392Z" fill="#3F4350"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
core-plugins/mattermost-plugin-playbooks/build/.gitignore
vendored
Normal file
1
core-plugins/mattermost-plugin-playbooks/build/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
bin
|
||||
38
core-plugins/mattermost-plugin-playbooks/build/custom.mk
Normal file
38
core-plugins/mattermost-plugin-playbooks/build/custom.mk
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Include custom targets and environment variables here
|
||||
|
||||
## Generate mocks.
|
||||
mocks:
|
||||
ifneq ($(HAS_SERVER),)
|
||||
mockgen -destination server/bot/mocks/mock_poster.go github.com/mattermost/mattermost-plugin-playbooks/server/bot Poster
|
||||
mockgen -destination server/app/mocks/mock_job_once_scheduler.go github.com/mattermost/mattermost-plugin-playbooks/server/app JobOnceScheduler
|
||||
mockgen -destination server/app/mocks/mock_condition_store.go github.com/mattermost/mattermost-plugin-playbooks/server/app ConditionStore
|
||||
mockgen -destination server/app/mocks/mock_playbook_store.go github.com/mattermost/mattermost-plugin-playbooks/server/app PlaybookStore
|
||||
mockgen -destination server/app/mocks/mock_property_service.go github.com/mattermost/mattermost-plugin-playbooks/server/app PropertyService
|
||||
mockgen -destination server/app/mocks/mock_auditor.go github.com/mattermost/mattermost-plugin-playbooks/server/app Auditor
|
||||
mockgen -destination server/sqlstore/mocks/mock_kvapi.go github.com/mattermost/mattermost-plugin-playbooks/server/sqlstore KVAPI
|
||||
mockgen -destination server/sqlstore/mocks/mock_storeapi.go github.com/mattermost/mattermost-plugin-playbooks/server/sqlstore StoreAPI
|
||||
mockgen -destination server/sqlstore/mocks/mock_configurationapi.go github.com/mattermost/mattermost-plugin-playbooks/server/sqlstore ConfigurationAPI
|
||||
endif
|
||||
|
||||
## Runs the redocly server.
|
||||
.PHONY: docs-server
|
||||
docs-server:
|
||||
npx @redocly/openapi-cli@1.0.0-beta.3 preview-docs server/api/api.yaml
|
||||
|
||||
## Re-generate tests-e2e/db-setup/mattermost.sql from the Postgres image expected to be running
|
||||
## in the developer's Docker environment.
|
||||
.PHONY: tests-e2e/db-setup/mattermost.sql
|
||||
tests-e2e/db-setup/mattermost.sql:
|
||||
docker exec mattermost-postgres pg_dump \
|
||||
--username=mmuser \
|
||||
--clean \
|
||||
--if-exists \
|
||||
--exclude-table ir_incident \
|
||||
--exclude-table ir_playbook \
|
||||
--exclude-table ir_playbookmember \
|
||||
--exclude-table ir_statusposts \
|
||||
--exclude-table ir_system \
|
||||
--exclude-table ir_timelineevent \
|
||||
--exclude-table ir_userinfo \
|
||||
--exclude-table ir_viewedchannel \
|
||||
mattermost_test > tests-e2e/db-setup/mattermost.sql
|
||||
72
core-plugins/mattermost-plugin-playbooks/build/go.mod
Normal file
72
core-plugins/mattermost-plugin-playbooks/build/go.mod
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
module github.com/mattermost/mattermost-plugin-starter-template/build
|
||||
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.11
|
||||
|
||||
require (
|
||||
github.com/mattermost/mattermost/server/public v0.1.12
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/charmbracelet/bubbles v0.21.0 // indirect
|
||||
github.com/charmbracelet/bubbletea v1.3.10 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.10.1 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.3 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
|
||||
github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 // indirect
|
||||
github.com/mattermost/logr/v2 v2.0.22 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/pborman/uuid v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
github.com/tinylib/msgp v1.2.5 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wiggin77/merror v1.0.5 // indirect
|
||||
github.com/wiggin77/srslog v1.0.1 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect
|
||||
google.golang.org/grpc v1.70.0 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
341
core-plugins/mattermost-plugin-playbooks/build/go.sum
Normal file
341
core-plugins/mattermost-plugin-playbooks/build/go.sum
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
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/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
||||
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
|
||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
|
||||
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a h1:etIrTD8BQqzColk9nKRusM9um5+1q0iOEJLqfBMIK64=
|
||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a/go.mod h1:emQhSYTXqB0xxjLITTw4EaWZ+8IIQYw+kx9GqNUKdLg=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
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/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg=
|
||||
github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
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.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 h1:Khvh6waxG1cHc4Cz5ef9n3XVCxRWpAKUtqg9PJl5+y8=
|
||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404/go.mod h1:RyS7FDNQlzF1PsjbJWHRI35exqaKGSO9qD4iv8QjE34=
|
||||
github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 h1:Y1Tu/swM31pVwwb2BTCsOdamENjjWCI6qmfHLbk6OZI=
|
||||
github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956/go.mod h1:SRl30Lb7/QoYyohYeVBuqYvvmXSZJxZgiV3Zf6VbxjI=
|
||||
github.com/mattermost/logr/v2 v2.0.22 h1:npFkXlkAWR9J8payh8ftPcCZvLbHSI125mAM5/r/lP4=
|
||||
github.com/mattermost/logr/v2 v2.0.22/go.mod h1:0sUKpO+XNMZApeumaid7PYaUZPBIydfuWZ0dqixXo+s=
|
||||
github.com/mattermost/mattermost/server/public v0.1.12 h1:qlIU/llY0FWdHWQPtvncddQ99KJATPUX6wRHBlt8mfQ=
|
||||
github.com/mattermost/mattermost/server/public v0.1.12/go.mod h1:3RJZfl7sMedX6ihX+JMFOIAzCHhd0WQnuez+UFQS80k=
|
||||
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.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
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/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
|
||||
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
|
||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
|
||||
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/wiggin77/merror v1.0.5 h1:P+lzicsn4vPMycAf2mFf7Zk6G9eco5N+jB1qJ2XW3ME=
|
||||
github.com/wiggin77/merror v1.0.5/go.mod h1:H2ETSu7/bPE0Ymf4bEwdUoo73OOEkdClnoRisfw0Nm0=
|
||||
github.com/wiggin77/srslog v1.0.1 h1:gA2XjSMy3DrRdX9UqLuDtuVAAshb8bE1NhX1YK0Qe+8=
|
||||
github.com/wiggin77/srslog v1.0.1/go.mod h1:fehkyYDq1QfuYn60TDPu9YdY2bB85VUW2mvN1WynEls=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
220
core-plugins/mattermost-plugin-playbooks/build/manifest/main.go
Normal file
220
core-plugins/mattermost-plugin-playbooks/build/manifest/main.go
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
)
|
||||
|
||||
const pluginIDGoFileTemplate = `// This file is automatically generated. Do not modify it manually.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
)
|
||||
|
||||
var manifest *model.Manifest
|
||||
|
||||
const manifestStr = ` + "`" + `
|
||||
%s
|
||||
` + "`" + `
|
||||
|
||||
func init() {
|
||||
_ = json.NewDecoder(strings.NewReader(manifestStr)).Decode(&manifest)
|
||||
}
|
||||
`
|
||||
|
||||
const pluginIDJSFileTemplate = `// This file is automatically generated. Do not modify it manually.
|
||||
|
||||
const manifest = JSON.parse(` + "`" + `
|
||||
%s
|
||||
` + "`" + `);
|
||||
|
||||
export default manifest;
|
||||
`
|
||||
|
||||
// These build-time vars are read from shell commands and populated in ../setup.mk
|
||||
var (
|
||||
BuildHashShort string
|
||||
BuildTagLatest string
|
||||
BuildTagCurrent string
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) <= 1 {
|
||||
panic("no cmd specified")
|
||||
}
|
||||
|
||||
manifest, err := findManifest()
|
||||
if err != nil {
|
||||
panic("failed to find manifest: " + err.Error())
|
||||
}
|
||||
|
||||
cmd := os.Args[1]
|
||||
switch cmd {
|
||||
case "id":
|
||||
dumpPluginID(manifest)
|
||||
|
||||
case "version":
|
||||
dumpPluginVersion(manifest)
|
||||
|
||||
case "has_server":
|
||||
if manifest.HasServer() {
|
||||
fmt.Printf("true")
|
||||
}
|
||||
|
||||
case "has_webapp":
|
||||
if manifest.HasWebapp() {
|
||||
fmt.Printf("true")
|
||||
}
|
||||
|
||||
case "apply":
|
||||
if err := applyManifest(manifest); err != nil {
|
||||
panic("failed to apply manifest: " + err.Error())
|
||||
}
|
||||
|
||||
case "dist":
|
||||
if err := distManifest(manifest); err != nil {
|
||||
panic("failed to write manifest to dist directory: " + err.Error())
|
||||
}
|
||||
|
||||
case "check":
|
||||
if err := manifest.IsValid(); err != nil {
|
||||
panic("failed to check manifest: " + err.Error())
|
||||
}
|
||||
|
||||
default:
|
||||
panic("unrecognized command: " + cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func findManifest() (*model.Manifest, error) {
|
||||
_, manifestFilePath, err := model.FindManifest(".")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to find manifest in current working directory")
|
||||
}
|
||||
manifestFile, err := os.Open(manifestFilePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to open %s", manifestFilePath)
|
||||
}
|
||||
defer manifestFile.Close()
|
||||
|
||||
// Re-decode the manifest, disallowing unknown fields. When we write the manifest back out,
|
||||
// we don't want to accidentally clobber anything we won't preserve.
|
||||
var manifest model.Manifest
|
||||
decoder := json.NewDecoder(manifestFile)
|
||||
decoder.DisallowUnknownFields()
|
||||
if err = decoder.Decode(&manifest); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse manifest")
|
||||
}
|
||||
|
||||
// If no version is listed in the manifest, generate one based on the state of the current
|
||||
// commit, and use the first version we find (to prevent causing errors)
|
||||
if manifest.Version == "" {
|
||||
var version string
|
||||
tags := strings.Fields(BuildTagCurrent)
|
||||
for _, t := range tags {
|
||||
if strings.HasPrefix(t, "v") {
|
||||
version = t
|
||||
break
|
||||
}
|
||||
}
|
||||
if version == "" {
|
||||
if BuildTagLatest != "" {
|
||||
version = BuildTagLatest + "+" + BuildHashShort
|
||||
} else {
|
||||
version = "v0.0.0+" + BuildHashShort
|
||||
}
|
||||
}
|
||||
manifest.Version = strings.TrimPrefix(version, "v")
|
||||
}
|
||||
|
||||
// If no release notes specified, generate one from the latest tag, if present.
|
||||
if manifest.ReleaseNotesURL == "" && BuildTagLatest != "" {
|
||||
manifest.ReleaseNotesURL = manifest.HomepageURL + "releases/tag/" + BuildTagLatest
|
||||
}
|
||||
|
||||
return &manifest, nil
|
||||
}
|
||||
|
||||
// dumpPluginId writes the plugin id from the given manifest to standard out
|
||||
func dumpPluginID(manifest *model.Manifest) {
|
||||
fmt.Printf("%s", manifest.Id)
|
||||
}
|
||||
|
||||
// dumpPluginVersion writes the plugin version from the given manifest to standard out
|
||||
func dumpPluginVersion(manifest *model.Manifest) {
|
||||
fmt.Printf("%s", manifest.Version)
|
||||
}
|
||||
|
||||
// applyManifest propagates the plugin_id into the server and webapp folders, as necessary
|
||||
func applyManifest(manifest *model.Manifest) error {
|
||||
if manifest.HasServer() {
|
||||
// generate JSON representation of Manifest.
|
||||
manifestBytes, err := json.MarshalIndent(manifest, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifestStr := string(manifestBytes)
|
||||
|
||||
// write generated code to file by using Go file template.
|
||||
if err := os.WriteFile(
|
||||
"server/manifest.go",
|
||||
[]byte(fmt.Sprintf(pluginIDGoFileTemplate, manifestStr)),
|
||||
0600,
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "failed to write server/manifest.go")
|
||||
}
|
||||
}
|
||||
|
||||
if manifest.HasWebapp() {
|
||||
// generate JSON representation of Manifest.
|
||||
// JSON is very similar and compatible with JS's object literals. so, what we do here
|
||||
// is actually JS code generation.
|
||||
manifestBytes, err := json.MarshalIndent(manifest, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifestStr := string(manifestBytes)
|
||||
|
||||
// Escape newlines
|
||||
manifestStr = strings.ReplaceAll(manifestStr, `\n`, `\\n`)
|
||||
|
||||
// write generated code to file by using JS file template.
|
||||
if err := os.WriteFile(
|
||||
"webapp/src/manifest.ts",
|
||||
[]byte(fmt.Sprintf(pluginIDJSFileTemplate, manifestStr)),
|
||||
0600,
|
||||
); err != nil {
|
||||
return errors.Wrap(err, "failed to open webapp/src/manifest.ts")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// distManifest writes the manifest file to the dist directory
|
||||
func distManifest(manifest *model.Manifest) error {
|
||||
manifestBytes, err := json.MarshalIndent(manifest, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(fmt.Sprintf("dist/%s/plugin.json", manifest.Id), manifestBytes, 0600); err != nil {
|
||||
return errors.Wrap(err, "failed to write plugin.json")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
188
core-plugins/mattermost-plugin-playbooks/build/pluginctl/logs.go
Normal file
188
core-plugins/mattermost-plugin-playbooks/build/pluginctl/logs.go
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
)
|
||||
|
||||
const (
|
||||
logsPerPage = 100 // logsPerPage is the number of log entries to fetch per API call
|
||||
timeStampFormat = "2006-01-02 15:04:05.000 Z07:00"
|
||||
)
|
||||
|
||||
// logs fetches the latest 500 log entries from Mattermost,
|
||||
// and prints only the ones related to the plugin to stdout.
|
||||
func logs(ctx context.Context, client *model.Client4, pluginID string) error {
|
||||
err := checkJSONLogsSetting(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logs, err := fetchLogs(ctx, client, 0, 500, pluginID, time.Unix(0, 0))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch log entries: %w", err)
|
||||
}
|
||||
|
||||
err = printLogEntries(logs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to print logs entries: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// watchLogs fetches log entries from Mattermost and print them to stdout.
|
||||
// It will return without an error when ctx is canceled.
|
||||
func watchLogs(ctx context.Context, client *model.Client4, pluginID string) error {
|
||||
err := checkJSONLogsSetting(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
var oldestEntry string
|
||||
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
var page int
|
||||
for {
|
||||
logs, err := fetchLogs(ctx, client, page, logsPerPage, pluginID, now)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch log entries: %w", err)
|
||||
}
|
||||
|
||||
var allNew bool
|
||||
logs, oldestEntry, allNew = checkOldestEntry(logs, oldestEntry)
|
||||
|
||||
err = printLogEntries(logs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to print logs entries: %w", err)
|
||||
}
|
||||
|
||||
if !allNew {
|
||||
// No more logs to fetch
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkOldestEntry check a if logs contains new log entries.
|
||||
// It returns the filtered slice of log entries, the new oldest entry and whether or not all entries were new.
|
||||
func checkOldestEntry(logs []string, oldest string) ([]string, string, bool) {
|
||||
if len(logs) == 0 {
|
||||
return nil, oldest, false
|
||||
}
|
||||
|
||||
newOldestEntry := logs[(len(logs) - 1)]
|
||||
|
||||
i := slices.Index(logs, oldest)
|
||||
switch i {
|
||||
case -1:
|
||||
// Every log entry is new
|
||||
return logs, newOldestEntry, true
|
||||
case len(logs) - 1:
|
||||
// No new log entries
|
||||
return nil, oldest, false
|
||||
default:
|
||||
// Filter out oldest log entry
|
||||
return logs[i+1:], newOldestEntry, false
|
||||
}
|
||||
}
|
||||
|
||||
// fetchLogs fetches log entries from Mattermost
|
||||
// and filters them based on pluginID and timestamp.
|
||||
func fetchLogs(ctx context.Context, client *model.Client4, page, perPage int, pluginID string, since time.Time) ([]string, error) {
|
||||
logs, _, err := client.GetLogs(ctx, page, perPage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get logs from Mattermost: %w", err)
|
||||
}
|
||||
|
||||
logs, err = filterLogEntries(logs, pluginID, since)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to filter log entries: %w", err)
|
||||
}
|
||||
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// filterLogEntries filters a given slice of log entries by pluginID.
|
||||
// It also filters out any entries which timestamps are older then since.
|
||||
func filterLogEntries(logs []string, pluginID string, since time.Time) ([]string, error) {
|
||||
type logEntry struct {
|
||||
PluginID string `json:"plugin_id"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
var ret []string
|
||||
|
||||
for _, e := range logs {
|
||||
var le logEntry
|
||||
err := json.Unmarshal([]byte(e), &le)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal log entry into JSON: %w", err)
|
||||
}
|
||||
if le.PluginID != pluginID {
|
||||
continue
|
||||
}
|
||||
|
||||
let, err := time.Parse(timeStampFormat, le.Timestamp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unknown timestamp format: %w", err)
|
||||
}
|
||||
if let.Before(since) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Log entries returned by the API have a newline a prefix.
|
||||
// Remove that to make printing consistent.
|
||||
e = strings.TrimPrefix(e, "\n")
|
||||
|
||||
ret = append(ret, e)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// printLogEntries prints a slice of log entries to stdout.
|
||||
func printLogEntries(entries []string) error {
|
||||
for _, e := range entries {
|
||||
_, err := io.WriteString(os.Stdout, e+"\n")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write log entry to stdout: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkJSONLogsSetting(ctx context.Context, client *model.Client4) error {
|
||||
cfg, _, err := client.GetConfig(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch config: %w", err)
|
||||
}
|
||||
if cfg.LogSettings.FileJson == nil || !*cfg.LogSettings.FileJson {
|
||||
return errors.New("JSON output for file logs are disabled. Please enable LogSettings.FileJson via the configration in Mattermost.") //nolint:revive,stylecheck
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCheckOldestEntry(t *testing.T) {
|
||||
for name, tc := range map[string]struct {
|
||||
logs []string
|
||||
oldest string
|
||||
expectedLogs []string
|
||||
expectedOldest string
|
||||
expectedAllNew bool
|
||||
}{
|
||||
"nil logs": {
|
||||
logs: nil,
|
||||
oldest: "oldest",
|
||||
expectedLogs: nil,
|
||||
expectedOldest: "oldest",
|
||||
expectedAllNew: false,
|
||||
},
|
||||
"empty logs": {
|
||||
logs: []string{},
|
||||
oldest: "oldest",
|
||||
expectedLogs: nil,
|
||||
expectedOldest: "oldest",
|
||||
expectedAllNew: false,
|
||||
},
|
||||
"no new entries, one old entry": {
|
||||
logs: []string{"old"},
|
||||
oldest: "old",
|
||||
expectedLogs: []string{},
|
||||
expectedOldest: "old",
|
||||
expectedAllNew: false,
|
||||
},
|
||||
"no new entries, multipile old entries": {
|
||||
logs: []string{"old1", "old2", "old3"},
|
||||
oldest: "old3",
|
||||
expectedLogs: []string{},
|
||||
expectedOldest: "old3",
|
||||
expectedAllNew: false,
|
||||
},
|
||||
"one new entry, no old entry": {
|
||||
logs: []string{"new"},
|
||||
oldest: "old",
|
||||
expectedLogs: []string{"new"},
|
||||
expectedOldest: "new",
|
||||
expectedAllNew: true,
|
||||
},
|
||||
"multipile new entries, no old entry": {
|
||||
logs: []string{"new1", "new2", "new3"},
|
||||
oldest: "old",
|
||||
expectedLogs: []string{"new1", "new2", "new3"},
|
||||
expectedOldest: "new3",
|
||||
expectedAllNew: true,
|
||||
},
|
||||
"one new entry, one old entry": {
|
||||
logs: []string{"old", "new"},
|
||||
oldest: "old",
|
||||
expectedLogs: []string{"new"},
|
||||
expectedOldest: "new",
|
||||
expectedAllNew: false,
|
||||
},
|
||||
"one new entry, multipile old entries": {
|
||||
logs: []string{"old1", "old2", "old3", "new"},
|
||||
oldest: "old3",
|
||||
expectedLogs: []string{"new"},
|
||||
expectedOldest: "new",
|
||||
expectedAllNew: false,
|
||||
},
|
||||
"multipile new entries, ultipile old entries": {
|
||||
logs: []string{"old1", "old2", "old3", "new1", "new2", "new3"},
|
||||
oldest: "old3",
|
||||
expectedLogs: []string{"new1", "new2", "new3"},
|
||||
expectedOldest: "new3",
|
||||
expectedAllNew: false,
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
logs, oldest, allNew := checkOldestEntry(tc.logs, tc.oldest)
|
||||
|
||||
if allNew != tc.expectedAllNew {
|
||||
t.Logf("expected allNew: %v, got %v", tc.expectedAllNew, allNew)
|
||||
t.Fail()
|
||||
}
|
||||
if oldest != tc.expectedOldest {
|
||||
t.Logf("expected oldest: %v, got %v", tc.expectedOldest, oldest)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
compareSlice(t, tc.expectedLogs, logs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterLogEntries(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
for name, tc := range map[string]struct {
|
||||
logs []string
|
||||
pluginID string
|
||||
since time.Time
|
||||
expectedLogs []string
|
||||
expectedErr bool
|
||||
}{
|
||||
"nil slice": {
|
||||
logs: nil,
|
||||
expectedLogs: nil,
|
||||
expectedErr: false,
|
||||
},
|
||||
"empty slice": {
|
||||
logs: []string{},
|
||||
expectedLogs: nil,
|
||||
expectedErr: false,
|
||||
},
|
||||
"no JSON": {
|
||||
logs: []string{
|
||||
`{"foo"`,
|
||||
},
|
||||
expectedLogs: nil,
|
||||
expectedErr: true,
|
||||
},
|
||||
"unknown time format": {
|
||||
logs: []string{
|
||||
`{"message":"foo", "plugin_id": "some.plugin.id", "timestamp": "2023-12-18 10:58:53"}`,
|
||||
},
|
||||
pluginID: "some.plugin.id",
|
||||
expectedLogs: nil,
|
||||
expectedErr: true,
|
||||
},
|
||||
"one matching entry": {
|
||||
logs: []string{
|
||||
`{"message":"foo", "plugin_id": "some.plugin.id", "timestamp": "2023-12-18 10:58:53.091 +01:00"}`,
|
||||
},
|
||||
pluginID: "some.plugin.id",
|
||||
expectedLogs: []string{
|
||||
`{"message":"foo", "plugin_id": "some.plugin.id", "timestamp": "2023-12-18 10:58:53.091 +01:00"}`,
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
"filter out non plugin entries": {
|
||||
logs: []string{
|
||||
`{"message":"bar1", "timestamp": "2023-12-18 10:58:52.091 +01:00"}`,
|
||||
`{"message":"foo", "plugin_id": "some.plugin.id", "timestamp": "2023-12-18 10:58:53.091 +01:00"}`,
|
||||
`{"message":"bar2", "timestamp": "2023-12-18 10:58:54.091 +01:00"}`,
|
||||
},
|
||||
pluginID: "some.plugin.id",
|
||||
expectedLogs: []string{
|
||||
`{"message":"foo", "plugin_id": "some.plugin.id", "timestamp": "2023-12-18 10:58:53.091 +01:00"}`,
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
"filter out old entries": {
|
||||
logs: []string{
|
||||
fmt.Sprintf(`{"message":"old2", "plugin_id": "some.plugin.id", "timestamp": "%s"}`, now.Add(-2*time.Second).Format(timeStampFormat)),
|
||||
fmt.Sprintf(`{"message":"old1", "plugin_id": "some.plugin.id", "timestamp": "%s"}`, now.Add(-1*time.Second).Format(timeStampFormat)),
|
||||
fmt.Sprintf(`{"message":"now", "plugin_id": "some.plugin.id", "timestamp": "%s"}`, now.Format(timeStampFormat)),
|
||||
fmt.Sprintf(`{"message":"new1", "plugin_id": "some.plugin.id", "timestamp": "%s"}`, now.Add(1*time.Second).Format(timeStampFormat)),
|
||||
fmt.Sprintf(`{"message":"new2", "plugin_id": "some.plugin.id", "timestamp": "%s"}`, now.Add(2*time.Second).Format(timeStampFormat)),
|
||||
},
|
||||
pluginID: "some.plugin.id",
|
||||
since: now,
|
||||
expectedLogs: []string{
|
||||
fmt.Sprintf(`{"message":"new1", "plugin_id": "some.plugin.id", "timestamp": "%s"}`, now.Add(1*time.Second).Format(timeStampFormat)),
|
||||
fmt.Sprintf(`{"message":"new2", "plugin_id": "some.plugin.id", "timestamp": "%s"}`, now.Add(2*time.Second).Format(timeStampFormat)),
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
logs, err := filterLogEntries(tc.logs, tc.pluginID, tc.since)
|
||||
if tc.expectedErr {
|
||||
if err == nil {
|
||||
t.Logf("expected error, got nil")
|
||||
t.Fail()
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Logf("expected no error, got %v", err)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
compareSlice(t, tc.expectedLogs, logs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func compareSlice[S ~[]E, E comparable](t *testing.T, expected, got S) {
|
||||
if len(expected) != len(got) {
|
||||
t.Logf("expected len: %v, got %v", len(expected), len(got))
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
for i := 0; i < len(expected); i++ {
|
||||
if expected[i] != got[i] {
|
||||
t.Logf("expected [%d]: %v, got %v", i, expected[i], got[i])
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
187
core-plugins/mattermost-plugin-playbooks/build/pluginctl/main.go
Normal file
187
core-plugins/mattermost-plugin-playbooks/build/pluginctl/main.go
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// main handles deployment of the plugin to a development server using the Client4 API.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
)
|
||||
|
||||
const commandTimeout = 120 * time.Second
|
||||
|
||||
const helpText = `
|
||||
Usage:
|
||||
pluginctl deploy <plugin id> <bundle path>
|
||||
pluginctl disable <plugin id>
|
||||
pluginctl enable <plugin id>
|
||||
pluginctl reset <plugin id>
|
||||
`
|
||||
|
||||
func main() {
|
||||
err := pluginctl()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed: %s\n", err.Error())
|
||||
fmt.Print(helpText)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func pluginctl() error {
|
||||
if len(os.Args) < 3 {
|
||||
return errors.New("invalid number of arguments")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), commandTimeout)
|
||||
defer cancel()
|
||||
|
||||
client, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "deploy":
|
||||
if len(os.Args) < 4 {
|
||||
return errors.New("invalid number of arguments")
|
||||
}
|
||||
return deploy(ctx, client, os.Args[2], os.Args[3])
|
||||
case "disable":
|
||||
return disablePlugin(ctx, client, os.Args[2])
|
||||
case "enable":
|
||||
return enablePlugin(ctx, client, os.Args[2])
|
||||
case "reset":
|
||||
return resetPlugin(ctx, client, os.Args[2])
|
||||
case "logs":
|
||||
return logs(ctx, client, os.Args[2])
|
||||
case "logs-watch":
|
||||
return watchLogs(context.WithoutCancel(ctx), client, os.Args[2]) // Keep watching forever
|
||||
default:
|
||||
return errors.New("invalid second argument")
|
||||
}
|
||||
}
|
||||
|
||||
func getClient(ctx context.Context) (*model.Client4, error) {
|
||||
socketPath := os.Getenv("MM_LOCALSOCKETPATH")
|
||||
if socketPath == "" {
|
||||
socketPath = model.LocalModeSocketPath
|
||||
}
|
||||
|
||||
client, connected := getUnixClient(socketPath)
|
||||
if connected {
|
||||
log.Printf("Connecting using local mode over %s", socketPath)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
if os.Getenv("MM_LOCALSOCKETPATH") != "" {
|
||||
log.Printf("No socket found at %s for local mode deployment. Attempting to authenticate with credentials.", socketPath)
|
||||
}
|
||||
|
||||
siteURL := os.Getenv("MM_SERVICESETTINGS_SITEURL")
|
||||
adminToken := os.Getenv("MM_ADMIN_TOKEN")
|
||||
adminUsername := os.Getenv("MM_ADMIN_USERNAME")
|
||||
adminPassword := os.Getenv("MM_ADMIN_PASSWORD")
|
||||
|
||||
if siteURL == "" {
|
||||
return nil, errors.New("MM_SERVICESETTINGS_SITEURL is not set")
|
||||
}
|
||||
|
||||
client = model.NewAPIv4Client(siteURL)
|
||||
|
||||
if adminToken != "" {
|
||||
log.Printf("Authenticating using token against %s.", siteURL)
|
||||
client.SetToken(adminToken)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
if adminUsername != "" && adminPassword != "" {
|
||||
client := model.NewAPIv4Client(siteURL)
|
||||
log.Printf("Authenticating as %s against %s.", adminUsername, siteURL)
|
||||
_, _, err := client.Login(ctx, adminUsername, adminPassword)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to login as %s: %w", adminUsername, err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("one of MM_ADMIN_TOKEN or MM_ADMIN_USERNAME/MM_ADMIN_PASSWORD must be defined")
|
||||
}
|
||||
|
||||
func getUnixClient(socketPath string) (*model.Client4, bool) {
|
||||
_, err := net.Dial("unix", socketPath)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return model.NewAPIv4SocketClient(socketPath), true
|
||||
}
|
||||
|
||||
// deploy attempts to upload and enable a plugin via the Client4 API.
|
||||
// It will fail if plugin uploads are disabled.
|
||||
func deploy(ctx context.Context, client *model.Client4, pluginID, bundlePath string) error {
|
||||
pluginBundle, err := os.Open(bundlePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s: %w", bundlePath, err)
|
||||
}
|
||||
defer pluginBundle.Close()
|
||||
|
||||
log.Print("Uploading plugin via API.")
|
||||
_, _, err = client.UploadPluginForced(ctx, pluginBundle)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upload plugin bundle: %s", err.Error())
|
||||
}
|
||||
|
||||
log.Print("Enabling plugin.")
|
||||
_, err = client.EnablePlugin(ctx, pluginID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to enable plugin: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// disablePlugin attempts to disable the plugin via the Client4 API.
|
||||
func disablePlugin(ctx context.Context, client *model.Client4, pluginID string) error {
|
||||
log.Print("Disabling plugin.")
|
||||
_, err := client.DisablePlugin(ctx, pluginID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to disable plugin: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// enablePlugin attempts to enable the plugin via the Client4 API.
|
||||
func enablePlugin(ctx context.Context, client *model.Client4, pluginID string) error {
|
||||
log.Print("Enabling plugin.")
|
||||
_, err := client.EnablePlugin(ctx, pluginID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to enable plugin: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// resetPlugin attempts to reset the plugin via the Client4 API.
|
||||
func resetPlugin(ctx context.Context, client *model.Client4, pluginID string) error {
|
||||
err := disablePlugin(ctx, client, pluginID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = enablePlugin(ctx, client, pluginID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
1114
core-plugins/mattermost-plugin-playbooks/build/release/main.go
Normal file
1114
core-plugins/mattermost-plugin-playbooks/build/release/main.go
Normal file
File diff suppressed because it is too large
Load diff
2259
core-plugins/mattermost-plugin-playbooks/build/release/main_test.go
Normal file
2259
core-plugins/mattermost-plugin-playbooks/build/release/main_test.go
Normal file
File diff suppressed because it is too large
Load diff
53
core-plugins/mattermost-plugin-playbooks/build/setup.mk
Normal file
53
core-plugins/mattermost-plugin-playbooks/build/setup.mk
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Ensure that go is installed. Note that this is independent of whether or not a server is being
|
||||
# built, since the build script itself uses go.
|
||||
ifeq ($(GO),)
|
||||
$(error "go is not available: see https://golang.org/doc/install")
|
||||
endif
|
||||
|
||||
# Gather build variables to inject into the manifest tool
|
||||
BUILD_HASH_SHORT = $(shell git rev-parse --short HEAD)
|
||||
BUILD_TAG_LATEST = $(shell git describe --tags --match 'v*' --abbrev=0 2>/dev/null)
|
||||
BUILD_TAG_CURRENT = $(shell git tag --points-at HEAD)
|
||||
|
||||
# Ensure that the build tools are compiled. Go's caching makes this quick.
|
||||
$(shell cd build/manifest && $(GO) build -ldflags '-X "main.BuildHashShort=$(BUILD_HASH_SHORT)" -X "main.BuildTagLatest=$(BUILD_TAG_LATEST)" -X "main.BuildTagCurrent=$(BUILD_TAG_CURRENT)"' -o ../bin/manifest)
|
||||
|
||||
# Ensure that the deployment tools are compiled. Go's caching makes this quick.
|
||||
$(shell cd build/pluginctl && $(GO) build -o ../bin/pluginctl)
|
||||
|
||||
# Ensure that the release tool is compiled. Go's caching makes this quick.
|
||||
$(shell cd build/release && $(GO) build -o ../bin/release)
|
||||
|
||||
# Extract the plugin id from the manifest.
|
||||
PLUGIN_ID ?= $(shell build/bin/manifest id)
|
||||
ifeq ($(PLUGIN_ID),)
|
||||
$(error "Cannot parse id from $(MANIFEST_FILE)")
|
||||
endif
|
||||
|
||||
# Extract the plugin version from the manifest.
|
||||
PLUGIN_VERSION ?= $(shell build/bin/manifest version)
|
||||
ifeq ($(PLUGIN_VERSION),)
|
||||
$(error "Cannot parse version from $(MANIFEST_FILE)")
|
||||
endif
|
||||
|
||||
# Determine if a server is defined in the manifest.
|
||||
HAS_SERVER ?= $(shell build/bin/manifest has_server)
|
||||
|
||||
# Determine if a webapp is defined in the manifest.
|
||||
HAS_WEBAPP ?= $(shell build/bin/manifest has_webapp)
|
||||
|
||||
# Determine if a /public folder is in use
|
||||
HAS_PUBLIC ?= $(wildcard public/.)
|
||||
|
||||
# Determine if the mattermost-utilities repo is present
|
||||
HAS_MM_UTILITIES ?= $(wildcard $(MM_UTILITIES_DIR)/.)
|
||||
|
||||
# Store the current path for later use
|
||||
PWD ?= $(shell pwd)
|
||||
|
||||
# Ensure that npm (and thus node) is installed.
|
||||
ifneq ($(HAS_WEBAPP),)
|
||||
ifeq ($(NPM),)
|
||||
$(error "npm is not available: see https://www.npmjs.com/get-npm")
|
||||
endif
|
||||
endif
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
# Changelogs for mattermost-plugin-playbooks
|
||||
|
||||
This directory contains generated changelogs for each release, complete with QA review guidance.
|
||||
|
||||
## Structure
|
||||
|
||||
Each changelog file is named `<from-version>-<to-version>.md` (e.g., `v2.7.0-v2.8.0.md`).
|
||||
|
||||
## Changelog Sections
|
||||
|
||||
### Standard Sections
|
||||
- **Summary** — Overview of the release (features, fixes, improvements, breaking changes)
|
||||
- **Bug Fixes** — Issues that were resolved
|
||||
- **Features** — New user-facing functionality
|
||||
- **UI Improvements** — Visual or UX changes
|
||||
- **Performance** — Speed and optimization improvements
|
||||
- **Infrastructure** — CI, build system, load testing changes
|
||||
- **Chores / Maintenance** — Dependencies, refactors, test improvements, translations
|
||||
- **Commit List** — All commits in the range
|
||||
- **Recommended Next Version** — Semver suggestion and reasoning
|
||||
|
||||
### 🧪 QA Review Checklist (NEW)
|
||||
|
||||
**Purpose:** Help QA prioritize and efficiently test RC releases by calling out UI-facing changes.
|
||||
|
||||
**What gets included:**
|
||||
- ✅ New features affecting the UI
|
||||
- ✅ Bug fixes that change user workflows
|
||||
- ✅ Navigation and menu changes
|
||||
- ✅ Visual/branding updates
|
||||
- ❌ Internal refactors
|
||||
- ❌ Dependency updates
|
||||
- ❌ Test improvements
|
||||
- ❌ Translations
|
||||
|
||||
**Information provided for each change:**
|
||||
1. **PR number and author** — For questions or deeper context
|
||||
2. **What to test** — Specific steps QA should follow
|
||||
3. **Impact area(s)** — Where in the UI this change appears (RHS, Modal, Sidebar, etc.)
|
||||
4. **Regression risk** — High/Medium/Low to help QA prioritize effort
|
||||
|
||||
### Organization
|
||||
|
||||
The QA section is organized by impact type:
|
||||
- **Critical UI Changes** — New features, significant fixes, must-test items
|
||||
- **Visual / UX Changes** — Styling, layout, branding updates
|
||||
- **Navigation / Flow Changes** — Menu items, routing, link behavior
|
||||
|
||||
|
||||
|
||||
## For Release Engineers
|
||||
|
||||
When generating a new changelog:
|
||||
|
||||
1. **Use the standard changelog skill** (see `.pi/skills/changelog/SKILL.md`)
|
||||
2. **Extract UI-facing PRs** — Identify which PRs changed the UI
|
||||
3. **Populate QA section** — For each UI PR, add:
|
||||
- PR number and author
|
||||
- Clear description
|
||||
- Test guidance (specific steps or areas)
|
||||
- Impact location
|
||||
- Regression risk level
|
||||
4. **Save the file** — `changelogs/<from>-<to>.md`
|
||||
|
||||
## For QA Leads
|
||||
|
||||
When preparing to test an RC:
|
||||
|
||||
1. **Read the QA Review Checklist first** — This tells you what changed and where
|
||||
2. **Prioritize by Regression Risk** — High-risk items get more thorough testing
|
||||
3. **Use "What to test" guidance** — Follow the specific steps listed for each change
|
||||
4. **Know the PR authors** — Reach out if you find issues or need clarification
|
||||
5. **Check "Impact area(s)"** — Focus your regression testing on these zones
|
||||
|
||||
## Tips
|
||||
|
||||
- **Each PR entry includes the author** so QA can ask follow-up questions
|
||||
- **"What to test" is specific** — Not vague; includes actual user workflows
|
||||
- **Regression risk helps triage** — High-risk fixes get more test coverage
|
||||
- **Impact areas are consistent** — Helps QA mentally map the app
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
## Changelog: v2.7.0 → origin/master
|
||||
|
||||
### 📋 Summary
|
||||
|
||||
15 commits across 12 PRs since v2.7.0. This release contains 3 security/permissions bug fixes, a branding revert (restoring "Playbooks" naming in the RHS after the checklists rebrand was rolled back), a UX improvement (overflow tooltips), and significant load-testing infrastructure work. No breaking changes or new user-facing API endpoints.
|
||||
|
||||
---
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **[PR #2204](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2204)** — Fix checklist creation requiring `run_create` team permission instead of channel post permission, which blocked users in production where `run_create` is not granted by default — *@jgheithcock*
|
||||
- **[PR #2192](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2192)** — Fix privilege escalation when changing a playbook's team without manage-members permission (MM-66474); adds destination-team access check — *@jgheithcock*
|
||||
- **[PR #2191](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2191)** — Enforce playbook view permissions across API endpoints, GraphQL loaders, categories, and slash commands (MM-67325); adds `FilterPlaybooksByViewPermission` — *@jgheithcock*
|
||||
|
||||
---
|
||||
|
||||
### 💄 UI Improvements
|
||||
|
||||
- **[PR #2205](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2205)** — Restore Playbooks branding in RHS: title, channel header tooltip, and AppBar icon reverted from "Checklists" back to "Playbooks"; removed "Powered by Playbooks" footer — *@calebroseland*
|
||||
- **[PR #2207](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2207)** — Remove the "Playbook Runs are now Checklists" rebrand tour point that is no longer needed — *@calebroseland*
|
||||
- **[PR #2179](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2179)** — Add tooltips for overflowing/truncated text in checklists RHS (run list card titles and context menu dropdown title) — *@calebroseland*
|
||||
|
||||
---
|
||||
|
||||
### 🏗️ Infrastructure / Load Testing
|
||||
|
||||
- **[PR #2182](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2182)** — Implement browser-based simulation scripts for Playbooks client-side load testing, including build scripts, configuration, and sample scenario — *@M-ZubairAhmed*
|
||||
- **[PR #2171](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2171)** — Implement the load-test `SimulController` and `GenController` Plugin interfaces (OpenRHS, HookLogin, HookSwitchTeam, HookSwitchChannel, CreatePlaybook, CreateRun) — *@agarciamontoro*
|
||||
|
||||
---
|
||||
|
||||
### 🔧 Chores / Maintenance
|
||||
|
||||
- **[PR #2208](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2208)** — Upgrade `qs` from 6.10.5 to 6.14.1 (security fix) — *@JulienTant*
|
||||
- **[PR #2201](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2201)** — Clear all ownership rules from CODEOWNERS — *@calebroseland*
|
||||
- **[PR #2195](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2195)** — Update NOTICE.txt with updated dependencies — *@unified-ci-app*
|
||||
- **[PR #2189](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2189)** — Isolate release TUI unit tests from real git repo to prevent conflicts with existing tags — *@calebroseland*
|
||||
- **[PR #2200](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2200)**, **[PR #2196](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2196)**, **[PR #2183](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2183)** — Translations updates from Mattermost Weblate
|
||||
|
||||
---
|
||||
|
||||
### 🧪 QA Review Checklist
|
||||
|
||||
This section highlights **UI-facing changes** that QA should prioritize when validating the RC.
|
||||
Each item references the PR that introduced the change and the author for follow-up questions.
|
||||
|
||||
#### Critical UI Changes
|
||||
|
||||
- **[PR #2205](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2205)** — Restore Playbooks branding in RHS (revert checklists rebrand) — *@calebroseland*
|
||||
- **What to test:** Verify RHS title says "Playbooks" (not "Checklists"), channel header tooltip says "Playbooks", AppBar icon is the original Playbooks icon, and "Powered by Playbooks" footer is gone from the run list
|
||||
- **Impact area(s):** RHS title bar, Channel Header button tooltip, AppBar icon, Run List
|
||||
- **Regression risk:** Medium — Touches multiple UI entry points; verify no layout breakage or missing icons
|
||||
|
||||
#### Visual / UX Changes
|
||||
|
||||
- **[PR #2179](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2179)** — Add tooltips for overflowing text in checklists UI — *@calebroseland*
|
||||
- **What to test:** Create runs with long names. In the RHS run list, hover over a truncated run name — tooltip should appear with full text. Open the context menu dropdown — hover over a truncated title — tooltip should appear. Short names that aren't truncated should NOT show a tooltip.
|
||||
- **Impact area(s):** RHS run list cards, Context menu dropdown title
|
||||
|
||||
- **[PR #2207](https://github.com/mattermost/mattermost-plugin-playbooks/pull/2207)** — Remove checklists rebrand tour point — *@calebroseland*
|
||||
- **What to test:** Verify the "Playbook Runs are now Checklists" tour step no longer appears for new or existing users. Ensure the rest of the onboarding tour still completes correctly.
|
||||
- **Impact area(s):** Onboarding tour, Checklists panel
|
||||
|
||||
---
|
||||
|
||||
### 📝 Commit List
|
||||
|
||||
| Commit | Description |
|
||||
|--------|-------------|
|
||||
| `887d9cac` | Fix MM-67325 (#2191) |
|
||||
| `3044bbc3` | fix(deps): upgrade qs from 6.10.5 to 6.14.1 (#2208) |
|
||||
| `3b680f95` | MM-67785: Remove checklists rebrand tour point (#2207) |
|
||||
| `6ddc336b` | [MM-65832] Implement the simulation scripts for Playbooks client-side load testing (#2182) |
|
||||
| `2a1804a4` | MM-67701: Restore Playbooks branding in RHS (#2205) |
|
||||
| `78e721b4` | Fix MM-67648 (and preserve MM-66249 fix) (#2204) |
|
||||
| `c799390d` | chore(repo): clear CODEOWNERS entries (#2201) |
|
||||
| `b53bc39b` | Translations update from Mattermost Weblate (#2200) |
|
||||
| `e1d6ec2c` | Fix MM-66474 (#2192) |
|
||||
| `7be951a2` | chore: Update NOTICE.txt file with updated dependencies (#2195) |
|
||||
| `3ba38fec` | MM-66895: Add tooltip for overflowing text in checklists UI (#2179) |
|
||||
| `483f1495` | Translations update from Mattermost Weblate (#2196) |
|
||||
| `8f646f6e` | MM-66991: Implement the Plugin interface (#2171) |
|
||||
| `7e4af2ee` | Translations update from Mattermost Weblate (#2183) |
|
||||
| `dcb70291` | test(release): isolate unit tests from real git repo (#2189) |
|
||||
|
||||
---
|
||||
|
||||
### 🔖 Recommended Next Version: **v2.8.0**
|
||||
|
||||
**Reasoning:** While there are no new API endpoints, the user-visible surface changes significantly. The Playbooks branding revert (#2205, #2207) changes the RHS title, AppBar icon, channel header tooltip, and removes a tour point — users upgrading from v2.7.0 (which shipped with "Checklists" branding) will see a noticeably different UI. The overflow tooltips (#2179) add new UX behavior. The permissions filtering fix (#2191) changes what playbooks are visible across list views, categories, and commands. Taken together, these are meaningful user-facing changes that warrant a MINOR bump.
|
||||
|
||||
**→ Recommended version: `v2.8.0`**
|
||||
202
core-plugins/mattermost-plugin-playbooks/client/LICENSE
Normal file
202
core-plugins/mattermost-plugin-playbooks/client/LICENSE
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
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.
|
||||
63
core-plugins/mattermost-plugin-playbooks/client/action.go
Normal file
63
core-plugins/mattermost-plugin-playbooks/client/action.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
type GenericChannelActionWithoutPayload struct {
|
||||
ID string `json:"id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
Enabled bool `json:"enabled"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
ActionType string `json:"action_type"`
|
||||
TriggerType string `json:"trigger_type"`
|
||||
}
|
||||
|
||||
type GenericChannelAction struct {
|
||||
GenericChannelActionWithoutPayload
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
type WelcomeMessagePayload struct {
|
||||
Message string `json:"message" mapstructure:"message"`
|
||||
}
|
||||
|
||||
type PromptRunPlaybookFromKeywordsPayload struct {
|
||||
Keywords []string `json:"keywords" mapstructure:"keywords"`
|
||||
PlaybookID string `json:"playbook_id" mapstructure:"playbook_id"`
|
||||
}
|
||||
|
||||
type CategorizeChannelPayload struct {
|
||||
CategoryName string `json:"category_name" mapstructure:"category_name"`
|
||||
}
|
||||
|
||||
type WelcomeMessageAction struct {
|
||||
GenericChannelActionWithoutPayload
|
||||
Payload WelcomeMessagePayload `json:"payload"`
|
||||
}
|
||||
|
||||
const (
|
||||
// Action types
|
||||
ActionTypeWelcomeMessage = "send_welcome_message"
|
||||
ActionTypePromptRunPlaybook = "prompt_run_playbook"
|
||||
ActionTypeCategorizeChannel = "categorize_channel"
|
||||
|
||||
// Trigger types
|
||||
TriggerTypeNewMemberJoins = "new_member_joins"
|
||||
TriggerTypeKeywordsPosted = "keywords"
|
||||
)
|
||||
|
||||
// ChannelActionListOptions specifies the optional parameters to the
|
||||
// ActionsService.List method.
|
||||
type ChannelActionListOptions struct {
|
||||
TriggerType string `url:"trigger_type,omitempty"`
|
||||
ActionType string `url:"action_type,omitempty"`
|
||||
}
|
||||
|
||||
// ChannelActionCreateOptions specifies the parameters for ActionsService.Create method.
|
||||
type ChannelActionCreateOptions struct {
|
||||
ChannelID string `json:"channel_id"`
|
||||
Enabled bool `json:"enabled"`
|
||||
ActionType string `json:"action_type"`
|
||||
TriggerType string `json:"trigger_type"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
78
core-plugins/mattermost-plugin-playbooks/client/actions.go
Normal file
78
core-plugins/mattermost-plugin-playbooks/client/actions.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ActionsService handles communication with the actions related
|
||||
// methods of the Playbook API.
|
||||
type ActionsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Create an action. Returns the id of the newly created action.
|
||||
func (s *ActionsService) Create(ctx context.Context, channelID string, opts ChannelActionCreateOptions) (string, error) {
|
||||
actionURL := fmt.Sprintf("actions/channels/%s", channelID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, actionURL, opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var result struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
resp, err := s.client.do(ctx, req, &result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
return "", fmt.Errorf("expected status code %d", http.StatusCreated)
|
||||
}
|
||||
|
||||
return result.ID, nil
|
||||
}
|
||||
|
||||
// List the actions in a channel.
|
||||
func (s *ActionsService) List(ctx context.Context, channelID string, opts ChannelActionListOptions) ([]GenericChannelAction, error) {
|
||||
actionURL, err := addOptions(fmt.Sprintf("actions/channels/%s", channelID), opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build options: %w", err)
|
||||
}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, actionURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build request: %w", err)
|
||||
}
|
||||
|
||||
result := []GenericChannelAction{}
|
||||
resp, err := s.client.do(ctx, req, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute request: %w", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Update an existing action.
|
||||
func (s *ActionsService) Update(ctx context.Context, action GenericChannelAction) error {
|
||||
updateURL := fmt.Sprintf("actions/channels/%s/%s", action.ChannelID, action.ID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, updateURL, action)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
35
core-plugins/mattermost-plugin-playbooks/client/bot.go
Normal file
35
core-plugins/mattermost-plugin-playbooks/client/bot.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type BotService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// List the conditions for a run (read-only).
|
||||
func (s *BotService) Connect(ctx context.Context) error {
|
||||
connectURL := "bot/connect"
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, connectURL, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute request: %w", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to connect the bot: %d", resp.StatusCode)
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CategoriesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// CategoriesIsFavoriteOptions specifies the optional parameters to the
|
||||
// CategoriesService.IsFavorite method
|
||||
type CategoriesIsFavoriteOptions struct {
|
||||
TeamId string `url:"team_id,omitempty"`
|
||||
ItemId string `url:"item_id,omitempty"`
|
||||
ItemType string `url:"type,omitempty"`
|
||||
}
|
||||
|
||||
// List the conditions for a run (read-only).
|
||||
func (s *CategoriesService) IsFavorite(ctx context.Context, opts CategoriesIsFavoriteOptions) (bool, error) {
|
||||
isFavoriteURL, err := addOptions("my_categories/favorites", opts)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, isFavoriteURL, nil)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to build request: %w", err)
|
||||
}
|
||||
|
||||
var isFavorite bool
|
||||
resp, err := s.client.do(ctx, req, &isFavorite)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to execute request: %w", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
return isFavorite, nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("unable to get favorite status: %d", resp.StatusCode)
|
||||
}
|
||||
294
core-plugins/mattermost-plugin-playbooks/client/client.go
Normal file
294
core-plugins/mattermost-plugin-playbooks/client/client.go
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
)
|
||||
|
||||
const (
|
||||
apiVersion = "v0"
|
||||
manifestID = "playbooks"
|
||||
userAgent = "go-client/" + apiVersion
|
||||
)
|
||||
|
||||
// Client manages communication with the Playbooks API.
|
||||
type Client struct {
|
||||
// client is the underlying HTTP client used to make API requests.
|
||||
client *http.Client
|
||||
// BaseURL is the base HTTP endpoint for the Playbooks plugin.
|
||||
BaseURL *url.URL
|
||||
// User agent used when communicating with the Playbooks API.
|
||||
UserAgent string
|
||||
|
||||
// PlaybookRuns is a collection of methods used to interact with playbook runs.
|
||||
PlaybookRuns *PlaybookRunService
|
||||
// Playbooks is a collection of methods used to interact with playbooks.
|
||||
Playbooks *PlaybooksService
|
||||
// Settings is a collection of methods used to interact with settings.
|
||||
Settings *SettingsService
|
||||
// Actions is a collection of methods used to interact with actions.
|
||||
Actions *ActionsService
|
||||
// Stats is a collection of methods used to interact with stats.
|
||||
Stats *StatsService
|
||||
// Reminders is a collection of methods used to interact with reminders.
|
||||
Reminders *RemindersService
|
||||
// TabApp is a collection of methods used to interact with playbooks from the tabapp.
|
||||
TabApp *TabAppService
|
||||
// PlaybookConditions is a collection of methods used to interact with playbook conditions.
|
||||
PlaybookConditions *PlaybookConditionsService
|
||||
// RunConditions is a collection of methods used to interact with run conditions.
|
||||
RunConditions *RunConditionsService
|
||||
// Bot is a collection of methods used to interact with the Playbooks bot.
|
||||
Bot *BotService
|
||||
// Bot is a collection of methods used to interact with categories.
|
||||
Categories *CategoriesService
|
||||
}
|
||||
|
||||
// New creates a new instance of Client using the configuration from the given Mattermost Client.
|
||||
func New(client4 *model.Client4) (*Client, error) {
|
||||
ctx := context.Background()
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: client4.AuthToken},
|
||||
)
|
||||
|
||||
return newClient(client4.URL, oauth2.NewClient(ctx, ts))
|
||||
}
|
||||
|
||||
// newClient creates a new instance of Client from the given URL and http.Client.
|
||||
func newClient(mattermostSiteURL string, httpClient *http.Client) (*Client, error) {
|
||||
siteURL, err := url.Parse(mattermostSiteURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Client{client: httpClient, BaseURL: siteURL, UserAgent: userAgent}
|
||||
c.PlaybookRuns = &PlaybookRunService{c}
|
||||
c.Playbooks = &PlaybooksService{c}
|
||||
c.Settings = &SettingsService{c}
|
||||
c.Actions = &ActionsService{c}
|
||||
c.Stats = &StatsService{c}
|
||||
c.Reminders = &RemindersService{c}
|
||||
c.TabApp = &TabAppService{c}
|
||||
c.PlaybookConditions = &PlaybookConditionsService{c}
|
||||
c.RunConditions = &RunConditionsService{c}
|
||||
c.Bot = &BotService{c}
|
||||
c.Categories = &CategoriesService{c}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// newRequest creates an API request, JSON-encoding any given body parameter.
|
||||
func (c *Client) newRequest(method, endpoint string, body interface{}) (*http.Request, error) {
|
||||
u, err := c.BaseURL.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid endpoint %s", endpoint)
|
||||
}
|
||||
|
||||
var buf io.ReadWriter
|
||||
if body != nil {
|
||||
buf = &bytes.Buffer{}
|
||||
enc := json.NewEncoder(buf)
|
||||
enc.SetEscapeHTML(false)
|
||||
err = enc.Encode(body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to encode body %s", body)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, u.String(), buf)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create http request for url %s", u)
|
||||
}
|
||||
|
||||
if buf != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
if c.UserAgent != "" {
|
||||
req.Header.Set("User-Agent", c.UserAgent)
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// newAPIRequest creates an API request, JSON-encoding any given body parameter.
|
||||
func (c *Client) newAPIRequest(method, endpoint string, body interface{}) (*http.Request, error) {
|
||||
return c.newRequest(method, buildAPIURL(endpoint), body)
|
||||
}
|
||||
|
||||
// buildAPIURL constructs the path to the given endpoint.
|
||||
func buildAPIURL(endpoint string) string {
|
||||
return fmt.Sprintf("plugins/%s/api/%s/%s", manifestID, apiVersion, endpoint)
|
||||
}
|
||||
|
||||
// do sends an API request and returns the API response.
|
||||
//
|
||||
// The API response is JSON decoded and stored in the value pointed to by v, or returned as an
|
||||
// error if an API error has occurred. If v implements the io.Writer
|
||||
// interface, the raw response body will be written to v, without attempting to
|
||||
// first decode it.
|
||||
func (c *Client) do(ctx context.Context, req *http.Request, v interface{}) (*http.Response, error) {
|
||||
if ctx == nil {
|
||||
return nil, errors.New("context must be non-nil")
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, errors.Wrapf(ctx.Err(), "client err=%s", err.Error())
|
||||
default:
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = checkResponse(resp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if v != nil {
|
||||
if w, ok := v.(io.Writer); ok {
|
||||
if _, err = io.Copy(w, resp.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
|
||||
decErr := json.NewDecoder(bytes.NewReader(body)).Decode(v)
|
||||
if decErr == io.EOF {
|
||||
// TODO: Confirm if this happens only on empty bodies. If so, check that first before decoding.
|
||||
decErr = nil // ignore EOF errors caused by empty response body
|
||||
}
|
||||
if decErr != nil {
|
||||
err = decErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
type GraphQLInput struct {
|
||||
Query string `json:"query"`
|
||||
OperationName string `json:"operationName"`
|
||||
Variables map[string]interface{} `json:"variables"`
|
||||
}
|
||||
|
||||
func (c *Client) DoGraphql(ctx context.Context, input *GraphQLInput, v interface{}) error {
|
||||
url := "query"
|
||||
req, err := c.newAPIRequest(http.MethodPost, url, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.do(ctx, req, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkResponse checks the API response for an error.
|
||||
//
|
||||
// Any response with a status code outside 2xx is considered an error, and its body inspected for
|
||||
// an optional `Error` property in a JSON struct.
|
||||
func checkResponse(r *http.Response) error {
|
||||
if c := r.StatusCode; http.StatusOK <= c && c <= 299 {
|
||||
return nil
|
||||
}
|
||||
|
||||
errorResponse := &ErrorResponse{
|
||||
StatusCode: r.StatusCode,
|
||||
Method: r.Request.Method,
|
||||
URL: r.Request.URL.String(),
|
||||
}
|
||||
data, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
errorResponse.Err = fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
r.Body = ioutil.NopCloser(bytes.NewBuffer(data))
|
||||
|
||||
if data != nil {
|
||||
_ = json.Unmarshal(data, errorResponse)
|
||||
}
|
||||
|
||||
return errorResponse
|
||||
}
|
||||
|
||||
// addOption adds the given parameter as an URL query parameters to s.
|
||||
func addOption(s string, name, value string) (string, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return s, errors.Wrapf(err, "failed to parse %s", s)
|
||||
}
|
||||
|
||||
qa := u.Query()
|
||||
qa.Add(name, value)
|
||||
u.RawQuery = qa.Encode()
|
||||
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
// addOptions adds the parameters in opts as URL query parameters to s. opts
|
||||
// must be a struct whose fields may contain "url" tags.
|
||||
func addOptions(s string, opts interface{}) (string, error) {
|
||||
v := reflect.ValueOf(opts)
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return s, errors.Wrapf(err, "failed to parse %s", s)
|
||||
}
|
||||
|
||||
qs, err := query.Values(opts)
|
||||
if err != nil {
|
||||
return s, errors.Wrapf(err, "failed to opts %+v", opts)
|
||||
}
|
||||
|
||||
// Append to the existing query parameters.
|
||||
qa := u.Query()
|
||||
for key, values := range qs {
|
||||
for _, value := range values {
|
||||
qa.Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
u.RawQuery = qa.Encode()
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
// addPaginationOptions adds the given pagination parameters as URL query parameters to s.
|
||||
func addPaginationOptions(s string, page, perPage int) (string, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return s, errors.Wrapf(err, "failed to parse %s", s)
|
||||
}
|
||||
|
||||
qa := u.Query()
|
||||
qa.Add("page", strconv.Itoa(page))
|
||||
qa.Add("per_page", strconv.Itoa(perPage))
|
||||
u.RawQuery = qa.Encode()
|
||||
|
||||
return u.String(), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-plugin-playbooks/client"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// setup sets up a test HTTP server and matching Client.
|
||||
//
|
||||
// Tests should register handlers on mux providing mock responses for the API method being tested.
|
||||
func setup(t *testing.T) (c *client.Client, mux *http.ServeMux, serverURL string) {
|
||||
baseURLPath := ""
|
||||
|
||||
// mux is the HTTP request multiplexer used with the test server.
|
||||
mux = http.NewServeMux()
|
||||
|
||||
apiHandler := http.NewServeMux()
|
||||
apiHandler.Handle(baseURLPath+"/", http.StripPrefix(baseURLPath, mux))
|
||||
|
||||
// server is a test HTTP server used to provide mock API responses.
|
||||
server := httptest.NewServer(apiHandler)
|
||||
t.Cleanup(server.Close)
|
||||
serverURL = server.URL
|
||||
|
||||
// client is the workflows client being tested and is
|
||||
// configured to use test server.
|
||||
c, _ = client.NewClient("", &http.Client{})
|
||||
parsedURL, _ := url.Parse(server.URL + baseURLPath + "/")
|
||||
c.BaseURL = parsedURL
|
||||
|
||||
return c, mux, serverURL
|
||||
}
|
||||
|
||||
func testMethod(t *testing.T, r *http.Request, want string) {
|
||||
t.Helper()
|
||||
got := r.Method
|
||||
require.Equal(t, want, got, "request method: %v, want %v", got, want)
|
||||
}
|
||||
|
||||
type values map[string]string
|
||||
|
||||
func testFormValues(t *testing.T, r *http.Request, values values) {
|
||||
t.Helper()
|
||||
want := url.Values{}
|
||||
for k, v := range values {
|
||||
want.Set(k, v)
|
||||
}
|
||||
|
||||
require.NoError(t, r.ParseForm())
|
||||
got := r.Form
|
||||
require.Equal(t, want, got, "request parameters: %v, want %v", got, want)
|
||||
}
|
||||
5
core-plugins/mattermost-plugin-playbooks/client/doc.go
Normal file
5
core-plugins/mattermost-plugin-playbooks/client/doc.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// Package client provides an HTTP client for using the Playbooks API.
|
||||
package client
|
||||
37
core-plugins/mattermost-plugin-playbooks/client/doc_test.go
Normal file
37
core-plugins/mattermost-plugin-playbooks/client/doc_test.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
|
||||
"github.com/mattermost/mattermost-plugin-playbooks/client"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
ctx := context.Background()
|
||||
|
||||
client4 := model.NewAPIv4Client("http://localhost:8065")
|
||||
_, _, err := client4.Login(context.Background(), "test@example.com", "testtest")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
c, err := client.New(client4)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
playbookRunID := "h4n3h7s1qjf5pkis4dn6cuxgwa"
|
||||
playbookRun, err := c.PlaybookRuns.Get(ctx, playbookRunID)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Playbook Run Name: %s\n", playbookRun.Name)
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrorResponse is an error from an API request.
|
||||
type ErrorResponse struct {
|
||||
// Method is the HTTP verb used in the API request.
|
||||
Method string
|
||||
// URL is the HTTP endpoint used in the API request.
|
||||
URL string
|
||||
// StatusCode is the HTTP status code returned by the API.
|
||||
StatusCode int
|
||||
|
||||
// Err is the error parsed from the API response.
|
||||
Err error `json:"error"`
|
||||
}
|
||||
|
||||
func (e *ErrorResponse) UnmarshalJSON(data []byte) error {
|
||||
type Alias ErrorResponse
|
||||
temp := &struct {
|
||||
Err string `json:"error"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(e),
|
||||
}
|
||||
|
||||
// Try to extract a structured error from the body, otherwise fall back to using
|
||||
// the whole body as the error message.
|
||||
if err := json.Unmarshal(data, &temp); err != nil || temp.Err == "" {
|
||||
e.Err = errors.New(string(data))
|
||||
} else {
|
||||
e.Err = errors.New(temp.Err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unwrap exposes the underlying error of an ErrorResponse.
|
||||
func (e *ErrorResponse) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// Error describes the error from the API request.
|
||||
func (e *ErrorResponse) Error() string {
|
||||
return fmt.Sprintf("%s %s [%d]: %v", e.Method, e.URL, e.StatusCode, e.Err)
|
||||
}
|
||||
57
core-plugins/mattermost-plugin-playbooks/client/go.mod
Normal file
57
core-plugins/mattermost-plugin-playbooks/client/go.mod
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
module github.com/mattermost/mattermost-plugin-playbooks/client
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.9
|
||||
|
||||
require (
|
||||
github.com/google/go-querystring v1.1.0
|
||||
github.com/mattermost/mattermost/server/public v0.1.12
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/oauth2 v0.25.0
|
||||
gopkg.in/guregu/null.v4 v4.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.3 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
|
||||
github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 // indirect
|
||||
github.com/mattermost/logr/v2 v2.0.22 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/pborman/uuid v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/tinylib/msgp v1.2.5 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wiggin77/merror v1.0.5 // indirect
|
||||
github.com/wiggin77/srslog v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect
|
||||
google.golang.org/grpc v1.70.0 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
300
core-plugins/mattermost-plugin-playbooks/client/go.sum
Normal file
300
core-plugins/mattermost-plugin-playbooks/client/go.sum
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
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/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a h1:etIrTD8BQqzColk9nKRusM9um5+1q0iOEJLqfBMIK64=
|
||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a/go.mod h1:emQhSYTXqB0xxjLITTw4EaWZ+8IIQYw+kx9GqNUKdLg=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.5.2/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/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg=
|
||||
github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
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.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 h1:Khvh6waxG1cHc4Cz5ef9n3XVCxRWpAKUtqg9PJl5+y8=
|
||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404/go.mod h1:RyS7FDNQlzF1PsjbJWHRI35exqaKGSO9qD4iv8QjE34=
|
||||
github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 h1:Y1Tu/swM31pVwwb2BTCsOdamENjjWCI6qmfHLbk6OZI=
|
||||
github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956/go.mod h1:SRl30Lb7/QoYyohYeVBuqYvvmXSZJxZgiV3Zf6VbxjI=
|
||||
github.com/mattermost/logr/v2 v2.0.22 h1:npFkXlkAWR9J8payh8ftPcCZvLbHSI125mAM5/r/lP4=
|
||||
github.com/mattermost/logr/v2 v2.0.22/go.mod h1:0sUKpO+XNMZApeumaid7PYaUZPBIydfuWZ0dqixXo+s=
|
||||
github.com/mattermost/mattermost/server/public v0.1.12 h1:qlIU/llY0FWdHWQPtvncddQ99KJATPUX6wRHBlt8mfQ=
|
||||
github.com/mattermost/mattermost/server/public v0.1.12/go.mod h1:3RJZfl7sMedX6ihX+JMFOIAzCHhd0WQnuez+UFQS80k=
|
||||
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.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
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/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
|
||||
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
|
||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
|
||||
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/wiggin77/merror v1.0.5 h1:P+lzicsn4vPMycAf2mFf7Zk6G9eco5N+jB1qJ2XW3ME=
|
||||
github.com/wiggin77/merror v1.0.5/go.mod h1:H2ETSu7/bPE0Ymf4bEwdUoo73OOEkdClnoRisfw0Nm0=
|
||||
github.com/wiggin77/srslog v1.0.1 h1:gA2XjSMy3DrRdX9UqLuDtuVAAshb8bE1NhX1YK0Qe+8=
|
||||
github.com/wiggin77/srslog v1.0.1/go.mod h1:fehkyYDq1QfuYn60TDPu9YdY2bB85VUW2mvN1WynEls=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg=
|
||||
gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
197
core-plugins/mattermost-plugin-playbooks/client/playbook.go
Normal file
197
core-plugins/mattermost-plugin-playbooks/client/playbook.go
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/guregu/null.v4"
|
||||
)
|
||||
|
||||
// Playbook represents the planning before a playbook run is initiated.
|
||||
type Playbook struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Public bool `json:"public"`
|
||||
TeamID string `json:"team_id"`
|
||||
CreatePublicPlaybookRun bool `json:"create_public_playbook_run"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
NumStages int64 `json:"num_stages"`
|
||||
NumSteps int64 `json:"num_steps"`
|
||||
Checklists []Checklist `json:"checklists"`
|
||||
Members []PlaybookMember `json:"members"`
|
||||
ReminderMessageTemplate string `json:"reminder_message_template"`
|
||||
ReminderTimerDefaultSeconds int64 `json:"reminder_timer_default_seconds"`
|
||||
InvitedUserIDs []string `json:"invited_user_ids"`
|
||||
InvitedGroupIDs []string `json:"invited_group_ids"`
|
||||
InviteUsersEnabled bool `json:"invite_users_enabled"`
|
||||
DefaultOwnerID string `json:"default_owner_id"`
|
||||
DefaultOwnerEnabled bool `json:"default_owner_enabled"`
|
||||
BroadcastChannelIDs []string `json:"broadcast_channel_ids"`
|
||||
BroadcastEnabled bool `json:"broadcast_enabled"`
|
||||
WebhookOnCreationURLs []string `json:"webhook_on_creation_urls"`
|
||||
WebhookOnCreationEnabled bool `json:"webhook_on_creation_enabled"`
|
||||
Metrics []PlaybookMetricConfig `json:"metrics"`
|
||||
CreateChannelMemberOnNewParticipant bool `json:"create_channel_member_on_new_participant"`
|
||||
RemoveChannelMemberOnRemovedParticipant bool `json:"remove_channel_member_on_removed_participant"`
|
||||
ChannelID string `json:"channel_id" export:"channel_id"`
|
||||
ChannelMode ChannelPlaybookMode `json:"channel_mode" export:"channel_mode"`
|
||||
}
|
||||
|
||||
type PlaybookMember struct {
|
||||
UserID string `json:"user_id"`
|
||||
Roles []string `json:"roles"`
|
||||
SchemeRoles []string `json:"scheme_roles"`
|
||||
}
|
||||
|
||||
const (
|
||||
MetricTypeDuration = "metric_duration"
|
||||
MetricTypeCurrency = "metric_currency"
|
||||
MetricTypeInteger = "metric_integer"
|
||||
)
|
||||
|
||||
// Checklist represents a checklist in a playbook
|
||||
type Checklist struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Items []ChecklistItem `json:"items"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
}
|
||||
|
||||
// ChecklistItem represents an item in a checklist
|
||||
type ChecklistItem struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
State string `json:"state"`
|
||||
StateModified int64 `json:"state_modified"`
|
||||
AssigneeID string `json:"assignee_id"`
|
||||
AssigneeModified int64 `json:"assignee_modified"`
|
||||
Command string `json:"command"`
|
||||
CommandLastRun int64 `json:"command_last_run"`
|
||||
Description string `json:"description"`
|
||||
LastSkipped int64 `json:"delete_at"`
|
||||
DueDate int64 `json:"due_date"`
|
||||
TaskActions []TaskAction `json:"task_actions"`
|
||||
ConditionID string `json:"condition_id"`
|
||||
ConditionAction string `json:"condition_action"`
|
||||
ConditionReason string `json:"condition_reason"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
}
|
||||
|
||||
// TaskAction represents a task action in an item
|
||||
type TaskAction struct {
|
||||
Trigger TriggerAction `json:"trigger"`
|
||||
Actions []TriggerAction `json:"actions"`
|
||||
}
|
||||
|
||||
// TriggerAction represents a trigger or action in a Task Action
|
||||
type TriggerAction struct {
|
||||
Type string `json:"type"`
|
||||
Payload string `json:"payload"`
|
||||
}
|
||||
|
||||
// PlaybookCreateOptions specifies the parameters for PlaybooksService.Create method.
|
||||
type PlaybookCreateOptions struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
TeamID string `json:"team_id"`
|
||||
Public bool `json:"public"`
|
||||
CreatePublicPlaybookRun bool `json:"create_public_playbook_run"`
|
||||
Checklists []Checklist `json:"checklists"`
|
||||
Members []PlaybookMember `json:"members"`
|
||||
BroadcastChannelID string `json:"broadcast_channel_id"`
|
||||
ReminderMessageTemplate string `json:"reminder_message_template"`
|
||||
ReminderTimerDefaultSeconds int64 `json:"reminder_timer_default_seconds"`
|
||||
InvitedUserIDs []string `json:"invited_user_ids"`
|
||||
InvitedGroupIDs []string `json:"invited_group_ids"`
|
||||
InviteUsersEnabled bool `json:"invite_users_enabled"`
|
||||
DefaultOwnerID string `json:"default_owner_id"`
|
||||
DefaultOwnerEnabled bool `json:"default_owner_enabled"`
|
||||
BroadcastChannelIDs []string `json:"broadcast_channel_ids"`
|
||||
BroadcastEnabled bool `json:"broadcast_enabled"`
|
||||
Metrics []PlaybookMetricConfig `json:"metrics"`
|
||||
CreateChannelMemberOnNewParticipant bool `json:"create_channel_member_on_new_participant"`
|
||||
RemoveChannelMemberOnRemovedParticipant bool `json:"remove_channel_member_on_removed_participant"`
|
||||
ChannelID string `json:"channel_id" export:"channel_id"`
|
||||
ChannelMode ChannelPlaybookMode `json:"channel_mode" export:"channel_mode"`
|
||||
}
|
||||
|
||||
type PlaybookMetricConfig struct {
|
||||
ID string `json:"id"`
|
||||
PlaybookID string `json:"playbook_id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Type string `json:"type"`
|
||||
Target null.Int `json:"target"`
|
||||
}
|
||||
|
||||
// PlaybookListOptions specifies the optional parameters to the
|
||||
// PlaybooksService.List method.
|
||||
type PlaybookListOptions struct {
|
||||
Sort Sort `url:"sort,omitempty"`
|
||||
Direction SortDirection `url:"direction,omitempty"`
|
||||
SearchTeam string `url:"search_term,omitempty"`
|
||||
WithArchived bool `url:"with_archived,omitempty"`
|
||||
}
|
||||
|
||||
type GetPlaybooksResults struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
PageCount int `json:"page_count"`
|
||||
HasMore bool `json:"has_more"`
|
||||
Items []Playbook `json:"items"`
|
||||
}
|
||||
|
||||
type PlaybookStats struct {
|
||||
RunsInProgress int `json:"runs_in_progress"`
|
||||
ParticipantsActive int `json:"participants_active"`
|
||||
RunsFinishedPrev30Days int `json:"runs_finished_prev_30_days"`
|
||||
RunsFinishedPercentageChange int `json:"runs_finished_percentage_change"`
|
||||
RunsStartedPerWeek []int `json:"runs_started_per_week"`
|
||||
RunsStartedPerWeekTimes [][]int64 `json:"runs_started_per_week_times"`
|
||||
ActiveRunsPerDay []int `json:"active_runs_per_day"`
|
||||
ActiveRunsPerDayTimes [][]int64 `json:"active_runs_per_day_times"`
|
||||
ActiveParticipantsPerDay []int `json:"active_participants_per_day"`
|
||||
ActiveParticipantsPerDayTimes [][]int64 `json:"active_participants_per_day_times"`
|
||||
MetricOverallAverage []null.Int `json:"metric_overall_average"`
|
||||
MetricRollingAverage []null.Int `json:"metric_rolling_average"`
|
||||
MetricRollingAverageChange []null.Int `json:"metric_rolling_average_change"`
|
||||
MetricValueRange [][]int64 `json:"metric_value_range"`
|
||||
MetricRollingValues [][]int64 `json:"metric_rolling_values"`
|
||||
LastXRunNames []string `json:"last_x_run_names"`
|
||||
}
|
||||
|
||||
type ChannelPlaybookMode int
|
||||
|
||||
const (
|
||||
PlaybookRunCreateNewChannel ChannelPlaybookMode = iota
|
||||
PlaybookRunLinkExistingChannel
|
||||
)
|
||||
|
||||
var channelPlaybookTypes = [...]string{
|
||||
PlaybookRunCreateNewChannel: "create_new_channel",
|
||||
PlaybookRunLinkExistingChannel: "link_existing_channel",
|
||||
}
|
||||
|
||||
// String creates the string version of the ChannelPlaybookMode
|
||||
func (cpm ChannelPlaybookMode) String() string {
|
||||
return channelPlaybookTypes[cpm]
|
||||
}
|
||||
|
||||
// MarshalText converts a ChannelPlaybookMode to a string for serializers (including JSON)
|
||||
func (cpm ChannelPlaybookMode) MarshalText() ([]byte, error) {
|
||||
return []byte(channelPlaybookTypes[cpm]), nil
|
||||
}
|
||||
|
||||
// UnmarshalText parses a ChannelPlaybookMode from text. For deserializers (including JSON)
|
||||
func (cpm *ChannelPlaybookMode) UnmarshalText(text []byte) error {
|
||||
for i, st := range channelPlaybookTypes {
|
||||
if st == string(text) {
|
||||
*cpm = ChannelPlaybookMode(i)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("unknown ChannelPlaybookMode: %s", string(text))
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// PlaybookConditionsService handles communication with the playbook condition related
|
||||
// methods of the Playbooks API.
|
||||
type PlaybookConditionsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Condition represents a condition that can be applied to playbooks and runs.
|
||||
type Condition struct {
|
||||
ID string `json:"id"`
|
||||
ConditionExpr ConditionExprV1 `json:"condition_expr"`
|
||||
Version int `json:"version"`
|
||||
PlaybookID string `json:"playbook_id"`
|
||||
RunID string `json:"run_id,omitempty"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
}
|
||||
|
||||
// ConditionExprV1 represents a logical condition expression.
|
||||
type ConditionExprV1 struct {
|
||||
And []ConditionExprV1 `json:"and,omitempty"`
|
||||
Or []ConditionExprV1 `json:"or,omitempty"`
|
||||
|
||||
Is *ComparisonCondition `json:"is,omitempty"`
|
||||
IsNot *ComparisonCondition `json:"isNot,omitempty"`
|
||||
}
|
||||
|
||||
// ComparisonCondition represents a field comparison condition.
|
||||
type ComparisonCondition struct {
|
||||
FieldID string `json:"field_id"`
|
||||
Value json.RawMessage `json:"value"`
|
||||
}
|
||||
|
||||
// PlaybookConditionListOptions specifies the optional parameters to various
|
||||
// List methods that support pagination and filtering.
|
||||
type PlaybookConditionListOptions struct {
|
||||
Page int `url:"page,omitempty"`
|
||||
PerPage int `url:"per_page,omitempty"`
|
||||
}
|
||||
|
||||
// GetPlaybookConditionsResults contains the results of the List call.
|
||||
type GetPlaybookConditionsResults struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
PageCount int `json:"page_count"`
|
||||
HasMore bool `json:"has_more"`
|
||||
Items []Condition `json:"items"`
|
||||
}
|
||||
|
||||
// List the conditions for a playbook.
|
||||
func (s *PlaybookConditionsService) List(ctx context.Context, playbookID string, page, perPage int, opts PlaybookConditionListOptions) (*GetPlaybookConditionsResults, error) {
|
||||
conditionURL := fmt.Sprintf("playbooks/%s/conditions", playbookID)
|
||||
conditionURL, err := addPaginationOptions(conditionURL, page, perPage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build pagination options: %w", err)
|
||||
}
|
||||
|
||||
conditionURL, err = addOptions(conditionURL, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build options: %w", err)
|
||||
}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, conditionURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build request: %w", err)
|
||||
}
|
||||
|
||||
result := &GetPlaybookConditionsResults{}
|
||||
resp, err := s.client.do(ctx, req, result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute request: %w", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Create a playbook condition.
|
||||
func (s *PlaybookConditionsService) Create(ctx context.Context, playbookID string, condition Condition) (*Condition, error) {
|
||||
conditionURL := fmt.Sprintf("playbooks/%s/conditions", playbookID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, conditionURL, condition)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
createdCondition := new(Condition)
|
||||
resp, err := s.client.do(ctx, req, createdCondition)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
return nil, fmt.Errorf("expected status code %d", http.StatusCreated)
|
||||
}
|
||||
|
||||
return createdCondition, nil
|
||||
}
|
||||
|
||||
// Update a playbook condition.
|
||||
func (s *PlaybookConditionsService) Update(ctx context.Context, playbookID, conditionID string, condition Condition) (*Condition, error) {
|
||||
conditionURL := fmt.Sprintf("playbooks/%s/conditions/%s", playbookID, conditionID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, conditionURL, condition)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updatedCondition := new(Condition)
|
||||
resp, err := s.client.do(ctx, req, updatedCondition)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("expected status code %d", http.StatusOK)
|
||||
}
|
||||
|
||||
return updatedCondition, nil
|
||||
}
|
||||
|
||||
// Delete a playbook condition.
|
||||
func (s *PlaybookConditionsService) Delete(ctx context.Context, playbookID, conditionID string) error {
|
||||
conditionURL := fmt.Sprintf("playbooks/%s/conditions/%s", playbookID, conditionID)
|
||||
req, err := s.client.newAPIRequest(http.MethodDelete, conditionURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
return fmt.Errorf("expected status code %d", http.StatusNoContent)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
310
core-plugins/mattermost-plugin-playbooks/client/playbook_run.go
Normal file
310
core-plugins/mattermost-plugin-playbooks/client/playbook_run.go
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gopkg.in/guregu/null.v4"
|
||||
)
|
||||
|
||||
// Me is a constant that refers to the current user, and can be used in various APIs in place of
|
||||
// explicitly specifying the current user's id.
|
||||
const Me = "me"
|
||||
|
||||
// PlaybookRun represents a playbook run.
|
||||
type PlaybookRun struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Summary string `json:"summary"`
|
||||
SummaryModifiedAt int64 `json:"summary_modified_at"`
|
||||
OwnerUserID string `json:"owner_user_id"`
|
||||
ReporterUserID string `json:"reporter_user_id"`
|
||||
TeamID string `json:"team_id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
EndAt int64 `json:"end_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
ActiveStage int `json:"active_stage"`
|
||||
ActiveStageTitle string `json:"active_stage_title"`
|
||||
PostID string `json:"post_id"`
|
||||
PlaybookID string `json:"playbook_id"`
|
||||
Type string `json:"type"`
|
||||
Checklists []Checklist `json:"checklists"`
|
||||
StatusPosts []StatusPost `json:"status_posts"`
|
||||
CurrentStatus string `json:"current_status"`
|
||||
LastStatusUpdateAt int64 `json:"last_status_update_at"`
|
||||
ReminderPostID string `json:"reminder_post_id"`
|
||||
PreviousReminder time.Duration `json:"previous_reminder"`
|
||||
ReminderTimerDefaultSeconds int64 `json:"reminder_timer_default_seconds"`
|
||||
StatusUpdateEnabled bool `json:"status_update_enabled"`
|
||||
BroadcastChannelIDs []string `json:"broadcast_channel_ids"`
|
||||
WebhookOnStatusUpdateURLs []string `json:"webhook_on_status_update_urls"`
|
||||
StatusUpdateBroadcastChannelsEnabled bool `json:"status_update_broadcast_channels_enabled"`
|
||||
StatusUpdateBroadcastWebhooksEnabled bool `json:"status_update_broadcast_webhooks_enabled"`
|
||||
ReminderMessageTemplate string `json:"reminder_message_template"`
|
||||
InvitedUserIDs []string `json:"invited_user_ids"`
|
||||
InvitedGroupIDs []string `json:"invited_group_ids"`
|
||||
TimelineEvents []TimelineEvent `json:"timeline_events"`
|
||||
DefaultOwnerID string `json:"default_owner_id"`
|
||||
WebhookOnCreationURLs []string `json:"webhook_on_creation_urls"`
|
||||
Retrospective string `json:"retrospective"`
|
||||
RetrospectivePublishedAt int64 `json:"retrospective_published_at"`
|
||||
RetrospectiveWasCanceled bool `json:"retrospective_was_canceled"`
|
||||
RetrospectiveReminderIntervalSeconds int64 `json:"retrospective_reminder_interval_seconds"`
|
||||
RetrospectiveEnabled bool `json:"retrospective_enabled"`
|
||||
MessageOnJoin string `json:"message_on_join"`
|
||||
ParticipantIDs []string `json:"participant_ids"`
|
||||
CategoryName string `json:"category_name"`
|
||||
MetricsData []RunMetricData `json:"metrics_data"`
|
||||
CreateChannelMemberOnNewParticipant bool `json:"create_channel_member_on_new_participant"`
|
||||
RemoveChannelMemberOnRemovedParticipant bool `json:"remove_channel_member_on_removed_participant"`
|
||||
}
|
||||
|
||||
// StatusPost is information added to the playbook run when selecting from the db and sent to the
|
||||
// client; it is not saved to the db.
|
||||
type StatusPost struct {
|
||||
ID string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
}
|
||||
|
||||
// StatusPostComplete is the complete status update (post)
|
||||
// it's similar to StatusPost but with extended info.
|
||||
type StatusPostComplete struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
Message string `json:"message"`
|
||||
AuthorUserName string `json:"author_user_name"`
|
||||
}
|
||||
|
||||
// Metadata tracks ancillary metadata about a playbook run.
|
||||
type Metadata struct {
|
||||
ChannelName string `json:"channel_name"`
|
||||
ChannelDisplayName string `json:"channel_display_name"`
|
||||
TeamName string `json:"team_name"`
|
||||
NumParticipants int64 `json:"num_participants"`
|
||||
TotalPosts int64 `json:"total_posts"`
|
||||
Followers []string `json:"followers"`
|
||||
}
|
||||
|
||||
// TimelineEventType describes a type of timeline event.
|
||||
type TimelineEventType string
|
||||
|
||||
const (
|
||||
PlaybookRunCreated TimelineEventType = "incident_created"
|
||||
TaskStateModified TimelineEventType = "task_state_modified"
|
||||
StatusUpdated TimelineEventType = "status_updated"
|
||||
StatusUpdateRequested TimelineEventType = "status_update_requested"
|
||||
OwnerChanged TimelineEventType = "owner_changed"
|
||||
AssigneeChanged TimelineEventType = "assignee_changed"
|
||||
RanSlashCommand TimelineEventType = "ran_slash_command"
|
||||
EventFromPost TimelineEventType = "event_from_post"
|
||||
UserJoinedLeft TimelineEventType = "user_joined_left"
|
||||
PublishedRetrospective TimelineEventType = "published_retrospective"
|
||||
CanceledRetrospective TimelineEventType = "canceled_retrospective"
|
||||
RunFinished TimelineEventType = "run_finished"
|
||||
RunRestored TimelineEventType = "run_restored"
|
||||
StatusUpdatesEnabled TimelineEventType = "status_updates_enabled"
|
||||
StatusUpdatesDisabled TimelineEventType = "status_updates_disabled"
|
||||
)
|
||||
|
||||
// TimelineEvent represents an event recorded to a playbook run's timeline.
|
||||
type TimelineEvent struct {
|
||||
ID string `json:"id"`
|
||||
PlaybookRunID string `json:"playbook_run"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
EventAt int64 `json:"event_at"`
|
||||
EventType TimelineEventType `json:"event_type"`
|
||||
Summary string `json:"summary"`
|
||||
Details string `json:"details"`
|
||||
PostID string `json:"post_id"`
|
||||
SubjectUserID string `json:"subject_user_id"`
|
||||
CreatorUserID string `json:"creator_user_id"`
|
||||
}
|
||||
|
||||
// PlaybookRunCreateOptions specifies the parameters for PlaybookRunService.Create method.
|
||||
type PlaybookRunCreateOptions struct {
|
||||
Name string `json:"name"`
|
||||
OwnerUserID string `json:"owner_user_id"`
|
||||
TeamID string `json:"team_id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
Summary string `json:"summary"`
|
||||
PostID string `json:"post_id"`
|
||||
PlaybookID string `json:"playbook_id"`
|
||||
CreatePublicRun *bool `json:"create_public_run"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// RunAction represents the run action settings. Frontend passes this struct to update settings.
|
||||
type RunAction struct {
|
||||
BroadcastChannelIDs []string `json:"broadcast_channel_ids"`
|
||||
WebhookOnStatusUpdateURLs []string `json:"webhook_on_status_update_urls"`
|
||||
|
||||
StatusUpdateBroadcastChannelsEnabled bool `json:"status_update_broadcast_channels_enabled"`
|
||||
StatusUpdateBroadcastWebhooksEnabled bool `json:"status_update_broadcast_webhooks_enabled"`
|
||||
}
|
||||
|
||||
// RetrospectiveUpdate represents the run retrospective info
|
||||
type RetrospectiveUpdate struct {
|
||||
Text string `json:"retrospective"`
|
||||
Metrics []RunMetricData `json:"metrics"`
|
||||
}
|
||||
|
||||
// Sort enumerates the available fields we can sort on.
|
||||
type Sort string
|
||||
|
||||
const (
|
||||
// SortByCreateAt sorts by the "create_at" field. It is the default.
|
||||
SortByCreateAt Sort = "create_at"
|
||||
|
||||
// SortByID sorts by the "id" field.
|
||||
SortByID Sort = "id"
|
||||
|
||||
// SortByName sorts by the "name" field.
|
||||
SortByName Sort = "name"
|
||||
|
||||
// SortByOwnerUserID sorts by the "owner_user_id" field.
|
||||
SortByOwnerUserID Sort = "owner_user_id"
|
||||
|
||||
// SortByTeamID sorts by the "team_id" field.
|
||||
SortByTeamID Sort = "team_id"
|
||||
|
||||
// SortByEndAt sorts by the "end_at" field.
|
||||
SortByEndAt Sort = "end_at"
|
||||
|
||||
// SortBySteps sorts playbooks by the number of steps in the playbook.
|
||||
SortBySteps Sort = "steps"
|
||||
|
||||
// SortByStages sorts playbooks by the number of stages in the playbook.
|
||||
SortByStages Sort = "stages"
|
||||
|
||||
// SortByTitle sorts by the "title" field.
|
||||
SortByTitle Sort = "title"
|
||||
|
||||
// SortByRuns sorts by the number of times a playbook has been run.
|
||||
SortByRuns Sort = "runs"
|
||||
)
|
||||
|
||||
// SortDirection determines whether results are sorted ascending or descending.
|
||||
type SortDirection string
|
||||
|
||||
const (
|
||||
// Desc sorts the results in descending order.
|
||||
SortDesc SortDirection = "DESC"
|
||||
|
||||
// Asc sorts the results in ascending order.
|
||||
SortAsc SortDirection = "ASC"
|
||||
)
|
||||
|
||||
// PlaybookRunListOptions specifies the optional parameters to the
|
||||
// PlaybookRunService.List method.
|
||||
type PlaybookRunListOptions struct {
|
||||
// TeamID filters playbook runs to those in the given team.
|
||||
TeamID string `url:"team_id,omitempty"`
|
||||
|
||||
Sort Sort `url:"sort,omitempty"`
|
||||
Direction SortDirection `url:"direction,omitempty"`
|
||||
|
||||
// Statuses filters by InProgress or Ended; defaults to All when no status specified.
|
||||
Statuses []Status `url:"statuses,omitempty"`
|
||||
|
||||
// OwnerID filters by owner's Mattermost user ID. Defaults to blank (no filter). Specify "me" for current user.
|
||||
OwnerID string `url:"owner_user_id,omitempty"`
|
||||
|
||||
// ParticipantID filters playbook runs that have this user as a participant. Defaults to blank (no filter). Specify "me" for current user.
|
||||
ParticipantID string `url:"participant_id,omitempty"`
|
||||
|
||||
// ParticipantOrFollowerID filters playbook runs that have this user as member or as follower. Defaults to blank (no filter). Specify "me" for current user.
|
||||
ParticipantOrFollowerID string `url:"participant_or_follower,omitempty"`
|
||||
|
||||
// SearchTerm returns results of the search term and respecting the other header filter options.
|
||||
// The search term acts as a filter and respects the Sort and Direction fields (i.e., results are
|
||||
// not returned in relevance order).
|
||||
SearchTerm string `url:"search_term,omitempty"`
|
||||
|
||||
// PlaybookID filters playbook runs that are derived from this playbook id.
|
||||
// Defaults to blank (no filter).
|
||||
PlaybookID string `url:"playbook_id,omitempty"`
|
||||
|
||||
// ChannelID filters playbook runs associated with the given channel ID.
|
||||
// Defaults to blank (no filter).
|
||||
ChannelID string `url:"channel_id,omitempty"`
|
||||
|
||||
// ActiveGTE filters playbook runs that were active after (or equal) to the unix time given (in millis).
|
||||
// A value of 0 means the filter is ignored (which is the default).
|
||||
ActiveGTE int64 `url:"active_gte,omitempty"`
|
||||
|
||||
// ActiveLT filters playbook runs that were active before the unix time given (in millis).
|
||||
// A value of 0 means the filter is ignored (which is the default).
|
||||
ActiveLT int64 `url:"active_lt,omitempty"`
|
||||
|
||||
// StartedGTE filters playbook runs that were started after (or equal) to the unix time given (in millis).
|
||||
// A value of 0 means the filter is ignored (which is the default).
|
||||
StartedGTE int64 `url:"started_gte,omitempty"`
|
||||
|
||||
// StartedLT filters playbook runs that were started before the unix time given (in millis).
|
||||
// A value of 0 means the filter is ignored (which is the default).
|
||||
StartedLT int64 `url:"started_lt,omitempty"`
|
||||
|
||||
// ActivitySince, if not zero, returns playbook runs that have had any activity since this timestamp.
|
||||
// Activity includes creation, updates, or completion that occurred after this timestamp (in milliseconds).
|
||||
// A value of 0 (or negative, normalized to 0) means this filter is not applied.
|
||||
// This is sent as the "since" URL parameter.
|
||||
ActivitySince int64 `url:"since,omitempty"`
|
||||
}
|
||||
|
||||
// PlaybookRunList contains the paginated result.
|
||||
type PlaybookRunList struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
PageCount int `json:"page_count"`
|
||||
HasMore bool `json:"has_more"`
|
||||
Items []*PlaybookRun
|
||||
}
|
||||
|
||||
// Status is the type used to specify the activity status of the playbook run.
|
||||
type Status string
|
||||
|
||||
const (
|
||||
StatusInProgress Status = "InProgress"
|
||||
StatusFinished Status = "Finished"
|
||||
)
|
||||
|
||||
type GetPlaybookRunsResults struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
PageCount int `json:"page_count"`
|
||||
HasMore bool `json:"has_more"`
|
||||
Items []PlaybookRun `json:"items"`
|
||||
}
|
||||
|
||||
// StatusUpdateOptions are the fields required to update a playbook run's status
|
||||
type StatusUpdateOptions struct {
|
||||
Message string `json:"message"`
|
||||
Reminder time.Duration `json:"reminder"`
|
||||
FinishRun bool `json:"finish_run"`
|
||||
}
|
||||
|
||||
// PlaybookRunUpdateOptions are the fields that can be updated for a playbook run
|
||||
type PlaybookRunUpdateOptions struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Summary *string `json:"summary,omitempty"`
|
||||
}
|
||||
|
||||
type RunMetricData struct {
|
||||
MetricConfigID string `json:"metric_config_id"`
|
||||
Value null.Int `json:"value"`
|
||||
}
|
||||
|
||||
// OwnerInfo holds the summary information of a owner.
|
||||
type OwnerInfo struct {
|
||||
UserID string `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
468
core-plugins/mattermost-plugin-playbooks/client/playbook_runs.go
Normal file
468
core-plugins/mattermost-plugin-playbooks/client/playbook_runs.go
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PlaybookRunService handles communication with the playbook run related
|
||||
// methods of the Playbooks API.
|
||||
type PlaybookRunService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Get a playbook run.
|
||||
func (s *PlaybookRunService) Get(ctx context.Context, playbookRunID string) (*PlaybookRun, error) {
|
||||
playbookRunURL := fmt.Sprintf("runs/%s", playbookRunID)
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, playbookRunURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
playbookRun := new(PlaybookRun)
|
||||
resp, err := s.client.do(ctx, req, playbookRun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return playbookRun, nil
|
||||
}
|
||||
|
||||
// GetByChannelID gets a playbook run by ChannelID.
|
||||
func (s *PlaybookRunService) GetByChannelID(ctx context.Context, channelID string) (*PlaybookRun, error) {
|
||||
channelURL := fmt.Sprintf("runs/channel/%s", channelID)
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, channelURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
playbookRun := new(PlaybookRun)
|
||||
resp, err := s.client.do(ctx, req, playbookRun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return playbookRun, nil
|
||||
}
|
||||
|
||||
// Get a playbook run's metadata.
|
||||
func (s *PlaybookRunService) GetMetadata(ctx context.Context, playbookRunID string) (*Metadata, error) {
|
||||
playbookRunURL := fmt.Sprintf("runs/%s/metadata", playbookRunID)
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, playbookRunURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
playbookRun := new(Metadata)
|
||||
resp, err := s.client.do(ctx, req, playbookRun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return playbookRun, nil
|
||||
}
|
||||
|
||||
// Get all playbook status updates.
|
||||
func (s *PlaybookRunService) GetStatusUpdates(ctx context.Context, playbookRunID string) ([]StatusPostComplete, error) {
|
||||
playbookRunURL := fmt.Sprintf("runs/%s/status-updates", playbookRunID)
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, playbookRunURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var statusUpdates []StatusPostComplete
|
||||
resp, err := s.client.do(ctx, req, &statusUpdates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return statusUpdates, nil
|
||||
}
|
||||
|
||||
// List the playbook runs.
|
||||
func (s *PlaybookRunService) List(ctx context.Context, page, perPage int, opts PlaybookRunListOptions) (*GetPlaybookRunsResults, error) {
|
||||
playbookRunURL := "runs"
|
||||
playbookRunURL, err := addOptions(playbookRunURL, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build options: %w", err)
|
||||
}
|
||||
playbookRunURL, err = addPaginationOptions(playbookRunURL, page, perPage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build pagination options: %w", err)
|
||||
}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, playbookRunURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build request: %w", err)
|
||||
}
|
||||
|
||||
result := &GetPlaybookRunsResults{}
|
||||
resp, err := s.client.do(ctx, req, result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute request: %w", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Create a playbook run.
|
||||
func (s *PlaybookRunService) Create(ctx context.Context, opts PlaybookRunCreateOptions) (*PlaybookRun, error) {
|
||||
playbookRunURL := "runs"
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, playbookRunURL, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
playbookRun := new(PlaybookRun)
|
||||
resp, err := s.client.do(ctx, req, playbookRun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
return nil, fmt.Errorf("expected status code %d", http.StatusCreated)
|
||||
}
|
||||
|
||||
return playbookRun, nil
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) UpdateStatus(ctx context.Context, playbookRunID string, message string, reminderInSeconds int64) error {
|
||||
updateURL := fmt.Sprintf("runs/%s/status", playbookRunID)
|
||||
opts := StatusUpdateOptions{
|
||||
Message: message,
|
||||
Reminder: time.Duration(reminderInSeconds),
|
||||
}
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, updateURL, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("expected status code %d", http.StatusOK)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates a playbook run.
|
||||
func (s *PlaybookRunService) Update(ctx context.Context, playbookRunID string, updates PlaybookRunUpdateOptions) (*PlaybookRun, error) {
|
||||
updateURL := fmt.Sprintf("runs/%s", playbookRunID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPatch, updateURL, updates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
playbookRun := new(PlaybookRun)
|
||||
resp, err := s.client.do(ctx, req, playbookRun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return playbookRun, nil
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) RequestUpdate(ctx context.Context, playbookRunID, userID string) error {
|
||||
requestURL := fmt.Sprintf("runs/%s/request-update", playbookRunID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, requestURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.client.do(ctx, req, nil)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("expected status code %d", http.StatusOK)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) Finish(ctx context.Context, playbookRunID string) error {
|
||||
finishURL := fmt.Sprintf("runs/%s/finish", playbookRunID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, finishURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) CreateChecklist(ctx context.Context, playbookRunID string, checklist Checklist) error {
|
||||
createURL := fmt.Sprintf("runs/%s/checklists", playbookRunID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, createURL, checklist)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) RemoveChecklist(ctx context.Context, playbookRunID string, checklistNumber int) error {
|
||||
createURL := fmt.Sprintf("runs/%s/checklists/%d", playbookRunID, checklistNumber)
|
||||
req, err := s.client.newAPIRequest(http.MethodDelete, createURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) RenameChecklist(ctx context.Context, playbookRunID string, checklistNumber int, newTitle string) error {
|
||||
createURL := fmt.Sprintf("runs/%s/checklists/%d/rename", playbookRunID, checklistNumber)
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, createURL, struct{ Title string }{newTitle})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) AddChecklistItem(ctx context.Context, playbookRunID string, checklistNumber int, checklistItem ChecklistItem) error {
|
||||
addURL := fmt.Sprintf("runs/%s/checklists/%d/add", playbookRunID, checklistNumber)
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, addURL, checklistItem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) MoveChecklist(ctx context.Context, playbookRunID string, sourceChecklistIdx, destChecklistIdx int) error {
|
||||
createURL := fmt.Sprintf("runs/%s/checklists/move", playbookRunID)
|
||||
body := struct {
|
||||
SourceChecklistIdx int `json:"source_checklist_idx"`
|
||||
DestChecklistIdx int `json:"dest_checklist_idx"`
|
||||
}{sourceChecklistIdx, destChecklistIdx}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, createURL, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) MoveChecklistItem(ctx context.Context, playbookRunID string, sourceChecklistIdx, sourceItemIdx, destChecklistIdx, destItemIdx int) error {
|
||||
createURL := fmt.Sprintf("runs/%s/checklists/move-item", playbookRunID)
|
||||
body := struct {
|
||||
SourceChecklistIdx int `json:"source_checklist_idx"`
|
||||
SourceItemIdx int `json:"source_item_idx"`
|
||||
DestChecklistIdx int `json:"dest_checklist_idx"`
|
||||
DestItemIdx int `json:"dest_item_idx"`
|
||||
}{sourceChecklistIdx, sourceItemIdx, destChecklistIdx, destItemIdx}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, createURL, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateRetrospective updates the run's retrospective info
|
||||
func (s *PlaybookRunService) UpdateRetrospective(ctx context.Context, playbookRunID, userID string, retroUpdate RetrospectiveUpdate) error {
|
||||
createURL := fmt.Sprintf("runs/%s/retrospective", playbookRunID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, createURL, retroUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.client.do(ctx, req, nil)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("expected status code %d", http.StatusOK)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// PublishRetrospective publishes the run's retrospective
|
||||
func (s *PlaybookRunService) PublishRetrospective(ctx context.Context, playbookRunID, userID string, retroUpdate RetrospectiveUpdate) error {
|
||||
createURL := fmt.Sprintf("runs/%s/retrospective/publish", playbookRunID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, createURL, retroUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.client.do(ctx, req, nil)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("expected status code %d", http.StatusOK)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) SetItemAssignee(ctx context.Context, playbookRunID string, checklistIdx int, itemIdx int, assigneeID string) error {
|
||||
createURL := fmt.Sprintf("runs/%s/checklists/%d/item/%d/assignee", playbookRunID, checklistIdx, itemIdx)
|
||||
body := struct {
|
||||
AssigneeID string `json:"assignee_id"`
|
||||
}{assigneeID}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, createURL, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.client.do(ctx, req, nil)
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) SetItemCommand(ctx context.Context, playbookRunID string, checklistIdx int, itemIdx int, newCommand string) error {
|
||||
createURL := fmt.Sprintf("runs/%s/checklists/%d/item/%d/command", playbookRunID, checklistIdx, itemIdx)
|
||||
body := struct {
|
||||
Command string `json:"command"`
|
||||
}{newCommand}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, createURL, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) RunItemCommand(ctx context.Context, playbookRunID string, checklistIdx int, itemIdx int) error {
|
||||
createURL := fmt.Sprintf("runs/%s/checklists/%d/item/%d/run", playbookRunID, checklistIdx, itemIdx)
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, createURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PlaybookRunService) SetItemDueDate(ctx context.Context, playbookRunID string, checklistIdx int, itemIdx int, duedate int64) error {
|
||||
createURL := fmt.Sprintf("runs/%s/checklists/%d/item/%d/duedate", playbookRunID, checklistIdx, itemIdx)
|
||||
body := struct {
|
||||
DueDate int64 `json:"due_date"`
|
||||
}{duedate}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, createURL, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get a playbook run.
|
||||
func (s *PlaybookRunService) GetOwners(ctx context.Context) ([]OwnerInfo, error) {
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, "runs/owners", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
owners := make([]OwnerInfo, 0)
|
||||
resp, err := s.client.do(ctx, req, &owners)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return owners, nil
|
||||
}
|
||||
|
||||
// GetPropertyFields gets all property fields for a run. It is a wrapper around GetPropertyFieldsSince with updatedSince set to 0.
|
||||
func (s *PlaybookRunService) GetPropertyFields(ctx context.Context, playbookRunID string) ([]PropertyField, error) {
|
||||
return s.GetPropertyFieldsSince(ctx, playbookRunID, 0)
|
||||
}
|
||||
|
||||
// GetPropertyFieldsSince gets all property fields for a run since a given timestamp.
|
||||
// updatedSince: optional timestamp in milliseconds - only return fields updated after this time (0 = all)
|
||||
func (s *PlaybookRunService) GetPropertyFieldsSince(ctx context.Context, playbookRunID string, updatedSince int64) ([]PropertyField, error) {
|
||||
propertyFieldsURL := fmt.Sprintf("runs/%s/property_fields", playbookRunID)
|
||||
if updatedSince > 0 {
|
||||
propertyFieldsURL = fmt.Sprintf("%s?updated_since=%d", propertyFieldsURL, updatedSince)
|
||||
}
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, propertyFieldsURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fields []PropertyField
|
||||
resp, err := s.client.do(ctx, req, &fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// GetPropertyValues gets all property values for a run. It is a wrapper around GetPropertyValuesSince with updatedSince set to 0.
|
||||
func (s *PlaybookRunService) GetPropertyValues(ctx context.Context, playbookRunID string) ([]PropertyValue, error) {
|
||||
return s.GetPropertyValuesSince(ctx, playbookRunID, 0)
|
||||
}
|
||||
|
||||
// GetPropertyValuesSince gets all property values for a run since a given timestamp.
|
||||
// updatedSince: optional timestamp in milliseconds - only return values updated after this time (0 = all)
|
||||
func (s *PlaybookRunService) GetPropertyValuesSince(ctx context.Context, playbookRunID string, updatedSince int64) ([]PropertyValue, error) {
|
||||
propertyValuesURL := fmt.Sprintf("runs/%s/property_values", playbookRunID)
|
||||
if updatedSince > 0 {
|
||||
propertyValuesURL = fmt.Sprintf("%s?updated_since=%d", propertyValuesURL, updatedSince)
|
||||
}
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, propertyValuesURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var values []PropertyValue
|
||||
resp, err := s.client.do(ctx, req, &values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// SetPropertyValue sets a property value for a run.
|
||||
func (s *PlaybookRunService) SetPropertyValue(ctx context.Context, playbookRunID, fieldID string, value PropertyValueRequest) (*PropertyValue, error) {
|
||||
propertyValueURL := fmt.Sprintf("runs/%s/property_fields/%s/value", playbookRunID, fieldID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, propertyValueURL, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
propertyValue := new(PropertyValue)
|
||||
resp, err := s.client.do(ctx, req, propertyValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return propertyValue, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
|
||||
"github.com/mattermost/mattermost-plugin-playbooks/client"
|
||||
)
|
||||
|
||||
func ExamplePlaybookRunService_Get() {
|
||||
ctx := context.Background()
|
||||
|
||||
client4 := model.NewAPIv4Client("http://localhost:8065")
|
||||
client4.Login(context.Background(), "test@example.com", "testtest")
|
||||
|
||||
c, err := client.New(client4)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
playbookRunID := "h4n3h7s1qjf5pkis4dn6cuxgwa"
|
||||
playbookRun, err := c.PlaybookRuns.Get(ctx, playbookRunID)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Playbook Run Name: %s\n", playbookRun.Name)
|
||||
}
|
||||
|
||||
func ExamplePlaybookRunService_List() {
|
||||
ctx := context.Background()
|
||||
|
||||
client4 := model.NewAPIv4Client("http://localhost:8065")
|
||||
_, _, err := client4.Login(context.Background(), "test@example.com", "testtest")
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
teams, _, err := client4.GetAllTeams(context.Background(), "", 0, 1)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
if len(teams) == 0 {
|
||||
log.Fatal("no teams for this user")
|
||||
}
|
||||
|
||||
c, err := client.New(client4)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var playbookRuns []client.PlaybookRun
|
||||
for page := 0; ; page++ {
|
||||
result, err := c.PlaybookRuns.List(ctx, page, 100, client.PlaybookRunListOptions{
|
||||
TeamID: teams[0].Id,
|
||||
Sort: client.SortByCreateAt,
|
||||
Direction: client.SortDesc,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
playbookRuns = append(playbookRuns, result.Items...)
|
||||
if !result.HasMore {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, playbookRun := range playbookRuns {
|
||||
fmt.Printf("Playbook Run Name: %s\n", playbookRun.Name)
|
||||
}
|
||||
}
|
||||
349
core-plugins/mattermost-plugin-playbooks/client/playbooks.go
Normal file
349
core-plugins/mattermost-plugin-playbooks/client/playbooks.go
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// PlaybooksService handles communication with the playbook related
|
||||
// methods of the Playbook API.
|
||||
type PlaybooksService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Get a playbook.
|
||||
func (s *PlaybooksService) Get(ctx context.Context, playbookID string) (*Playbook, error) {
|
||||
playbookURL := fmt.Sprintf("playbooks/%s", playbookID)
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, playbookURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
playbook := new(Playbook)
|
||||
resp, err := s.client.do(ctx, req, playbook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return playbook, nil
|
||||
}
|
||||
|
||||
// List the playbooks.
|
||||
func (s *PlaybooksService) List(ctx context.Context, teamId string, page, perPage int, opts PlaybookListOptions) (*GetPlaybooksResults, error) {
|
||||
playbookURL := "playbooks"
|
||||
playbookURL, err := addOption(playbookURL, "team_id", teamId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build options: %w", err)
|
||||
}
|
||||
|
||||
playbookURL, err = addPaginationOptions(playbookURL, page, perPage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build pagination options: %w", err)
|
||||
}
|
||||
|
||||
playbookURL, err = addOptions(playbookURL, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build options: %w", err)
|
||||
}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, playbookURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build request: %w", err)
|
||||
}
|
||||
|
||||
result := &GetPlaybooksResults{}
|
||||
resp, err := s.client.do(ctx, req, result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute request: %w", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Create a playbook. Returns the id of the newly created playbook
|
||||
func (s *PlaybooksService) Create(ctx context.Context, opts PlaybookCreateOptions) (string, error) {
|
||||
// For ease of use set the default if not specificed so it doesn't just error
|
||||
if opts.ReminderTimerDefaultSeconds == 0 {
|
||||
opts.ReminderTimerDefaultSeconds = 86400
|
||||
}
|
||||
|
||||
playbookURL := "playbooks"
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, playbookURL, opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var result struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
resp, err := s.client.do(ctx, req, &result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
return "", fmt.Errorf("expected status code %d", http.StatusCreated)
|
||||
}
|
||||
|
||||
return result.ID, nil
|
||||
}
|
||||
|
||||
func (s *PlaybooksService) Update(ctx context.Context, playbook Playbook) error {
|
||||
updateURL := fmt.Sprintf("playbooks/%s", playbook.ID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, updateURL, playbook)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PlaybooksService) Archive(ctx context.Context, playbookID string) error {
|
||||
updateURL := fmt.Sprintf("playbooks/%s", playbookID)
|
||||
req, err := s.client.newAPIRequest(http.MethodDelete, updateURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PlaybooksService) Export(ctx context.Context, playbookID string) ([]byte, error) {
|
||||
url := fmt.Sprintf("playbooks/%s/export", playbookID)
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := s.client.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
result, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("expected status code %d", http.StatusOK)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Duplicate a playbook. Returns the id of the newly created playbook
|
||||
func (s *PlaybooksService) Duplicate(ctx context.Context, playbookID string) (string, error) {
|
||||
url := fmt.Sprintf("playbooks/%s/duplicate", playbookID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var result struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
resp, err := s.client.do(ctx, req, &result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
return "", fmt.Errorf("expected status code %d", http.StatusCreated)
|
||||
}
|
||||
|
||||
return result.ID, nil
|
||||
}
|
||||
|
||||
// Imports a playbook. Returns the id of the newly created playbook
|
||||
func (s *PlaybooksService) Import(ctx context.Context, toImport []byte, team string) (string, error) {
|
||||
url := "playbooks/import?team_id=" + team
|
||||
u, err := s.client.BaseURL.Parse(buildAPIURL(url))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "invalid endpoint %s", url)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPost, u.String(), bytes.NewReader(toImport))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to create http request for import")
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
var result struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
resp, err := s.client.do(ctx, req, &result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
return "", fmt.Errorf("expected status code %d", http.StatusCreated)
|
||||
}
|
||||
|
||||
return result.ID, nil
|
||||
}
|
||||
|
||||
func (s *PlaybooksService) Stats(ctx context.Context, playbookID string) (*PlaybookStats, error) {
|
||||
playbookStatsURL := fmt.Sprintf("stats/playbook?playbook_id=%s", playbookID)
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, playbookStatsURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stats := new(PlaybookStats)
|
||||
resp, err := s.client.do(ctx, req, stats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (s *PlaybooksService) AutoFollow(ctx context.Context, playbookID string, userID string) error {
|
||||
followsURL := fmt.Sprintf("playbooks/%s/autofollows/%s", playbookID, userID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, followsURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PlaybooksService) AutoUnfollow(ctx context.Context, playbookID string, userID string) error {
|
||||
followsURL := fmt.Sprintf("playbooks/%s/autofollows/%s", playbookID, userID)
|
||||
req, err := s.client.newAPIRequest(http.MethodDelete, followsURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PlaybooksService) GetAutoFollows(ctx context.Context, playbookID string) ([]string, error) {
|
||||
autofollowsURL := fmt.Sprintf("playbooks/%s/autofollows", playbookID)
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, autofollowsURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var followers []string
|
||||
resp, err := s.client.do(ctx, req, &followers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return followers, nil
|
||||
}
|
||||
|
||||
// GetPropertyFields gets all property fields for a playbook. It is a wrapper around GetPropertyFieldsSince with updatedSince set to 0.
|
||||
func (s *PlaybooksService) GetPropertyFields(ctx context.Context, playbookID string) ([]PropertyField, error) {
|
||||
return s.GetPropertyFieldsSince(ctx, playbookID, 0)
|
||||
}
|
||||
|
||||
// GetPropertyFieldsSince gets all property fields for a playbook.
|
||||
// updatedSince: optional timestamp in milliseconds - only return fields updated after this time (0 = all)
|
||||
func (s *PlaybooksService) GetPropertyFieldsSince(ctx context.Context, playbookID string, updatedSince int64) ([]PropertyField, error) {
|
||||
propertyFieldsURL := fmt.Sprintf("playbooks/%s/property_fields", playbookID)
|
||||
if updatedSince > 0 {
|
||||
propertyFieldsURL = fmt.Sprintf("%s?updated_since=%d", propertyFieldsURL, updatedSince)
|
||||
}
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, propertyFieldsURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fields []PropertyField
|
||||
resp, err := s.client.do(ctx, req, &fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// CreatePropertyField creates a new property field for a playbook.
|
||||
func (s *PlaybooksService) CreatePropertyField(ctx context.Context, playbookID string, field PropertyFieldRequest) (*PropertyField, error) {
|
||||
propertyFieldsURL := fmt.Sprintf("playbooks/%s/property_fields", playbookID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, propertyFieldsURL, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
propertyField := new(PropertyField)
|
||||
resp, err := s.client.do(ctx, req, propertyField)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return propertyField, nil
|
||||
}
|
||||
|
||||
// UpdatePropertyField updates an existing property field for a playbook.
|
||||
func (s *PlaybooksService) UpdatePropertyField(ctx context.Context, playbookID, fieldID string, field PropertyFieldRequest) (*PropertyField, error) {
|
||||
propertyFieldURL := fmt.Sprintf("playbooks/%s/property_fields/%s", playbookID, fieldID)
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, propertyFieldURL, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
propertyField := new(PropertyField)
|
||||
resp, err := s.client.do(ctx, req, propertyField)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return propertyField, nil
|
||||
}
|
||||
|
||||
// DeletePropertyField deletes a property field from a playbook.
|
||||
func (s *PlaybooksService) DeletePropertyField(ctx context.Context, playbookID, fieldID string) error {
|
||||
propertyFieldURL := fmt.Sprintf("playbooks/%s/property_fields/%s", playbookID, fieldID)
|
||||
req, err := s.client.newAPIRequest(http.MethodDelete, propertyFieldURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
|
||||
"github.com/mattermost/mattermost-plugin-playbooks/client"
|
||||
)
|
||||
|
||||
func ExamplePlaybooksService_Get() {
|
||||
ctx := context.Background()
|
||||
|
||||
client4 := model.NewAPIv4Client("http://localhost:8065")
|
||||
client4.Login(context.Background(), "test@example.com", "testtest")
|
||||
|
||||
c, err := client.New(client4)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
playbookID := "h4n3h7s1qjf5pkis4dn6cuxgwa"
|
||||
playbook, err := c.Playbooks.Get(ctx, playbookID)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Playbook Name: %s\n", playbook.Title)
|
||||
}
|
||||
|
||||
func ExamplePlaybooksService_List() {
|
||||
ctx := context.Background()
|
||||
|
||||
client4 := model.NewAPIv4Client("http://localhost:8065")
|
||||
_, _, err := client4.Login(context.Background(), "test@example.com", "testtest")
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
teams, _, err := client4.GetAllTeams(context.Background(), "", 0, 1)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
if len(teams) == 0 {
|
||||
log.Fatal("no teams for this user")
|
||||
}
|
||||
|
||||
c, err := client.New(client4)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var playbooks []client.Playbook
|
||||
for page := 0; ; page++ {
|
||||
result, err := c.Playbooks.List(ctx, teams[0].Id, page, 100, client.PlaybookListOptions{
|
||||
Sort: client.SortByCreateAt,
|
||||
Direction: client.SortDesc,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
playbooks = append(playbooks, result.Items...)
|
||||
if !result.HasMore {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, playbook := range playbooks {
|
||||
fmt.Printf("Playbook Name: %s\n", playbook.Title)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// PropertyField represents a property field definition
|
||||
type PropertyField struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
Attrs map[string]interface{} `json:"attrs"`
|
||||
}
|
||||
|
||||
// PropertyValue represents a property value
|
||||
type PropertyValue struct {
|
||||
ID string `json:"id"`
|
||||
FieldID string `json:"field_id"`
|
||||
Value json.RawMessage `json:"value"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
}
|
||||
|
||||
// PropertyFieldRequest represents a request to create or update a property field
|
||||
type PropertyFieldRequest struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Attrs *PropertyFieldAttrsInput `json:"attrs,omitempty"`
|
||||
}
|
||||
|
||||
// PropertyFieldAttrsInput represents property field attributes for input
|
||||
type PropertyFieldAttrsInput struct {
|
||||
Visibility *string `json:"visibility,omitempty"`
|
||||
SortOrder *float64 `json:"sortOrder,omitempty"`
|
||||
Options *[]PropertyOptionInput `json:"options,omitempty"`
|
||||
ParentID *string `json:"parentID,omitempty"`
|
||||
}
|
||||
|
||||
// PropertyOptionInput represents a property option for input
|
||||
type PropertyOptionInput struct {
|
||||
ID *string `json:"id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Color *string `json:"color,omitempty"`
|
||||
}
|
||||
|
||||
// PropertyValueRequest represents a request to set a property value
|
||||
type PropertyValueRequest struct {
|
||||
Value json.RawMessage `json:"value"`
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
type ReminderResetPayload struct {
|
||||
NewReminderSeconds int `json:"new_reminder_seconds"`
|
||||
}
|
||||
36
core-plugins/mattermost-plugin-playbooks/client/reminders.go
Normal file
36
core-plugins/mattermost-plugin-playbooks/client/reminders.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type RemindersService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
func (s *RemindersService) Reset(ctx context.Context, playbookRunID string, payload ReminderResetPayload) error {
|
||||
resetURL := fmt.Sprintf("runs/%s/reminder", playbookRunID)
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodPost, resetURL, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.client.do(ctx, req, ioutil.Discard)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
return fmt.Errorf("expected status code %d", http.StatusNoContent)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// RunConditionsService handles communication with the run condition related
|
||||
// methods of the Playbooks API. Run conditions are read-only.
|
||||
type RunConditionsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// RunConditionListOptions specifies the optional parameters to various
|
||||
// List methods that support pagination and filtering for run conditions.
|
||||
type RunConditionListOptions struct {
|
||||
Page int `url:"page,omitempty"`
|
||||
PerPage int `url:"per_page,omitempty"`
|
||||
}
|
||||
|
||||
// GetRunConditionsResults contains the results of the List call for run conditions.
|
||||
type GetRunConditionsResults struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
PageCount int `json:"page_count"`
|
||||
HasMore bool `json:"has_more"`
|
||||
Items []Condition `json:"items"`
|
||||
}
|
||||
|
||||
// List the conditions for a run (read-only).
|
||||
func (s *RunConditionsService) List(ctx context.Context, runID string, page, perPage int, opts RunConditionListOptions) (*GetRunConditionsResults, error) {
|
||||
conditionURL := fmt.Sprintf("runs/%s/conditions", runID)
|
||||
conditionURL, err := addPaginationOptions(conditionURL, page, perPage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build pagination options: %w", err)
|
||||
}
|
||||
|
||||
conditionURL, err = addOptions(conditionURL, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build options: %w", err)
|
||||
}
|
||||
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, conditionURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build request: %w", err)
|
||||
}
|
||||
|
||||
result := &GetRunConditionsResults{}
|
||||
resp, err := s.client.do(ctx, req, result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute request: %w", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
52
core-plugins/mattermost-plugin-playbooks/client/settings.go
Normal file
52
core-plugins/mattermost-plugin-playbooks/client/settings.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type GlobalSettings struct {
|
||||
EnableExperimentalFeatures bool `json:"enable_experimental_features"`
|
||||
}
|
||||
|
||||
// SettingsService handles communication with the settings related methods.
|
||||
type SettingsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Get the configured settings.
|
||||
func (s *SettingsService) Get(ctx context.Context) (*GlobalSettings, error) {
|
||||
settingsURL := "settings"
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, settingsURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
settings := new(GlobalSettings)
|
||||
resp, err := s.client.do(ctx, req, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
// Update the configured settings.
|
||||
func (s *SettingsService) Update(ctx context.Context, settings GlobalSettings) error {
|
||||
settingsURL := "settings"
|
||||
req, err := s.client.newAPIRequest(http.MethodPut, settingsURL, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.client.do(ctx, req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
199
core-plugins/mattermost-plugin-playbooks/client/since_test.go
Normal file
199
core-plugins/mattermost-plugin-playbooks/client/since_test.go
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// Test file for the ActivitySince parameter in PlaybookRunListOptions.
|
||||
// This test validates that the client properly encodes and sends the 'since'
|
||||
// parameter, and correctly handles different response scenarios.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestSinceParameter tests the ActivitySince parameter in PlaybookRunListOptions.
|
||||
// It covers four test cases:
|
||||
// 1. With a past timestamp - should return runs active after that time
|
||||
// 2. With a future timestamp - should return no runs
|
||||
// 3. Without a since parameter - should return default results
|
||||
// 4. URL encoding - verifies the parameter is properly encoded in the URL
|
||||
func TestSinceParameter(t *testing.T) {
|
||||
// Common response values
|
||||
const defaultTotalCount = 2
|
||||
const defaultPageCount = 1
|
||||
|
||||
// Setup server to simulate API responses
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Parse query parameters
|
||||
query := r.URL.Query()
|
||||
sinceStr := query.Get("since")
|
||||
|
||||
// Create response based on since parameter
|
||||
resp := GetPlaybookRunsResults{
|
||||
TotalCount: defaultTotalCount,
|
||||
PageCount: defaultPageCount,
|
||||
HasMore: false,
|
||||
Items: []PlaybookRun{},
|
||||
}
|
||||
|
||||
// Parse since parameter if present
|
||||
if sinceStr != "" {
|
||||
since, err := strconv.ParseInt(sinceStr, 10, 64)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid since parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Create timestamps for testing
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
// Add playbook runs only if the since parameter is in the past
|
||||
if since < now {
|
||||
// First run - created before since, updated after since
|
||||
run1 := PlaybookRun{
|
||||
ID: "run1",
|
||||
Name: "Run 1",
|
||||
CreateAt: since - 10000, // Created before since
|
||||
UpdateAt: since + 5000, // Updated after since
|
||||
}
|
||||
|
||||
// Second run - both created and updated after since
|
||||
run2 := PlaybookRun{
|
||||
ID: "run2",
|
||||
Name: "Run 2",
|
||||
CreateAt: since + 1000, // Created after since
|
||||
UpdateAt: since + 2000, // Updated after since
|
||||
}
|
||||
|
||||
resp.Items = append(resp.Items, run1, run2)
|
||||
}
|
||||
}
|
||||
|
||||
// Return JSON response
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
// Create test client
|
||||
parsed, _ := url.Parse(ts.URL)
|
||||
mockClient4 := model.NewAPIv4Client(parsed.String())
|
||||
mockClient4.HTTPClient = &http.Client{}
|
||||
|
||||
c, err := New(mockClient4)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test 1: Get runs with since parameter in the past
|
||||
t.Run("WithSinceParameter", func(t *testing.T) {
|
||||
// Set since time to 1 hour ago
|
||||
since := time.Now().Add(-1 * time.Hour).UnixMilli()
|
||||
|
||||
// Call API with since parameter
|
||||
ctx := context.Background()
|
||||
result, err := c.PlaybookRuns.List(ctx, 0, 100, PlaybookRunListOptions{
|
||||
ActivitySince: since,
|
||||
})
|
||||
|
||||
// Verify results
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, defaultTotalCount, result.TotalCount, "Should return expected total count")
|
||||
assert.Len(t, result.Items, 2, "Should return 2 runs")
|
||||
|
||||
// Verify first run fields - created before since but updated after
|
||||
assert.Equal(t, "run1", result.Items[0].ID)
|
||||
assert.Less(t, result.Items[0].CreateAt, since, "First run should be created before since")
|
||||
assert.Greater(t, result.Items[0].UpdateAt, since, "First run should be updated after since")
|
||||
|
||||
// Verify second run fields - both created and updated after since
|
||||
assert.Equal(t, "run2", result.Items[1].ID)
|
||||
assert.Greater(t, result.Items[1].CreateAt, since, "Second run should be created after since")
|
||||
assert.Greater(t, result.Items[1].UpdateAt, since, "Second run should be updated after since")
|
||||
})
|
||||
|
||||
// Test 2: Get runs with future since parameter (should return empty results)
|
||||
t.Run("WithFutureSinceParameter", func(t *testing.T) {
|
||||
// Set since time to 1 hour in the future
|
||||
since := time.Now().Add(1 * time.Hour).UnixMilli()
|
||||
|
||||
// Call API with since parameter
|
||||
ctx := context.Background()
|
||||
result, err := c.PlaybookRuns.List(ctx, 0, 100, PlaybookRunListOptions{
|
||||
ActivitySince: since,
|
||||
})
|
||||
|
||||
// Verify results
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, defaultTotalCount, result.TotalCount)
|
||||
assert.Len(t, result.Items, 0, "Should have no runs when using future timestamp")
|
||||
})
|
||||
|
||||
// Test 3: Get runs without since parameter
|
||||
t.Run("WithoutSinceParameter", func(t *testing.T) {
|
||||
// Call API without since parameter
|
||||
ctx := context.Background()
|
||||
result, err := c.PlaybookRuns.List(ctx, 0, 100, PlaybookRunListOptions{})
|
||||
|
||||
// Verify results
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, defaultTotalCount, result.TotalCount)
|
||||
assert.Len(t, result.Items, 0, "Should have no runs when since parameter is omitted")
|
||||
})
|
||||
|
||||
// Test 4: Verify URL encoding of since parameter
|
||||
t.Run("URLEncoding", func(t *testing.T) {
|
||||
// Create a specific server just for URL validation
|
||||
urlCheckServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Check that the since parameter is properly included in the URL
|
||||
query := r.URL.Query()
|
||||
sinceStr := query.Get("since")
|
||||
|
||||
// Validate parameter exists with expected value
|
||||
require.NotEmpty(t, sinceStr, "Since parameter should be present in URL")
|
||||
|
||||
since, err := strconv.ParseInt(sinceStr, 10, 64)
|
||||
require.NoError(t, err, "Since parameter should be a valid int64")
|
||||
assert.Equal(t, int64(12345), since, "Since parameter should match the value we sent")
|
||||
|
||||
// Return empty success response
|
||||
w.WriteHeader(http.StatusOK)
|
||||
empty := GetPlaybookRunsResults{
|
||||
TotalCount: 0,
|
||||
PageCount: 0,
|
||||
HasMore: false,
|
||||
Items: []PlaybookRun{},
|
||||
}
|
||||
json.NewEncoder(w).Encode(empty)
|
||||
}))
|
||||
defer urlCheckServer.Close()
|
||||
|
||||
// Create a client pointing to our URL check server
|
||||
parsedURL, _ := url.Parse(urlCheckServer.URL)
|
||||
urlCheckClient4 := model.NewAPIv4Client(parsedURL.String())
|
||||
urlCheckClient4.HTTPClient = &http.Client{}
|
||||
|
||||
urlCheckC, err := New(urlCheckClient4)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make request with specific test value
|
||||
ctx := context.Background()
|
||||
_, err = urlCheckC.PlaybookRuns.List(ctx, 0, 100, PlaybookRunListOptions{
|
||||
ActivitySince: 12345,
|
||||
})
|
||||
require.NoError(t, err, "Request with since parameter should succeed")
|
||||
})
|
||||
}
|
||||
38
core-plugins/mattermost-plugin-playbooks/client/stats.go
Normal file
38
core-plugins/mattermost-plugin-playbooks/client/stats.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// StatsService handles communication with the stats related methods.
|
||||
type StatsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// PlaybookSiteStats holds the data that we want to expose in system console
|
||||
type PlaybookSiteStats struct {
|
||||
TotalPlaybooks int `json:"total_playbooks"`
|
||||
TotalPlaybookRuns int `json:"total_playbook_runs"`
|
||||
}
|
||||
|
||||
// Get the stats that should be displayed in system console.
|
||||
func (s *StatsService) GetSiteStats(ctx context.Context) (*PlaybookSiteStats, error) {
|
||||
statsURL := "stats/site"
|
||||
req, err := s.client.newAPIRequest(http.MethodGet, statsURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stats := new(PlaybookSiteStats)
|
||||
resp, err := s.client.do(ctx, req, stats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
68
core-plugins/mattermost-plugin-playbooks/client/tabapp.go
Normal file
68
core-plugins/mattermost-plugin-playbooks/client/tabapp.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// LimitedUser returns the minimum amount of user data needed for the app.
|
||||
type LimitedUser struct {
|
||||
UserID string `json:"user_id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
}
|
||||
|
||||
// LimitedUser returns the minimum amount of post data needed for the app.
|
||||
type LimitedPost struct {
|
||||
Message string `json:"message"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UserID string `json:"user_id"`
|
||||
}
|
||||
|
||||
type TabAppResults struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
PageCount int `json:"page_count"`
|
||||
PerPage int `json:"per_page"`
|
||||
HasMore bool `json:"has_more"`
|
||||
Items []PlaybookRun `json:"items"`
|
||||
Users map[string]LimitedUser `json:"users"`
|
||||
Posts map[string]LimitedPost `json:"posts"`
|
||||
}
|
||||
|
||||
type TabAppService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
type TabAppGetRunsOptions struct {
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"per_page"`
|
||||
}
|
||||
|
||||
func (s *TabAppService) GetRuns(ctx context.Context, token string, options TabAppGetRunsOptions) (*TabAppResults, error) {
|
||||
url := fmt.Sprintf("plugins/%s/tabapp/runs", manifestID)
|
||||
|
||||
url, err := addOptions(url, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := s.client.newRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", token)
|
||||
|
||||
tabAppResults := new(TabAppResults)
|
||||
resp, err := s.client.do(ctx, req, tabAppResults)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return tabAppResults, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
var BuildAPIURL = buildAPIURL
|
||||
var NewClient = newClient
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) 2020-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPlaybookRunUpdateAtSerialization(t *testing.T) {
|
||||
// Create a PlaybookRun with UpdateAt field set
|
||||
now := time.Now().UnixMilli()
|
||||
run := PlaybookRun{
|
||||
ID: "test-id",
|
||||
Name: "Test Run",
|
||||
CreateAt: now - 1000, // 1 second earlier
|
||||
UpdateAt: now,
|
||||
}
|
||||
|
||||
// Serialize to JSON
|
||||
jsonData, err := json.Marshal(run)
|
||||
require.NoError(t, err, "Failed to marshal PlaybookRun to JSON")
|
||||
|
||||
// Validate that UpdateAt field is included in the JSON
|
||||
jsonStr := string(jsonData)
|
||||
assert.Contains(t, jsonStr, `"update_at":`, "JSON should contain update_at field")
|
||||
assert.Contains(t, jsonStr, `"create_at":`, "JSON should contain create_at field")
|
||||
|
||||
// Deserialize from JSON
|
||||
var decodedRun PlaybookRun
|
||||
err = json.Unmarshal(jsonData, &decodedRun)
|
||||
require.NoError(t, err, "Failed to unmarshal PlaybookRun from JSON")
|
||||
|
||||
// Validate the UpdateAt field was preserved
|
||||
assert.Equal(t, run.UpdateAt, decodedRun.UpdateAt, "UpdateAt field should be preserved after serialization/deserialization")
|
||||
assert.Equal(t, now, decodedRun.UpdateAt, "UpdateAt value should be preserved")
|
||||
}
|
||||
|
||||
func TestChecklistUpdateAtSerialization(t *testing.T) {
|
||||
// Create a Checklist with UpdateAt field set
|
||||
now := time.Now().UnixMilli()
|
||||
checklist := Checklist{
|
||||
ID: "test-checklist-id",
|
||||
Title: "Test Checklist",
|
||||
UpdateAt: now,
|
||||
Items: []ChecklistItem{},
|
||||
}
|
||||
|
||||
// Serialize to JSON
|
||||
jsonData, err := json.Marshal(checklist)
|
||||
require.NoError(t, err, "Failed to marshal Checklist to JSON")
|
||||
|
||||
// Validate that UpdateAt field is included in the JSON
|
||||
jsonStr := string(jsonData)
|
||||
assert.Contains(t, jsonStr, `"update_at":`, "JSON should contain update_at field")
|
||||
|
||||
// Deserialize from JSON
|
||||
var decodedChecklist Checklist
|
||||
err = json.Unmarshal(jsonData, &decodedChecklist)
|
||||
require.NoError(t, err, "Failed to unmarshal Checklist from JSON")
|
||||
|
||||
// Validate the UpdateAt field was preserved
|
||||
assert.Equal(t, checklist.UpdateAt, decodedChecklist.UpdateAt, "UpdateAt field should be preserved after serialization/deserialization")
|
||||
assert.Equal(t, now, decodedChecklist.UpdateAt, "UpdateAt value should be preserved")
|
||||
}
|
||||
|
||||
func TestChecklistItemUpdateAtSerialization(t *testing.T) {
|
||||
// Create a ChecklistItem with UpdateAt field set
|
||||
now := time.Now().UnixMilli()
|
||||
item := ChecklistItem{
|
||||
ID: "test-item-id",
|
||||
Title: "Test Item",
|
||||
UpdateAt: now,
|
||||
}
|
||||
|
||||
// Serialize to JSON
|
||||
jsonData, err := json.Marshal(item)
|
||||
require.NoError(t, err, "Failed to marshal ChecklistItem to JSON")
|
||||
|
||||
// Validate that UpdateAt field is included in the JSON
|
||||
jsonStr := string(jsonData)
|
||||
assert.Contains(t, jsonStr, `"update_at":`, "JSON should contain update_at field")
|
||||
|
||||
// Deserialize from JSON
|
||||
var decodedItem ChecklistItem
|
||||
err = json.Unmarshal(jsonData, &decodedItem)
|
||||
require.NoError(t, err, "Failed to unmarshal ChecklistItem from JSON")
|
||||
|
||||
// Validate the UpdateAt field was preserved
|
||||
assert.Equal(t, item.UpdateAt, decodedItem.UpdateAt, "UpdateAt field should be preserved after serialization/deserialization")
|
||||
assert.Equal(t, now, decodedItem.UpdateAt, "UpdateAt value should be preserved")
|
||||
}
|
||||
|
||||
func TestNestedUpdateAtSerialization(t *testing.T) {
|
||||
// Create a nested structure to test the complete serialization path
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
// Create a checklist item with UpdateAt
|
||||
item := ChecklistItem{
|
||||
ID: "test-item-id",
|
||||
Title: "Test Item",
|
||||
UpdateAt: now,
|
||||
}
|
||||
|
||||
// Create a checklist with the item
|
||||
checklist := Checklist{
|
||||
ID: "test-checklist-id",
|
||||
Title: "Test Checklist",
|
||||
UpdateAt: now + 1000, // 1 second later
|
||||
Items: []ChecklistItem{item},
|
||||
}
|
||||
|
||||
// Create a PlaybookRun with the checklist
|
||||
run := PlaybookRun{
|
||||
ID: "test-run-id",
|
||||
Name: "Test Run",
|
||||
CreateAt: now - 1000, // 1 second earlier
|
||||
UpdateAt: now + 2000, // 2 seconds later
|
||||
Checklists: []Checklist{checklist},
|
||||
}
|
||||
|
||||
// Serialize to JSON
|
||||
jsonData, err := json.Marshal(run)
|
||||
require.NoError(t, err, "Failed to marshal nested structure to JSON")
|
||||
|
||||
// Deserialize from JSON
|
||||
var decodedRun PlaybookRun
|
||||
err = json.Unmarshal(jsonData, &decodedRun)
|
||||
require.NoError(t, err, "Failed to unmarshal nested structure from JSON")
|
||||
|
||||
// Validate the UpdateAt fields were preserved at all levels
|
||||
assert.Equal(t, run.UpdateAt, decodedRun.UpdateAt, "Run UpdateAt should be preserved")
|
||||
require.Len(t, decodedRun.Checklists, 1, "Should have one checklist")
|
||||
assert.Equal(t, checklist.UpdateAt, decodedRun.Checklists[0].UpdateAt, "Checklist UpdateAt should be preserved")
|
||||
require.Len(t, decodedRun.Checklists[0].Items, 1, "Should have one checklist item")
|
||||
assert.Equal(t, item.UpdateAt, decodedRun.Checklists[0].Items[0].UpdateAt, "ChecklistItem UpdateAt should be preserved")
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# Use /e2e-test command to execute cypress integration tests
|
||||
|
||||
Date: **2023-01-17**
|
||||
Status: **Under Discussion**
|
||||
|
||||
Contents:
|
||||
|
||||
- [Context](#context)
|
||||
- [Decision](#decision)
|
||||
- [Implementation](#implementation)
|
||||
- [Notes](#notes)
|
||||
|
||||
## Context
|
||||
|
||||
Following our initiative to deprecate CirleCI in favor of Github Actions we are also trying to reduce costs in the process.
|
||||
Analyzing our pipelines we figured out that integration tests take too much time and are resource hungry. We want to introduce a new process to mitigate the cost and provide a more user friendly developer experience.
|
||||
|
||||
|
||||
## Decision
|
||||
|
||||
We want to introduce a custom PR command `/e2e-test` in order to manually trigger integration tests. The idea is similar to mattermod command introduced [here](https://github.com/mattermost/mattermost-mattermod/pull/327). The proposal includes limiting the e2e testing only on approved PRs to avoid community increased load .
|
||||
|
||||

|
||||
|
||||
## Implementation
|
||||
|
||||
In order to avoid unexessary coding we can use Github Actions built in functionality to take action based on PR comments . No need to increase complexity by adding functionality on our bots . A simple example will be as below :
|
||||
|
||||
```yaml
|
||||
name: Trigger on PR Comment
|
||||
on:
|
||||
issue_comment:
|
||||
types:
|
||||
- created
|
||||
jobs:
|
||||
e2e-test:
|
||||
# Check if the comments originate from pull request (because Issues and PRs have the same API)
|
||||
# Check that comment starts with /e2e-test
|
||||
if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/e2e-test') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: ci/integration-tests
|
||||
run: echo "Let's execute some integration tests"
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Further parameterization can be introduced with flags on the comment body
|
||||
- Developers can change the functionality easily on the same repo without extra code
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Architecture Decision Records
|
||||
|
||||
- [Use e2e-test command to trigger integration tests](0001-use-command-for-e2e-tests.md)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
|
|
@ -0,0 +1,123 @@
|
|||
{
|
||||
"extends": [
|
||||
"plugin:mattermost/react",
|
||||
"plugin:cypress/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/eslint-plugin",
|
||||
"mattermost",
|
||||
"import",
|
||||
"no-only-tests",
|
||||
"@typescript-eslint",
|
||||
"cypress"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"cypress/globals": true
|
||||
},
|
||||
"rules": {
|
||||
"header/header": [
|
||||
2,
|
||||
"line",
|
||||
" Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n See LICENSE.txt for license information.",
|
||||
2
|
||||
],
|
||||
"cypress/assertion-before-screenshot": "warn",
|
||||
"cypress/no-assigning-return-values": "error",
|
||||
"cypress/no-force": "warn",
|
||||
"cypress/no-async-tests": "error",
|
||||
"cypress/no-pause": "error",
|
||||
"cypress/no-unnecessary-waiting": 0,
|
||||
"cypress/unsafe-to-chain-command": 0,
|
||||
"func-names": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"max-nested-callbacks": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"no-process-env": 0,
|
||||
"no-duplicate-imports": 0,
|
||||
"no-undefined": 0,
|
||||
"no-use-before-define": 0,
|
||||
"import/no-duplicates": 2,
|
||||
"mattermost/use-external-link": 2,
|
||||
"eol-last": ["error", "always"],
|
||||
"import/order": [
|
||||
0,
|
||||
{
|
||||
"newlines-between": "always-and-inside-groups",
|
||||
"groups": [
|
||||
"builtin",
|
||||
"external",
|
||||
[
|
||||
"internal",
|
||||
"parent"
|
||||
],
|
||||
"sibling",
|
||||
"index"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-only-tests/no-only-tests": ["error", {"focus": ["only", "skip"]}],
|
||||
"max-lines": ["warn", {"max": 800, "skipBlankLines": true, "skipComments": true}]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.ts"],
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"camelcase": 0,
|
||||
"no-shadow": 0,
|
||||
"import/no-unresolved": 0, // ts handles this better
|
||||
"@typescript-eslint/naming-convention": [
|
||||
2,
|
||||
{
|
||||
"selector": "function",
|
||||
"format": ["camelCase", "PascalCase"]
|
||||
},
|
||||
{
|
||||
"selector": "variable",
|
||||
"format": ["camelCase", "PascalCase", "UPPER_CASE"]
|
||||
},
|
||||
{
|
||||
"selector": "parameter",
|
||||
"format": ["camelCase", "PascalCase"],
|
||||
"leadingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": "typeLike",
|
||||
"format": ["PascalCase"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
2,
|
||||
{
|
||||
"vars": "all",
|
||||
"args": "after-used"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-var-requires": 0,
|
||||
"@typescript-eslint/no-empty-function": 0,
|
||||
"@typescript-eslint/prefer-interface": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"@typescript-eslint/indent": [
|
||||
2,
|
||||
4,
|
||||
{
|
||||
"SwitchCase": 0
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-use-before-define": [
|
||||
2,
|
||||
{
|
||||
"classes": false,
|
||||
"functions": false,
|
||||
"variables": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
16
core-plugins/mattermost-plugin-playbooks/e2e-tests/.gitignore
vendored
Normal file
16
core-plugins/mattermost-plugin-playbooks/e2e-tests/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# env, cert, key, license
|
||||
.env*
|
||||
*.crt
|
||||
*.key
|
||||
*.license
|
||||
|
||||
# Plugin
|
||||
*.tar.gz
|
||||
|
||||
# node
|
||||
*.lock
|
||||
|
||||
node_modules
|
||||
results
|
||||
tests/screenshots
|
||||
tests/videos
|
||||
|
|
@ -0,0 +1 @@
|
|||
legacy-peer-deps=true
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {defineConfig} from 'cypress';
|
||||
|
||||
export default defineConfig({
|
||||
chromeWebSecurity: false,
|
||||
defaultCommandTimeout: 20000,
|
||||
downloadsFolder: 'tests/downloads',
|
||||
fixturesFolder: 'tests/fixtures',
|
||||
numTestsKeptInMemory: 0,
|
||||
screenshotsFolder: 'tests/screenshots',
|
||||
taskTimeout: 20000,
|
||||
video: false,
|
||||
viewportWidth: 1300,
|
||||
reporter: 'cypress-multi-reporters',
|
||||
reporterOptions: {
|
||||
reporterEnabled: 'spec, mocha-junit-reporter',
|
||||
mochaJunitReporterReporterOptions: {
|
||||
mochaFile: 'results/junit/test_results[hash].xml',
|
||||
},
|
||||
},
|
||||
env: {
|
||||
adminEmail: 'sysadmin@sample.mattermost.com',
|
||||
adminUsername: 'sysadmin',
|
||||
adminPassword: 'Sys@dmin-sample1',
|
||||
allowedUntrustedInternalConnections: 'localhost',
|
||||
cwsURL: 'http://localhost:8076',
|
||||
cwsAPIURL: 'http://localhost:8076',
|
||||
dbClient: 'postgres',
|
||||
dbConnection: 'postgres://mmuser:mostest@localhost/mattermost_test?sslmode=disable&connect_timeout=10',
|
||||
elasticsearchConnectionURL: 'http://localhost:9200',
|
||||
firstTest: false,
|
||||
keycloakAppName: 'mattermost',
|
||||
keycloakBaseUrl: 'http://localhost:8484',
|
||||
keycloakUsername: 'mmuser',
|
||||
keycloakPassword: 'mostest',
|
||||
ldapServer: 'localhost',
|
||||
ldapPort: 389,
|
||||
minioAccessKey: 'minioaccesskey',
|
||||
minioSecretKey: 'miniosecretkey',
|
||||
minioS3Bucket: 'mattermost-test',
|
||||
minioS3Endpoint: 'localhost:9000',
|
||||
minioS3SSL: false,
|
||||
numberOfTrialUsers: 100,
|
||||
resetBeforeTest: false,
|
||||
runLDAPSync: true,
|
||||
secondServerURL: 'http://localhost/s/p',
|
||||
serverEdition: 'Team',
|
||||
serverClusterEnabled: false,
|
||||
serverClusterName: 'mm_dev_cluster',
|
||||
serverClusterHostCount: 3,
|
||||
smtpUrl: 'http://localhost:9001',
|
||||
webhookBaseUrl: 'http://localhost:3000',
|
||||
},
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
return require('./tests/plugins/index.js')(on, config); // eslint-disable-line global-require
|
||||
},
|
||||
baseUrl: process.env.MM_SERVICESETTINGS_SITEURL || 'http://localhost:8065',
|
||||
excludeSpecPattern: '**/node_modules/**/*',
|
||||
specPattern: 'tests/integration/**/*_spec.{js,ts}',
|
||||
supportFile: 'tests/support/index.js',
|
||||
testIsolation: false,
|
||||
},
|
||||
retries: {
|
||||
openMode: 0,
|
||||
runMode: 2,
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
--
|
||||
-- Just add the system admin.
|
||||
--
|
||||
|
||||
COPY public.users (id, createat, updateat, deleteat, username, password, authdata, authservice, email, emailverified, nickname, firstname, lastname, "position", roles, allowmarketing, props, notifyprops, lastpasswordupdate, lastpictureupdate, failedattempts, locale, timezone, mfaactive, mfasecret, remoteid) FROM stdin;
|
||||
qanmxu8aafgdipxiibkuos1uaw 1634316565952 1634316566065 0 sysadmin $2a$10$FF7zZzLYW80liKKJaKGVFOc6xCsKU2OZfwCvGBmF4xACuwstyPFN. \N sysadmin@sample.mattermost.com t Kenneth Moreno Software Test Engineer III system_admin system_user f {} {"push": "mention", "email": "true", "channel": "true", "desktop": "mention", "comments": "never", "first_name": "false", "push_status": "away", "mention_keys": "", "push_threads": "all", "desktop_sound": "true", "email_threads": "all", "desktop_threads": "all"} 1634316565952 0 0 en {"manualTimezone": "", "automaticTimezone": "", "useAutomaticTimezone": "true"} f \N
|
||||
\.
|
||||
13800
core-plugins/mattermost-plugin-playbooks/e2e-tests/package-lock.json
generated
Normal file
13800
core-plugins/mattermost-plugin-playbooks/e2e-tests/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,98 @@
|
|||
{
|
||||
"engines": {
|
||||
"node": "20.x || 22.x || >=24.0.0",
|
||||
"npm": ">=10.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "7.21.8",
|
||||
"@babel/eslint-plugin": "7.19.1",
|
||||
"@cypress/request": "2.88.11",
|
||||
"@mattermost/client": "10.6.0",
|
||||
"@mattermost/types": "10.6.0",
|
||||
"@testing-library/cypress": "10.1.0",
|
||||
"@types/async": "3.2.20",
|
||||
"@types/authenticator": "1.1.1",
|
||||
"@types/express": "4.17.17",
|
||||
"@types/fs-extra": "11.0.1",
|
||||
"@types/lodash": "4.14.194",
|
||||
"@types/lodash.intersection": "4.4.7",
|
||||
"@types/lodash.mapkeys": "4.6.7",
|
||||
"@types/lodash.without": "4.4.7",
|
||||
"@types/mime-types": "2.1.1",
|
||||
"@types/mochawesome": "6.2.1",
|
||||
"@types/pdf-parse": "1.1.1",
|
||||
"@types/recursive-readdir": "2.2.1",
|
||||
"@types/shelljs": "0.8.12",
|
||||
"@types/uuid": "9.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.59.2",
|
||||
"@typescript-eslint/parser": "5.59.2",
|
||||
"async": "3.2.4",
|
||||
"authenticator": "1.1.5",
|
||||
"aws-sdk": "2.1371.0",
|
||||
"axios": "1.4.0",
|
||||
"axios-retry": "3.4.0",
|
||||
"chai": "4.3.7",
|
||||
"chalk": "4.1.2",
|
||||
"client-oauth2": "github:larkox/js-client-oauth2#e24e2eb5dfcbbbb3a59d095e831dbe0012b0ac49",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "15.12.0",
|
||||
"cypress-file-upload": "5.0.8",
|
||||
"cypress-multi-reporters": "1.6.3",
|
||||
"cypress-plugin-tab": "1.0.5",
|
||||
"cypress-real-events": "^1.15.0",
|
||||
"cypress-wait-until": "1.7.2",
|
||||
"dayjs": "1.11.7",
|
||||
"deepmerge": "4.3.1",
|
||||
"dotenv": "16.0.3",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-import-resolver-webpack": "0.13.2",
|
||||
"eslint-plugin-cypress": "2.13.3",
|
||||
"eslint-plugin-header": "3.1.1",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-mattermost": "github:mattermost/eslint-plugin-mattermost#5b0c972eacf19286e4c66221b39113bf8728a99e",
|
||||
"eslint-plugin-no-only-tests": "3.1.0",
|
||||
"eslint-plugin-react": "7.32.2",
|
||||
"express": "4.18.2",
|
||||
"extract-zip": "2.0.1",
|
||||
"knex": "2.4.2",
|
||||
"localforage": "1.10.0",
|
||||
"lodash.intersection": "4.4.0",
|
||||
"lodash.mapkeys": "4.6.0",
|
||||
"lodash.without": "4.4.0",
|
||||
"lodash.xor": "4.5.0",
|
||||
"mime": "3.0.0",
|
||||
"mime-types": "2.1.35",
|
||||
"mocha": "10.2.0",
|
||||
"mocha-junit-reporter": "2.2.0",
|
||||
"mocha-multi-reporters": "1.5.1",
|
||||
"mochawesome": "7.1.3",
|
||||
"mochawesome-merge": "4.3.0",
|
||||
"mochawesome-report-generator": "6.2.0",
|
||||
"moment-timezone": "0.5.43",
|
||||
"path": "0.12.7",
|
||||
"pdf-parse": "1.1.1",
|
||||
"pg": "8.10.0",
|
||||
"recursive-readdir": "2.2.3",
|
||||
"shelljs": "0.8.5",
|
||||
"timezones.json": "1.7.0",
|
||||
"typescript": "5.0.4",
|
||||
"uuid": "9.0.0",
|
||||
"yargs": "17.7.2"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc -b",
|
||||
"cypress:open": "cross-env TZ=Etc/UTC cypress open",
|
||||
"cypress:run": "cross-env TZ=Etc/UTC cypress run",
|
||||
"cypress:run:chrome": "cross-env TZ=Etc/UTC cypress run --browser chrome",
|
||||
"cypress:run:firefox": "cross-env TZ=Etc/UTC cypress run --browser firefox",
|
||||
"cypress:run:edge": "cross-env TZ=Etc/UTC cypress run --browser edge",
|
||||
"cypress:run:electron": "cross-env TZ=Etc/UTC cypress run --browser electron",
|
||||
"benchmarks:run-server": "cd mattermost && bin/mattermost",
|
||||
"start:webhook": "node webhook_serve.js",
|
||||
"test": "cross-env TZ=Etc/UTC cypress run",
|
||||
"test:ci": "node run_tests.js",
|
||||
"uniq-meta": "grep -r \"^// $META:\" cypress | grep -ow '@\\w*' | sort | uniq",
|
||||
"check": "eslint --ext .js,.ts . --quiet --cache",
|
||||
"fix": "eslint --ext .js,.ts . --quiet --fix --cache"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue