diff --git a/.github/workflows/add-good-first-issue-labels.yml b/.github/workflows/add-good-first-issue-labels.yml new file mode 100644 index 00000000..2ae3a056 --- /dev/null +++ b/.github/workflows/add-good-first-issue-labels.yml @@ -0,0 +1,71 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# Purpose of this workflow is to enable anyone to label issue with 'Good First Issue' and 'area/*' with a single command. +name: Add 'Good First Issue' and 'area/*' labels # if proper comment added + +on: + issue_comment: + types: + - created + +jobs: + add-labels: + if: ${{(!github.event.issue.pull_request && github.event.issue.state != 'closed' && github.actor != 'asyncapi-bot') && (contains(github.event.comment.body, '/good-first-issue') || contains(github.event.comment.body, '/gfi' ))}} + runs-on: ubuntu-latest + steps: + - name: Add label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const areas = ['javascript', 'typescript', 'java' , 'go', 'docs', 'ci-cd', 'design']; + const words = context.payload.comment.body.trim().split(" "); + const areaIndex = words.findIndex((word)=> word === '/gfi' || word === '/good-first-issue') + 1 + let area = words[areaIndex]; + switch(area){ + case 'ts': + area = 'typescript'; + break; + case 'js': + area = 'javascript'; + break; + case 'markdown': + area = 'docs'; + break; + } + if(!areas.includes(area)){ + const message = `Hey @${context.payload.sender.login}, your message doesn't follow the requirements, you can try \`/help\`.` + + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: message + }) + } else { + + // remove area if there is any before adding new labels. + const currentLabels = (await github.rest.issues.listLabelsOnIssue({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + })).data.map(label => label.name); + + const shouldBeRemoved = currentLabels.filter(label => (label.startsWith('area/') && !label.endsWith(area))); + shouldBeRemoved.forEach(label => { + github.rest.issues.deleteLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + name: label, + }); + }); + + // Add new labels. + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['good first issue', `area/${area}`] + }); + } diff --git a/.github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml b/.github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml new file mode 100644 index 00000000..66606fc1 --- /dev/null +++ b/.github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml @@ -0,0 +1,110 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# Purpose of this workflow is to enable anyone to label PR with the following labels: +# `ready-to-merge` and `do-not-merge` labels to get stuff merged or blocked from merging +# `autoupdate` to keep a branch up-to-date with the target branch + +name: Label PRs # if proper comment added + +on: + issue_comment: + types: + - created + +jobs: + add-ready-to-merge-label: + if: > + github.event.issue.pull_request && + github.event.issue.state != 'closed' && + github.actor != 'asyncapi-bot' && + ( + contains(github.event.comment.body, '/ready-to-merge') || + contains(github.event.comment.body, '/rtm' ) + ) + + runs-on: ubuntu-latest + steps: + - name: Add ready-to-merge label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const prDetailsUrl = context.payload.issue.pull_request.url; + const { data: pull } = await github.request(prDetailsUrl); + const { draft: isDraft} = pull; + if(!isDraft) { + console.log('adding ready-to-merge label...'); + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['ready-to-merge'] + }) + } + + const { data: comparison } = + await github.rest.repos.compareCommitsWithBasehead({ + owner: pull.head.repo.owner.login, + repo: pull.head.repo.name, + basehead: `${pull.base.label}...${pull.head.label}`, + }); + if (comparison.behind_by !== 0 && pull.mergeable_state === 'behind') { + console.log(`This branch is behind the target by ${comparison.behind_by} commits`) + console.log('adding out-of-date comment...'); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Hello, @${{ github.actor }}! πŸ‘‹πŸΌ + This PR is not up to date with the base branch and can't be merged. + Please update your branch manually with the latest version of the base branch. + PRO-TIP: Add a comment to your PR with the text: \`/au\` or \`/autoupdate\` and our bot will take care of updating the branch in the future. The only requirement for this to work is to enable [Allow edits from maintainers](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) option in your PR. + Thanks πŸ˜„` + }) + } + + add-do-not-merge-label: + if: > + github.event.issue.pull_request && + github.event.issue.state != 'closed' && + github.actor != 'asyncapi-bot' && + ( + contains(github.event.comment.body, '/do-not-merge') || + contains(github.event.comment.body, '/dnm' ) + ) + runs-on: ubuntu-latest + steps: + - name: Add do-not-merge label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['do-not-merge'] + }) + add-autoupdate-label: + if: > + github.event.issue.pull_request && + github.event.issue.state != 'closed' && + github.actor != 'asyncapi-bot' && + ( + contains(github.event.comment.body, '/autoupdate') || + contains(github.event.comment.body, '/au' ) + ) + runs-on: ubuntu-latest + steps: + - name: Add autoupdate label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['autoupdate'] + }) diff --git a/.github/workflows/automerge-for-humans-merging.yml b/.github/workflows/automerge-for-humans-merging.yml new file mode 100644 index 00000000..9ba0be90 --- /dev/null +++ b/.github/workflows/automerge-for-humans-merging.yml @@ -0,0 +1,55 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# Purpose of this workflow is to allow people to merge PR without a need of maintainer doing it. If all checks are in place (including maintainers approval) - JUST MERGE IT! +name: Automerge For Humans + +on: + pull_request_target: + types: + - labeled + - unlabeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + +jobs: + automerge-for-humans: + if: github.event.pull_request.draft == false && (github.event.pull_request.user.login != 'asyncapi-bot' || github.event.pull_request.user.login != 'dependabot[bot]' || github.event.pull_request.user.login != 'dependabot-preview[bot]') #it runs only if PR actor is not a bot, at least not a bot that we know + runs-on: ubuntu-latest + steps: + - name: Get list of authors + uses: sergeysova/jq-action@v2 + id: authors + with: + # This cmd does following (line by line): + # 1. CURL querying the list of commits of the current PR via GH API. Why? Because the current event payload does not carry info about the commits. + # 2. Iterates over the previous returned payload, and creates an array with the filtered results (see below) so we can work wit it later. An example of payload can be found in https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#webhook-payload-example-34. + # 3. Grabs the data we need for adding the `Co-authored-by: ...` lines later and puts it into objects to be used later on. + # 4. Filters the results by excluding the current PR sender. We don't need to add it as co-author since is the PR creator and it will become by default the main author. + # 5. Removes repeated authors (authors can have more than one commit in the PR). + # 6. Builds the `Co-authored-by: ...` lines with actual info. + # 7. Transforms the array into plain text. Thanks to this, the actual stdout of this step can be used by the next Workflow step (wich is basically the automerge). + cmd: | + curl -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.GH_TOKEN }}" "${{github.event.pull_request._links.commits.href}}?per_page=100" | + jq -r '[.[] + | {name: .commit.author.name, email: .commit.author.email, login: .author.login}] + | map(select(.login != "${{github.event.pull_request.user.login}}")) + | unique + | map("Co-authored-by: " + .name + " <" + .email + ">") + | join("\n")' + multiline: true + - name: Automerge PR + uses: pascalgn/automerge-action@22948e0bc22f0aa673800da838595a3e7347e584 #v0.15.6 https://github.com/pascalgn/automerge-action/releases/tag/v0.15.6 + env: + GITHUB_TOKEN: "${{ secrets.GH_TOKEN }}" + MERGE_LABELS: "!do-not-merge,ready-to-merge" + MERGE_METHOD: "squash" + # Using the output of the previous step (`Co-authored-by: ...` lines) as commit description. + # Important to keep 2 empty lines as https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors#creating-co-authored-commits-on-the-command-line mentions + MERGE_COMMIT_MESSAGE: "{pullRequest.title} (#{pullRequest.number})\n\n\n${{ steps.authors.outputs.value }}" + MERGE_RETRIES: "20" + MERGE_RETRY_SLEEP: "30000" diff --git a/.github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml b/.github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml new file mode 100644 index 00000000..00e7f993 --- /dev/null +++ b/.github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml @@ -0,0 +1,32 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# Defence from evil contributor that after adding `ready-to-merge` all suddenly makes evil commit or evil change in PR title +# Label is removed once above action is detected +name: Remove ready-to-merge label + +on: + pull_request_target: + types: + - synchronize + - edited + +jobs: + remove-ready-label: + runs-on: ubuntu-latest + steps: + - name: Remove label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const labelToRemove = 'ready-to-merge'; + const labels = context.payload.pull_request.labels; + const isLabelPresent = labels.some(label => label.name === labelToRemove) + if(!isLabelPresent) return; + github.rest.issues.removeLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: labelToRemove + }) diff --git a/.github/workflows/automerge-orphans.yml b/.github/workflows/automerge-orphans.yml index d712c13d..a1776853 100644 --- a/.github/workflows/automerge-orphans.yml +++ b/.github/workflows/automerge-orphans.yml @@ -1,5 +1,5 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo name: 'Notify on failing automerge' @@ -9,25 +9,27 @@ on: jobs: identify-orphans: + if: startsWith(github.repository, 'asyncapi/') name: Find orphans and notify runs-on: ubuntu-latest steps: + - name: Checkout repository + uses: actions/checkout@v3 - name: Get list of orphans - uses: actions/github-script@v3 + uses: actions/github-script@v6 id: orphans with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const query = `query($owner:String!, $name:String!) { repository(owner:$owner, name:$name){ - pullRequests(first: 100){ + pullRequests(first: 100, states: OPEN){ nodes{ title url author { resourcePath } - state } } } @@ -38,7 +40,7 @@ jobs: }; const { repository: { pullRequests: { nodes } } } = await github.graphql(query, variables); - let orphans = nodes.filter((pr)=> pr.state === 'OPEN' && (pr.author.resourcePath === '/asyncapi-bot' || pr.author.resourcePath === '/apps/dependabot')) + let orphans = nodes.filter( (pr) => pr.author.resourcePath === '/asyncapi-bot' || pr.author.resourcePath === '/apps/dependabot') if (orphans.length) { core.setOutput('found', 'true'); @@ -50,15 +52,15 @@ jobs: } - if: steps.orphans.outputs.found == 'true' name: Convert markdown to slack markdown - uses: LoveToKnow/slackify-markdown-action@v1.0.0 + uses: asyncapi/.github/.github/actions/slackify-markdown@master id: issuemarkdown with: - text: "-> [${{steps.orphans.outputs.title}}](${{steps.orphans.outputs.url}})" + markdown: "-> [${{steps.orphans.outputs.title}}](${{steps.orphans.outputs.url}})" - if: steps.orphans.outputs.found == 'true' name: Send info about orphan to slack uses: rtCamp/action-slack-notify@v2 env: - SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}} + SLACK_WEBHOOK: ${{secrets.SLACK_CI_FAIL_NOTIFY}} SLACK_TITLE: 🚨 Not merged PR that should be automerged 🚨 SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}} MSG_MINIMAL: true \ No newline at end of file diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 19c8d189..9253675c 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -1,47 +1,52 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo. -name: Automerge release bump PR +name: Automerge PRs from bots on: pull_request_target: types: - - labeled - - unlabeled - - synchronize - opened - - edited - - ready_for_review - - reopened - - unlocked - pull_request_review: - types: - - submitted - -jobs: + - synchronize - autoapprove: - if: github.event.pull_request.draft == false +jobs: + autoapprove-for-bot: + name: Autoapprove PR comming from a bot + if: > + contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]"]'), github.event.pull_request.user.login) && + contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]"]'), github.actor) && + !contains(github.event.pull_request.labels.*.name, 'released') runs-on: ubuntu-latest steps: - name: Autoapproving - uses: hmarr/auto-approve-action@v2 - if: github.actor == 'asyncapi-bot' || github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' + uses: hmarr/auto-approve-action@44888193675f29a83e04faf4002fa8c0b537b1e4 # v3.2.1 is used https://github.com/hmarr/auto-approve-action/releases/tag/v3.2.1 + with: + github-token: "${{ secrets.GH_TOKEN_BOT_EVE }}" + + - name: Label autoapproved + uses: actions/github-script@v6 with: - github-token: "${{ secrets.GITHUB_TOKEN }}" + github-token: ${{ secrets.GH_TOKEN }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['autoapproved', 'autoupdate'] + }) - automerge: - needs: [autoapprove] + automerge-for-bot: + name: Automerge PR autoapproved by a bot + needs: [autoapprove-for-bot] runs-on: ubuntu-latest steps: - name: Automerging - uses: pascalgn/automerge-action@v0.13.0 - if: github.actor == 'asyncapi-bot' || github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' + uses: pascalgn/automerge-action@22948e0bc22f0aa673800da838595a3e7347e584 #v0.15.6 https://github.com/pascalgn/automerge-action/releases/tag/v0.15.6 env: GITHUB_TOKEN: "${{ secrets.GH_TOKEN }}" GITHUB_LOGIN: asyncapi-bot MERGE_LABELS: "" MERGE_METHOD: "squash" - MERGE_COMMIT_MESSAGE: "pull-request-title" + MERGE_COMMIT_MESSAGE: "{pullRequest.title} (#{pullRequest.number})" MERGE_RETRIES: "20" - MERGE_RETRY_SLEEP: "20000" \ No newline at end of file + MERGE_RETRY_SLEEP: "30000" diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml new file mode 100644 index 00000000..ad8e0198 --- /dev/null +++ b/.github/workflows/autoupdate.yml @@ -0,0 +1,34 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# This workflow is designed to work with: +# - autoapprove and automerge workflows for dependabot and asyncapibot. +# - special release branches that we from time to time create in upstream repos. If we open up PRs for them from the very beginning of the release, the release branch will constantly update with new things from the destination branch they are opened against + +# It uses GitHub Action that auto-updates pull requests branches, whenever changes are pushed to their destination branch. +# Autoupdating to latest destination branch works only in the context of upstream repo and not forks + +name: autoupdate + +on: + push: + branches-ignore: + - 'version-bump/**' + - 'dependabot/**' + - 'bot/**' + - 'all-contributors/**' + +jobs: + autoupdate-for-bot: + if: startsWith(github.repository, 'asyncapi/') + name: Autoupdate autoapproved PR created in the upstream + runs-on: ubuntu-latest + steps: + - name: Autoupdating + uses: docker://chinthakagodawita/autoupdate-action:v1 + env: + GITHUB_TOKEN: '${{ secrets.GH_TOKEN_BOT_EVE }}' + PR_FILTER: "labelled" + PR_LABELS: "autoupdate" + PR_READY_STATE: "ready_for_review" + MERGE_CONFLICT_ACTION: "ignore" diff --git a/.github/workflows/help-command.yml b/.github/workflows/help-command.yml new file mode 100644 index 00000000..d4ba4a44 --- /dev/null +++ b/.github/workflows/help-command.yml @@ -0,0 +1,60 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +name: Create help comment + +on: + issue_comment: + types: + - created + +jobs: + create_help_comment_pr: + if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/help') && github.actor != 'asyncapi-bot' }} + runs-on: ubuntu-latest + steps: + - name: Add comment to PR + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + //Yes to add comment to PR the same endpoint is use that we use to create a comment in issue + //For more details http://developer.github.com/v3/issues/comments/ + //Also proved by this action https://github.com/actions-ecosystem/action-create-comment/blob/main/src/main.ts + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Hello, @${{ github.actor }}! πŸ‘‹πŸΌ + + I'm 🧞🧞🧞 Genie 🧞🧞🧞 from the magic lamp. Looks like somebody needs a hand! + + At the moment the following comments are supported in pull requests: + + - \`/ready-to-merge\` or \`/rtm\` - This comment will trigger automerge of PR in case all required checks are green, approvals in place and do-not-merge label is not added + - \`/do-not-merge\` or \`/dnm\` - This comment will block automerging even if all conditions are met and ready-to-merge label is added + - \`/autoupdate\` or \`/au\` - This comment will add \`autoupdate\` label to the PR and keeps your PR up-to-date to the target branch's future changes. Unless there is a merge conflict or it is a draft PR.` + }) + + create_help_comment_issue: + if: ${{ !github.event.issue.pull_request && contains(github.event.comment.body, '/help') && github.actor != 'asyncapi-bot' }} + runs-on: ubuntu-latest + steps: + - name: Add comment to Issue + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Hello, @${{ github.actor }}! πŸ‘‹πŸΌ + + I'm 🧞🧞🧞 Genie 🧞🧞🧞 from the magic lamp. Looks like somebody needs a hand! + + At the moment the following comments are supported in issues: + + - \`/good-first-issue {js | ts | java | go | docs | design | ci-cd}\` or \`/gfi {js | ts | java | go | docs | design | ci-cd}\` - label an issue as a \`good first issue\`. + example: \`/gfi js\` or \`/good-first-issue ci-cd\`` + }) \ No newline at end of file diff --git a/.github/workflows/if-go-pr-testing.yml b/.github/workflows/if-go-pr-testing.yml deleted file mode 100644 index 4a4078ff..00000000 --- a/.github/workflows/if-go-pr-testing.yml +++ /dev/null @@ -1,58 +0,0 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo -#It does magic only if there is go.mod file in the root of the project -name: PR testing - if Go project - -on: - pull_request: - types: [opened, reopened, synchronize, ready_for_review] - -jobs: - lint: - if: github.event.pull_request.draft == false - name: lint - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - name: Check if Go project and has go.mod - id: gomod - run: test -e ./go.mod && echo "::set-output name=exists::true" || echo "::set-output name=exists::false" - shell: bash - - if: steps.gomod.outputs.exists == 'true' - name: Setup Go - uses: actions/setup-go@v2.1.3 - with: - go-version: 1.16 - - if: steps.gomod.outputs.exists == 'true' - name: golangci-lint - uses: golangci/golangci-lint-action@v2 # golangci-lint version extracted from go.mod. `latest` if missing. - with: - skip-go-installation: true # we wanna control the version of Go in use - - test: - if: github.event.pull_request.draft == false - name: ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - name: Check if Go project and has go.mod - id: gomod - run: test -e ./go.mod && echo "::set-output name=exists::true" || echo "::set-output name=exists::false" - shell: bash - - if: steps.gomod.outputs.exists == 'true' - name: Setup Go - uses: actions/setup-go@v2.1.3 - with: - go-version: 1.16 - - if: steps.gomod.outputs.exists == 'true' - name: Build - run: go build -v ./... - - if: steps.gomod.outputs.exists == 'true' - name: Test - run: go test -v ./... - diff --git a/.github/workflows/if-nodejs-pr-testing.yml b/.github/workflows/if-nodejs-pr-testing.yml deleted file mode 100644 index 21dd5b5a..00000000 --- a/.github/workflows/if-nodejs-pr-testing.yml +++ /dev/null @@ -1,48 +0,0 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo -#It does magic only if there is package.json file in the root of the project -name: PR testing - if Node project - -on: - pull_request: - types: [opened, reopened, synchronize, ready_for_review] - -jobs: - test: - if: github.event.pull_request.draft == false - name: ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - name: Check if Node.js project and has package.json - id: packagejson - run: test -e ./package.json && echo "::set-output name=exists::true" || echo "::set-output name=exists::false" - shell: bash - - if: steps.packagejson.outputs.exists == 'true' - name: Setup Node.js - uses: actions/setup-node@v1 - with: - node-version: 14 - - if: steps.packagejson.outputs.exists == 'true' - name: Install dependencies - id: first-installation - run: npm install --loglevel verbose - continue-on-error: true - - if: steps.first-installation.outputs.status == 'failure' && steps.packagejson.outputs.exists == 'true' - name: Clear NPM cache and install deps again - run: | - npm cache clean --force - npm install --loglevel verbose - - if: steps.packagejson.outputs.exists == 'true' - name: Test - run: npm test - - if: steps.packagejson.outputs.exists == 'true' - name: Run linter - run: npm run lint - - if: steps.packagejson.outputs.exists == 'true' - name: Run release assets generation to make sure PR does not break it - run: npm run generate:assets diff --git a/.github/workflows/if-nodejs-release.yml b/.github/workflows/if-nodejs-release.yml deleted file mode 100644 index 793eccb0..00000000 --- a/.github/workflows/if-nodejs-release.yml +++ /dev/null @@ -1,70 +0,0 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo -#It does magic only if there is package.json file in the root of the project -name: Release - if Node project - -on: - push: - branches: - - master - # below lines are not enough to have release supported for these branches - # make sure configuration of `semantic-release` package mentiones these branches - - next - - '**-release' - -jobs: - - test: - name: Test on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - name: Check if Node.js project and has package.json - id: packagejson - run: test -e ./package.json && echo "::set-output name=exists::true" || echo "::set-output name=exists::false" - shell: bash - - if: steps.packagejson.outputs.exists == 'true' - name: Setup Node.js - uses: actions/setup-node@v1 - with: - node-version: 14 - - if: steps.packagejson.outputs.exists == 'true' - name: Install dependencies - run: npm install - - if: steps.packagejson.outputs.exists == 'true' - name: Run test - run: npm test - - release: - needs: test - name: Publish to NPM and GitHub - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - name: Check if Node.js project and has package.json - id: packagejson - run: test -e ./package.json && echo "::set-output name=exists::true" || echo "::set-output name=exists::false" - - if: steps.packagejson.outputs.exists == 'true' - name: Setup Node.js - uses: actions/setup-node@v1 - with: - node-version: 14 - - if: steps.packagejson.outputs.exists == 'true' - name: Install dependencies - run: npm install - - if: steps.packagejson.outputs.exists == 'true' - name: Release to NPM and GitHub - id: release - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GIT_AUTHOR_NAME: asyncapi-bot - GIT_AUTHOR_EMAIL: info@asyncapi.io - GIT_COMMITTER_NAME: asyncapi-bot - GIT_COMMITTER_EMAIL: info@asyncapi.io - run: npm run release \ No newline at end of file diff --git a/.github/workflows/if-nodejs-version-bump.yml b/.github/workflows/if-nodejs-version-bump.yml deleted file mode 100644 index c1f5c860..00000000 --- a/.github/workflows/if-nodejs-version-bump.yml +++ /dev/null @@ -1,48 +0,0 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo -#It does magic only if there is package.json file in the root of the project -name: Version bump - if Node.js project - -on: - release: - types: - - published - -jobs: - version_bump: - name: Generate assets and bump - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # target branch of release. More info https://docs.github.com/en/rest/reference/repos#releases - # in case release is created from release branch then we need to checkout from given branch - # if @semantic-release/github is used to publish, the minimum version is 7.2.0 for proper working - ref: ${{ github.event.release.target_commitish }} - - name: Check if Node.js project and has package.json - id: packagejson - run: test -e ./package.json && echo "::set-output name=exists::true" || echo "::set-output name=exists::false" - - if: steps.packagejson.outputs.exists == 'true' - name: Install dependencies - run: npm install - - if: steps.packagejson.outputs.exists == 'true' - name: Assets generation - run: npm run generate:assets - - if: steps.packagejson.outputs.exists == 'true' - name: Bump version in package.json - # There is no need to substract "v" from the tag as version script handles it - # When adding "bump:version" script in package.json, make sure no tags are added by default (--no-git-tag-version) as they are already added by release workflow - # When adding "bump:version" script in package.json, make sure --allow-same-version is set in case someone forgot and updated package.json manually and we want to avoide this action to fail and raise confusion - run: VERSION=${{github.event.release.tag_name}} npm run bump:version - - if: steps.packagejson.outputs.exists == 'true' - name: Create Pull Request with updated asset files including package.json - uses: peter-evans/create-pull-request@v3 - with: - token: ${{ secrets.GH_TOKEN }} - commit-message: 'chore(release): ${{github.event.release.tag_name}}' - committer: asyncapi-bot - author: asyncapi-bot - title: 'chore(release): ${{github.event.release.tag_name}}' - body: 'Version bump in package.json for release [${{github.event.release.tag_name}}](${{github.event.release.html_url}})' - branch: version-bump/${{github.event.release.tag_name}} \ No newline at end of file diff --git a/.github/workflows/issues-prs-notifications.yml b/.github/workflows/issues-prs-notifications.yml index 8577db6f..78ebe960 100644 --- a/.github/workflows/issues-prs-notifications.yml +++ b/.github/workflows/issues-prs-notifications.yml @@ -1,49 +1,76 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# This action notifies community on slack whenever there is a new issue, PR or discussion started in given repository name: Notify slack on: - issues: types: [opened, reopened] pull_request_target: types: [opened, reopened, ready_for_review] + discussion: + types: [created] + jobs: + issue: + if: github.event_name == 'issues' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' + name: Notify slack on every new issue + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Convert markdown to slack markdown for issue + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: issuemarkdown + with: + markdown: "[${{github.event.issue.title}}](${{github.event.issue.html_url}}) \n ${{github.event.issue.body}}" + - name: Send info about issue + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}} + SLACK_TITLE: πŸ› New Issue in ${{github.repository}} πŸ› + SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}} + MSG_MINIMAL: true - issue: - if: github.event_name == 'issues' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' - name: On every new issue - runs-on: ubuntu-latest - steps: - - name: Convert markdown to slack markdown for issue - uses: LoveToKnow/slackify-markdown-action@v1.0.0 - id: issuemarkdown - with: - text: "[${{github.event.issue.title}}](${{github.event.issue.html_url}}) \n ${{github.event.issue.body}}" - - name: Send info about issue - uses: rtCamp/action-slack-notify@v2 - env: - SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}} - SLACK_TITLE: πŸ› New Issue πŸ› - SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}} - MSG_MINIMAL: true + pull_request: + if: github.event_name == 'pull_request_target' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' + name: Notify slack on every new pull request + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Convert markdown to slack markdown for pull request + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: prmarkdown + with: + markdown: "[${{github.event.pull_request.title}}](${{github.event.pull_request.html_url}}) \n ${{github.event.pull_request.body}}" + - name: Send info about pull request + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}} + SLACK_TITLE: πŸ’ͺ New Pull Request in ${{github.repository}} πŸ’ͺ + SLACK_MESSAGE: ${{steps.prmarkdown.outputs.text}} + MSG_MINIMAL: true - pull_request: - if: github.event_name == 'pull_request_target' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' - name: On every new pull request - runs-on: ubuntu-latest - steps: - - name: Convert markdown to slack markdown for pull request - uses: LoveToKnow/slackify-markdown-action@v1.0.0 - id: prmarkdown - with: - text: "[${{github.event.pull_request.title}}](${{github.event.pull_request.html_url}}) \n ${{github.event.pull_request.body}}" - - name: Send info about pull request - uses: rtCamp/action-slack-notify@v2 - env: - SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}} - SLACK_TITLE: πŸ’ͺ New Pull Request πŸ’ͺ - SLACK_MESSAGE: ${{steps.prmarkdown.outputs.text}} - MSG_MINIMAL: true + discussion: + if: github.event_name == 'discussion' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' + name: Notify slack on every new pull request + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Convert markdown to slack markdown for pull request + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: discussionmarkdown + with: + markdown: "[${{github.event.discussion.title}}](${{github.event.discussion.html_url}}) \n ${{github.event.discussion.body}}" + - name: Send info about pull request + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}} + SLACK_TITLE: πŸ’¬ New Discussion in ${{github.repository}} πŸ’¬ + SLACK_MESSAGE: ${{steps.discussionmarkdown.outputs.text}} + MSG_MINIMAL: true diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml index fbee657e..77aa1c6e 100644 --- a/.github/workflows/lint-pr-title.yml +++ b/.github/workflows/lint-pr-title.yml @@ -1,22 +1,47 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo name: Lint PR title on: - pull_request_target: types: [opened, reopened, synchronize, edited, ready_for_review] jobs: - - lint: - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v3.2.5 + lint-pr-title: + name: Lint PR title + runs-on: ubuntu-latest + steps: + # Since this workflow is REQUIRED for a PR to be mergable, we have to have this 'if' statement in step level instead of job level. + - if: ${{ !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor) }} + uses: amannn/action-semantic-pull-request@c3cd5d1ea3580753008872425915e343e351ab54 #version 5.2.0 https://github.com/amannn/action-semantic-pull-request/releases/tag/v5.2.0 + id: lint_pr_title env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GH_TOKEN}} with: subjectPattern: ^(?![A-Z]).+$ subjectPatternError: | The subject "{subject}" found in the pull request title "{title}" should start with a lowercase character. + + # Comments the error message from the above lint_pr_title action + - if: ${{ always() && steps.lint_pr_title.outputs.error_message != null && !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor)}} + name: Comment on PR + uses: marocchino/sticky-pull-request-comment@3d60a5b2dae89d44e0c6ddc69dd7536aec2071cd #use 2.5.0 https://github.com/marocchino/sticky-pull-request-comment/releases/tag/v2.5.0 + with: + header: pr-title-lint-error + GITHUB_TOKEN: ${{ secrets.GH_TOKEN}} + message: | + + We require all PRs to follow [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). + More details πŸ‘‡πŸΌ + ``` + ${{ steps.lint_pr_title.outputs.error_message}} + ``` + # deletes the error comment if the title is correct + - if: ${{ steps.lint_pr_title.outputs.error_message == null }} + name: delete the comment + uses: marocchino/sticky-pull-request-comment@3d60a5b2dae89d44e0c6ddc69dd7536aec2071cd #use 2.5.0 https://github.com/marocchino/sticky-pull-request-comment/releases/tag/v2.5.0 + with: + header: pr-title-lint-error + delete: true + GITHUB_TOKEN: ${{ secrets.GH_TOKEN}} diff --git a/.github/workflows/notify-tsc-members-mention.yml b/.github/workflows/notify-tsc-members-mention.yml new file mode 100644 index 00000000..d72fd85b --- /dev/null +++ b/.github/workflows/notify-tsc-members-mention.yml @@ -0,0 +1,297 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# This action notifies community on slack whenever there is a new issue, PR or discussion started in given repository +name: Notify slack and email subscribers whenever TSC members are mentioned in GitHub + +on: + issue_comment: + types: + - created + + discussion_comment: + types: + - created + + issues: + types: + - opened + + pull_request_target: + types: + - opened + + discussion: + types: + - created + +jobs: + issue: + if: github.event_name == 'issues' && contains(github.event.issue.body, '@asyncapi/tsc_members') + name: TSC notification on every new issue + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: issuemarkdown + with: + markdown: "[${{github.event.issue.title}}](${{github.event.issue.html_url}}) \n ${{github.event.issue.body}}" + - name: Send info about issue + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New issue that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.issue.html_url}}', '${{github.event.issue.title}}'); + + pull_request: + if: github.event_name == 'pull_request_target' && contains(github.event.pull_request.body, '@asyncapi/tsc_members') + name: TSC notification on every new pull request + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: prmarkdown + with: + markdown: "[${{github.event.pull_request.title}}](${{github.event.pull_request.html_url}}) \n ${{github.event.pull_request.body}}" + - name: Send info about pull request + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New PR that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.prmarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.pull_request.html_url}}', '${{github.event.pull_request.title}}'); + + discussion: + if: github.event_name == 'discussion' && contains(github.event.discussion.body, '@asyncapi/tsc_members') + name: TSC notification on every new discussion + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: discussionmarkdown + with: + markdown: "[${{github.event.discussion.title}}](${{github.event.discussion.html_url}}) \n ${{github.event.discussion.body}}" + - name: Send info about pull request + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New discussion that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.discussionmarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.discussion.html_url}}', '${{github.event.discussion.title}}'); + + issue_comment: + if: ${{ github.event_name == 'issue_comment' && !github.event.issue.pull_request && contains(github.event.comment.body, '@asyncapi/tsc_members') }} + name: TSC notification on every new comment in issue + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: issuemarkdown + with: + markdown: "[${{github.event.issue.title}}](${{github.event.comment.html_url}}) \n ${{github.event.comment.body}}" + - name: Send info about issue comment + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New comment under existing issue that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.comment.html_url}}', '${{github.event.issue.title}}'); + + pr_comment: + if: github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '@asyncapi/tsc_members') + name: TSC notification on every new comment in pr + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: prmarkdown + with: + markdown: "[${{github.event.issue.title}}](${{github.event.comment.html_url}}) \n ${{github.event.comment.body}}" + - name: Send info about PR comment + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New comment under existing PR that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.prmarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.comment.html_url}}', '${{github.event.issue.title}}'); + + discussion_comment: + if: github.event_name == 'discussion_comment' && contains(github.event.comment.body, '@asyncapi/tsc_members') + name: TSC notification on every new comment in discussion + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: discussionmarkdown + with: + markdown: "[${{github.event.discussion.title}}](${{github.event.comment.html_url}}) \n ${{github.event.comment.body}}" + - name: Send info about discussion comment + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New comment under existing discussion that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.discussionmarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.comment.html_url}}', '${{github.event.discussion.title}}'); diff --git a/.github/workflows/release-announcements.yml b/.github/workflows/release-announcements.yml index 653ca286..9587cace 100644 --- a/.github/workflows/release-announcements.yml +++ b/.github/workflows/release-announcements.yml @@ -1,5 +1,6 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + name: 'Announce releases in different channels' on: @@ -9,15 +10,17 @@ on: jobs: - slack: + slack-announce: name: Slack - notify on every release runs-on: ubuntu-latest steps: + - name: Checkout repository + uses: actions/checkout@v3 - name: Convert markdown to slack markdown for issue - uses: LoveToKnow/slackify-markdown-action@v1.0.0 + uses: asyncapi/.github/.github/actions/slackify-markdown@master id: markdown with: - text: "[${{github.event.release.tag_name}}](${{github.event.release.html_url}}) \n ${{ github.event.release.body }}" + markdown: "[${{github.event.release.tag_name}}](${{github.event.release.html_url}}) \n ${{ github.event.release.body }}" - name: Send info about release to Slack uses: rtCamp/action-slack-notify@v2 env: @@ -26,14 +29,14 @@ jobs: SLACK_MESSAGE: ${{steps.markdown.outputs.text}} MSG_MINIMAL: true - twitter: + twitter-announce: name: Twitter - notify on minor and major releases runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Get version of last and previous release - uses: actions/github-script@v3 + uses: actions/github-script@v6 id: versions with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -59,15 +62,15 @@ jobs: - name: Identify release type id: releasetype # if previousver is not provided then this steps just logs information about missing version, no errors - run: echo "::set-output name=type::$(npx -q -p semver-diff-cli semver-diff ${{steps.versions.outputs.previousver}} ${{steps.versions.outputs.lastver}})" + run: echo "type=$(npx -q -p semver-diff-cli semver-diff ${{steps.versions.outputs.previousver}} ${{steps.versions.outputs.lastver}})" >> $GITHUB_OUTPUT - name: Get name of the person that is behind the newly released version id: author - run: echo "::set-output name=name::$(git log -1 --pretty=format:'%an')" + run: echo "name=$(git log -1 --pretty=format:'%an')" >> $GITHUB_OUTPUT - name: Publish information about the release to Twitter # tweet only if detected version change is not a patch # tweet goes out even if the type is not major or minor but "You need provide version number to compare." # it is ok, it just means we did not identify previous version as we are tweeting out information about the release for the first time if: steps.releasetype.outputs.type != 'null' && steps.releasetype.outputs.type != 'patch' # null means that versions are the same - uses: m1ner79/Github-Twittction@v1.0.1 + uses: m1ner79/Github-Twittction@d1e508b6c2170145127138f93c49b7c46c6ff3a7 # using 2.0.0 https://github.com/m1ner79/Github-Twittction/releases/tag/v2.0.0 with: twitter_status: "Release ${{github.event.release.tag_name}} for ${{github.repository}} is out in the wild 😱πŸ’ͺπŸΎπŸŽ‚\n\nThank you for the contribution ${{ steps.author.outputs.name }} ${{github.event.release.html_url}}" twitter_consumer_key: ${{ secrets.TWITTER_CONSUMER_KEY }} diff --git a/.github/workflows/scripts/README.md b/.github/workflows/scripts/README.md new file mode 100644 index 00000000..ba97dca0 --- /dev/null +++ b/.github/workflows/scripts/README.md @@ -0,0 +1 @@ +The entire `scripts` directory is centrally managed in [.github](https://github.com/asyncapi/.github/) repository. Any changes in this folder should be done in central repository. \ No newline at end of file diff --git a/.github/workflows/scripts/mailchimp/htmlContent.js b/.github/workflows/scripts/mailchimp/htmlContent.js new file mode 100644 index 00000000..d132c72f --- /dev/null +++ b/.github/workflows/scripts/mailchimp/htmlContent.js @@ -0,0 +1,495 @@ +/** + * This code is centrally managed in https://github.com/asyncapi/.github/ + * Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + */ +module.exports = (link, title) => { + + return ` + + + + + + + + *|MC:SUBJECT|* + + + + + + +
+ + + + +
+ + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + +
+ + Hey *|FNAME|*,
+
+There is a new topic at AsyncAPI Initiative that requires Technical Steering Committee attention. +
+Please have a look if it is just something you need to be aware of, or maybe your vote is needed. +
+Topic: ${ title }. +
+ + + +
+ + + + + + + +
+ + + + + + + + +
+ + Cheers,
+AsyncAPI Initiative +
+ + + +
+ + + + + +
+ + + + + + + + +
+ + Want to change how you receive these emails?
+You can update your preferences or unsubscribe from this list.
+  +
+ + + +
+ + +
+
+ + +` +} \ No newline at end of file diff --git a/.github/workflows/scripts/mailchimp/index.js b/.github/workflows/scripts/mailchimp/index.js new file mode 100644 index 00000000..4a200c6c --- /dev/null +++ b/.github/workflows/scripts/mailchimp/index.js @@ -0,0 +1,79 @@ +/** + * This code is centrally managed in https://github.com/asyncapi/.github/ + * Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + */ +const mailchimp = require('@mailchimp/mailchimp_marketing'); +const core = require('@actions/core'); +const htmlContent = require('./htmlContent.js'); + +/** + * Sending API request to mailchimp to schedule email to subscribers + * Input is the URL to issue/discussion or other resource + */ +module.exports = async (link, title) => { + + let newCampaign; + + mailchimp.setConfig({ + apiKey: process.env.MAILCHIMP_API_KEY, + server: 'us12' + }); + + /* + * First we create campaign + */ + try { + newCampaign = await mailchimp.campaigns.create({ + type: 'regular', + recipients: { + list_id: '6e3e437abe', + segment_opts: { + match: 'any', + conditions: [{ + condition_type: 'Interests', + field: 'interests-2801e38b9f', + op: 'interestcontains', + value: ['f7204f9b90'] + }] + } + }, + settings: { + subject_line: `TSC attention required: ${ title }`, + preview_text: 'Check out the latest topic that TSC members have to be aware of', + title: `New topic info - ${ new Date(Date.now()).toUTCString()}`, + from_name: 'AsyncAPI Initiative', + reply_to: 'info@asyncapi.io', + } + }); + } catch (error) { + return core.setFailed(`Failed creating campaign: ${ JSON.stringify(error) }`); + } + + /* + * Content of the email is added separately after campaign creation + */ + try { + await mailchimp.campaigns.setContent(newCampaign.id, { html: htmlContent(link, title) }); + } catch (error) { + return core.setFailed(`Failed adding content to campaign: ${ JSON.stringify(error) }`); + } + + /* + * We schedule an email to send it immediately + */ + try { + //schedule for next hour + //so if this code was created by new issue creation at 9:46, the email is scheduled for 10:00 + //is it like this as schedule has limitiations and you cannot schedule email for 9:48 + const scheduleDate = new Date(Date.parse(new Date(Date.now()).toISOString()) + 1 * 1 * 60 * 60 * 1000); + scheduleDate.setUTCMinutes(00); + + await mailchimp.campaigns.schedule(newCampaign.id, { + schedule_time: scheduleDate.toISOString(), + }); + } catch (error) { + return core.setFailed(`Failed scheduling email: ${ JSON.stringify(error) }`); + } + + core.info(`New email campaign created`); +} \ No newline at end of file diff --git a/.github/workflows/scripts/mailchimp/package-lock.json b/.github/workflows/scripts/mailchimp/package-lock.json new file mode 100644 index 00000000..7ee7d84f --- /dev/null +++ b/.github/workflows/scripts/mailchimp/package-lock.json @@ -0,0 +1,597 @@ +{ + "name": "schedule-email", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "schedule-email", + "license": "Apache 2.0", + "dependencies": { + "@actions/core": "1.6.0", + "@mailchimp/mailchimp_marketing": "3.0.74" + } + }, + "node_modules/@actions/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", + "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==", + "dependencies": { + "@actions/http-client": "^1.0.11" + } + }, + "node_modules/@actions/http-client": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", + "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", + "dependencies": { + "tunnel": "0.0.6" + } + }, + "node_modules/@mailchimp/mailchimp_marketing": { + "version": "3.0.74", + "resolved": "https://registry.npmjs.org/@mailchimp/mailchimp_marketing/-/mailchimp_marketing-3.0.74.tgz", + "integrity": "sha512-039iu4GRr7wpXqweBLuS05wvOBtPxSa31cjxgftBYSt7031f0sHEi8Up2DicfbSuQK0AynPDeVyuxrb31Lx+yQ==", + "dependencies": { + "dotenv": "^8.2.0", + "superagent": "3.8.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "node_modules/cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "engines": { + "node": ">=10" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", + "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/superagent": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.1.tgz", + "integrity": "sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==", + "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at .", + "dependencies": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.1.1", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + } + }, + "dependencies": { + "@actions/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", + "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==", + "requires": { + "@actions/http-client": "^1.0.11" + } + }, + "@actions/http-client": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", + "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", + "requires": { + "tunnel": "0.0.6" + } + }, + "@mailchimp/mailchimp_marketing": { + "version": "3.0.74", + "resolved": "https://registry.npmjs.org/@mailchimp/mailchimp_marketing/-/mailchimp_marketing-3.0.74.tgz", + "integrity": "sha512-039iu4GRr7wpXqweBLuS05wvOBtPxSa31cjxgftBYSt7031f0sHEi8Up2DicfbSuQK0AynPDeVyuxrb31Lx+yQ==", + "requires": { + "dotenv": "^8.2.0", + "superagent": "3.8.1" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "superagent": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.1.tgz", + "integrity": "sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==", + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.1.1", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + } + } +} \ No newline at end of file diff --git a/.github/workflows/scripts/mailchimp/package.json b/.github/workflows/scripts/mailchimp/package.json new file mode 100644 index 00000000..cc50e43e --- /dev/null +++ b/.github/workflows/scripts/mailchimp/package.json @@ -0,0 +1,9 @@ +{ + "name": "schedule-email", + "description": "This code is responsible for scheduling an email campaign. This file is centrally managed in https://github.com/asyncapi/.github/", + "license": "Apache 2.0", + "dependencies": { + "@actions/core": "1.6.0", + "@mailchimp/mailchimp_marketing": "3.0.74" + } +} \ No newline at end of file diff --git a/.github/workflows/sentiment-analysis.yml b/.github/workflows/sentiment-analysis.yml deleted file mode 100644 index 431d1937..00000000 --- a/.github/workflows/sentiment-analysis.yml +++ /dev/null @@ -1,44 +0,0 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo - -name: 'Sentiment Analysis' - -on: - issue_comment: - types: - - created - - edited - issues: - types: - - opened - - edited - pull_request: - types: - - opened - - edited - pull_request_review: - types: - - submitted - - edited - pull_request_review_comment: - types: - - created - - edited -jobs: - test: - name: Checking sentiments - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Check sentiment - uses: derberg/code-of-conduct-sentiment-analysis-github-action@v1 - id: sentiments - with: - gcp_key: ${{ secrets.GCP_KEY_SENTIMENT }} - - uses: someimportantcompany/github-actions-slack-message@v1 - # this step runs only if sentiment is a negative number - if: steps.sentiments.outputs.sentiment < -0.6 - with: - webhook-url: ${{ secrets.SLACK_SENTIMENTS }} - text: Here ${{steps.sentiments.outputs.source}} you can find a potential negative text that requires your attention as the sentiment analysis score is ${{steps.sentiments.outputs.sentiment}} - color: orange \ No newline at end of file diff --git a/.github/workflows/stale-issues-prs.yml b/.github/workflows/stale-issues-prs.yml index f1c6a836..ed1fcf19 100644 --- a/.github/workflows/stale-issues-prs.yml +++ b/.github/workflows/stale-issues-prs.yml @@ -1,5 +1,5 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo name: Manage stale issues and PRs @@ -9,22 +9,37 @@ on: jobs: stale: + if: startsWith(github.repository, 'asyncapi/') + name: Mark issue or PR as stale runs-on: ubuntu-latest steps: - - uses: actions/stale@v1.1.0 + - uses: actions/stale@99b6c709598e2b0d0841cd037aaf1ba07a4410bd #v5.2.0 but pointing to commit for security reasons with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: | This issue has been automatically marked as stale because it has not had recent activity :sleeping: - It will be closed in 60 days if no further activity occurs. To unstale this issue, add a comment with detailed explanation. - Thank you for your contributions :heart: + + It will be closed in 120 days if no further activity occurs. To unstale this issue, add a comment with a detailed explanation. + + There can be many reasons why some specific issue has no activity. The most probable cause is lack of time, not lack of interest. AsyncAPI Initiative is a Linux Foundation project not owned by a single for-profit company. It is a community-driven initiative ruled under [open governance model](https://github.com/asyncapi/community/blob/master/CHARTER.md). + + Let us figure out together how to push this issue forward. Connect with us through [one of many communication channels](https://github.com/asyncapi/community/issues/1) we established here. + + Thank you for your patience :heart: stale-pr-message: | This pull request has been automatically marked as stale because it has not had recent activity :sleeping: - It will be closed in 60 days if no further activity occurs. To unstale this pull request, add a comment with detailed explanation. - Thank you for your contributions :heart: - days-before-stale: 60 - days-before-close: 60 + + It will be closed in 120 days if no further activity occurs. To unstale this pull request, add a comment with detailed explanation. + + There can be many reasons why some specific pull request has no activity. The most probable cause is lack of time, not lack of interest. AsyncAPI Initiative is a Linux Foundation project not owned by a single for-profit company. It is a community-driven initiative ruled under [open governance model](https://github.com/asyncapi/community/blob/master/CHARTER.md). + + Let us figure out together how to push this pull request forward. Connect with us through [one of many communication channels](https://github.com/asyncapi/community/issues/1) we established here. + + Thank you for your patience :heart: + days-before-stale: 120 + days-before-close: 120 stale-issue-label: stale stale-pr-label: stale - exempt-issue-label: keep-open - exempt-pr-label: keep-open \ No newline at end of file + exempt-issue-labels: keep-open + exempt-pr-labels: keep-open + close-issue-reason: not_planned diff --git a/.github/workflows/welcome-first-time-contrib.yml b/.github/workflows/welcome-first-time-contrib.yml index e687e75a..cbc23ce7 100644 --- a/.github/workflows/welcome-first-time-contrib.yml +++ b/.github/workflows/welcome-first-time-contrib.yml @@ -1,5 +1,5 @@ -#This action is centrally managed in https://github.com/asyncapi/.github/ -#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo name: Welcome first time contributors @@ -13,18 +13,73 @@ on: jobs: welcome: + name: Post welcome message + if: ${{ !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor) }} runs-on: ubuntu-latest steps: - - uses: actions/first-interaction@v1 + - uses: actions/github-script@v6 with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - issue-message: | - Welcome to AsyncAPI. Thanks a lot for reporting your first issue. Please check out our [contributors guide](https://github.com/asyncapi/.github/blob/master/CONTRIBUTING.md) and the instructions about a [basic recommended setup](https://github.com/asyncapi/.github/blob/master/git-workflow.md) useful for opening a pull request. - - Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115). - - - pr-message: | - Welcome to AsyncAPI. Thanks a lot for creating your first pull request. Please check out our [contributors guide](https://github.com/asyncapi/.github/blob/master/CONTRIBUTING.md) and the instructions about a [basic recommended setup](https://github.com/asyncapi/.github/blob/master/git-workflow.md) useful for opening a pull request. - - Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115). + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const issueMessage = `Welcome to AsyncAPI. Thanks a lot for reporting your first issue. Please check out our [contributors guide](https://github.com/asyncapi/community/blob/master/CONTRIBUTING.md) and the instructions about a [basic recommended setup](https://github.com/asyncapi/community/blob/master/git-workflow.md) useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115).`; + const prMessage = `Welcome to AsyncAPI. Thanks a lot for creating your first pull request. Please check out our [contributors guide](https://github.com/asyncapi/community/blob/master/CONTRIBUTING.md) useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115).`; + if (!issueMessage && !prMessage) { + throw new Error('Action must have at least one of issue-message or pr-message set'); + } + const isIssue = !!context.payload.issue; + let isFirstContribution; + if (isIssue) { + const query = `query($owner:String!, $name:String!, $contributer:String!) { + repository(owner:$owner, name:$name){ + issues(first: 1, filterBy: {createdBy:$contributer}){ + totalCount + } + } + }`; + const variables = { + owner: context.repo.owner, + name: context.repo.repo, + contributer: context.payload.sender.login + }; + const { repository: { issues: { totalCount } } } = await github.graphql(query, variables); + isFirstContribution = totalCount === 1; + } else { + const query = `query($qstr: String!) { + search(query: $qstr, type: ISSUE, first: 1) { + issueCount + } + }`; + const variables = { + "qstr": `repo:${context.repo.owner}/${context.repo.repo} type:pr author:${context.payload.sender.login}`, + }; + const { search: { issueCount } } = await github.graphql(query, variables); + isFirstContribution = issueCount === 1; + } + + if (!isFirstContribution) { + console.log(`Not the users first contribution.`); + return; + } + const message = isIssue ? issueMessage : prMessage; + // Add a comment to the appropriate place + if (isIssue) { + const issueNumber = context.payload.issue.number; + console.log(`Adding message: ${message} to issue #${issueNumber}`); + await github.rest.issues.createComment({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + issue_number: issueNumber, + body: message + }); + } + else { + const pullNumber = context.payload.pull_request.number; + console.log(`Adding message: ${message} to pull request #${pullNumber}`); + await github.rest.pulls.createReview({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + pull_number: pullNumber, + body: message, + event: 'COMMENT' + }); + } diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..aa30dea0 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,20 @@ +# This file provides an overview of code owners in this repository. + +# Each line is a file pattern followed by one or more owners. +# The last matching pattern has the most precedence. +# For more details, read the following article on GitHub: https://help.github.com/articles/about-codeowners/. + +# The default owners are automatically added as reviewers when you open a pull request unless different owners are specified in the file. + +* @derberg @fmvilas @dalelane @smoya @char0n @asyncapi-bot-eve + +/anypointmq/ @mboss37 @GeraldLoeffler +/ibmmq/ @rcoppen +/jms/ @rcoppen @SrfHead +/kafka/ @lbroudoux @dalelane +/googlepubsub/ @whitlockjc +/solace/ @damaru-inc @CameronRushton +/pulsar/ @VisualBean +/sns/ @dpwdec @iancooper +/sqs/ @dpwdec @iancooper +*.json @KhudaDad414 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..638f7334 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at fmvilas@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..1334921c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,79 @@ +# Contributing to AsyncAPI +We love your input! We want to make contributing to this project as easy and transparent as possible. + +## Contribution recogniton + +We use [All Contributors](https://allcontributors.org/docs/en/specification) specification to handle recognitions. For more details read [this](https://github.com/asyncapi/community/blob/master/recognize-contributors.md) document. + +## Summary of the contribution flow + +The following is a summary of the ideal contribution flow. Please, note that Pull Requests can also be rejected by the maintainers when appropriate. + +``` + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ + β”‚ Open an issue β”‚ + β”‚ (a bug report or a β”‚ + β”‚ feature request) β”‚ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + ⇩ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ + β”‚ Open a Pull Request β”‚ + β”‚ (only after issue β”‚ + β”‚ is approved) β”‚ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + ⇩ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ + β”‚ Your changes will β”‚ + β”‚ be merged and β”‚ + β”‚ published on the next β”‚ + β”‚ release β”‚ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Code of Conduct +AsyncAPI has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](./CODE_OF_CONDUCT.md) so that you can understand what sort of behaviour is expected. + +## Our Development Process +We use Github to host code, to track issues and feature requests, as well as accept pull requests. + +## Issues +[Open an issue](https://github.com/asyncapi/asyncapi/issues/new) **only** if you want to report a bug or a feature. Don't open issues for questions or support, instead join our [Slack workspace](https://www.asyncapi.com/slack-invite) and ask there. Don't forget to follow our [Slack Etiquette](https://github.com/asyncapi/community/blob/master/slack-etiquette.md) while interacting with community members! It's more likely you'll get help, and much faster! + +## Bug Reports and Feature Requests + +Please use our issues templates that provide you with hints on what information we need from you to help you out. + +## Pull Requests + +**Please, make sure you open an issue before starting with a Pull Request, unless it's a typo or a really obvious error.** Pull requests are the best way to propose changes to the specification. Get familiar with our document that explains [Git workflow](https://github.com/asyncapi/community/blob/master/git-workflow.md) used in our repositories. + +## Conventional commits + +Our repositories follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) specification. Releasing to GitHub and NPM is done with the support of [semantic-release](https://semantic-release.gitbook.io/semantic-release/). + +Pull requests should have a title that follows the specification, otherwise, merging is blocked. If you are not familiar with the specification simply ask maintainers to modify. You can also use this cheatsheet if you want: + +- `fix: ` prefix in the title indicates that PR is a bug fix and PATCH release must be triggered. +- `feat: ` prefix in the title indicates that PR is a feature and MINOR release must be triggered. +- `docs: ` prefix in the title indicates that PR is only related to the documentation and there is no need to trigger release. +- `chore: ` prefix in the title indicates that PR is only related to cleanup in the project and there is no need to trigger release. +- `test: ` prefix in the title indicates that PR is only related to tests and there is no need to trigger release. +- `refactor: ` prefix in the title indicates that PR is only related to refactoring and there is no need to trigger release. + +What about MAJOR release? just add `!` to the prefix, like `fix!: ` or `refactor!: ` + +Prefix that follows specification is not enough though. Remember that the title must be clear and descriptive with usage of [imperative mood](https://chris.beams.io/posts/git-commit/#imperative). + +Happy contributing :heart: + +## License +When you submit changes, your submissions are understood to be under the same [Apache 2.0 License](https://github.com/asyncapi/asyncapi/blob/master/LICENSE) that covers the project. Feel free to [contact the maintainers](https://www.asyncapi.com/slack-invite) if that's a concern. + +## References +This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/master/CONTRIBUTING.md). \ No newline at end of file diff --git a/LICENSE b/LICENSE index 0bddb9b7..ebfe05f2 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019 Francisco MΓ©ndez Vilas + Copyright The Linux Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index f39c8416..96e67ce1 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ This repository contains the specifications for each AsyncAPI protocol binding. * [AMQP binding](./amqp) * [AMQP 1.0 binding](./amqp1) +* [Google Cloud Pub/Sub binding](./googlepubsub) * [HTTP binding](./http) * [IBM MQ binding](./ibmmq) * [JMS binding](./jms) @@ -13,8 +14,10 @@ This repository contains the specifications for each AsyncAPI protocol binding. * [MQTT binding](./mqtt) * [MQTT5 binding](./mqtt5) * [NATS binding](./nats) +* [Pulsar](./pulsar) * [Redis binding](./redis) * [SNS binding](./sns) +* [Solace binding](./solace) * [SQS binding](./sqs) * [STOMP binding](./stomp) -* [WebSockets binding](./websockets) \ No newline at end of file +* [WebSockets binding](./websockets) diff --git a/anypointmq/README.md b/anypointmq/README.md new file mode 100644 index 00000000..a56540a4 --- /dev/null +++ b/anypointmq/README.md @@ -0,0 +1,220 @@ +# Anypoint MQ Bindings + +This document defines how to describe Anypoint MQ-specific information in AsyncAPI documents. + +[Anypoint MQ](https://docs.mulesoft.com/mq/) is MuleSoft's multi-tenant, cloud messaging service and is fully integrated with [Anypoint Platform](https://www.mulesoft.com/platform/enterprise-integration). + + +## Versions + +The version of this bindings specification is `0.0.1`. +This is also the `bindingVersion` for all binding objects defined by this specification. +In any given binding object, `latest` MAY alternatively be used to refer to the currently latest published version of this bindings specification. + +## Terminology + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this bindings specification are to be interpreted as described in IETF [RFC2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Protocol + +These bindings use the `anypointmq` [protocol](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#definitionsProtocol) in AsyncAPI documents to denote connections to and interactions with Anypoint MQ message brokers. + +The Anypoint MQ protocol is based on invocations of the [Anypoint MQ Broker REST API](https://anypoint.mulesoft.com/exchange/portals/anypoint-platform/f1e97bc6-315a-4490-82a7-23abe036327a.anypoint-platform/anypoint-mq-broker/). + +## Server Object + +The fields of the standard [Server Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#serverObject) are constrained and interpreted as follows: + +Server Object Field Name | Values for Anypoint MQ Protocol | Description +---|:---|:--- +`protocol` | `anypointmq` | **REQUIRED**. MUST be `anypointmq` for the scope of this specification. +`url` | e.g., `https://mq-us-east-1.anypoint.mulesoft.com/api` | **REQUIRED**. MUST be the endpoint URL of the Anypoint MQ Broker REST API _excluding_ the final major version indicator (e.g., `v1`). Valid examples are `https://mq-us-east-1.anypoint.mulesoft.com/api` and `https://mq-eu-central-1.eu1.anypoint.mulesoft.com/api` (and _not_ `https://.../api/v1`). +`protocolVersion` | e.g., `v1` | **OPTIONAL**, defaults to `v1`. If present MUST be the major version indicator of the Anypoint MQ Broker REST API omitted from the `url`, e.g. `v1`. +`security` | suitably configured OAuth 2.0 client credentials grant type | **REQUIRED**. Authentication against the MuleSoft-hosted Anypoint MQ message brokers uses the OAuth 2.0 client credentials grant type. At runtime, the client ID and client secret values of an Anypoint MQ client app must be supplied. Also, the OAuth 2.0 scopes are currently not client-configurable. The `security` field of the server object MUST correctly match these constraints. + +Note that the choice of a particular Anypoint MQ client app (via its client ID and secret) decides the Anypoint Platform organization ID and environment (ID) to be those in which this client app is defined. See the [Anypoint MQ documentation](https://docs.mulesoft.com/mq/mq-client-apps) for details on how to configure Anypoint MQ client apps. + + +## Server Binding Object + +This object MUST NOT contain any properties. Its name is reserved for future use. + + +## Channel Binding Object + +The Anypoint MQ [Channel Binding Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#channel-bindings-object) is defined by a [JSON Schema](json_schemas/channel.json), which defines these fields: + +Field Name | Type | Description +---|:---:|--- +`destination` | string | **OPTIONAL**, defaults to the channel name. The destination (queue or exchange) name for this channel. SHOULD only be specified if the channel name differs from the actual destination name, such as when the channel name is not a valid destination name in Anypoint MQ. +`destinationType` | string | **OPTIONAL**, defaults to `queue`. The type of destination, which MUST be either `exchange` or `queue` or `fifo-queue`. SHOULD be specified to document the messaging model (publish/subscribe, point-to-point, strict message ordering) supported by this channel. +`bindingVersion` | string | **OPTIONAL**, defaults to `latest`. The version of this binding. + +Note that an Anypoint MQ exchange can only be sent to, not received from. To receive messages sent to an exchange, [an intermediary queue must be defined and bound to the exchange](https://docs.mulesoft.com/mq/mq-understanding#message-exchanges). In this bindings specification, these intermediary queues are not exposed in the AsyncAPI document. Instead, it is simply assumed that whenever messages must be received from an exchange, such an intermediary queue is involved yet invisible in the AsyncAPI document. + +### Examples + +The following example shows a `channels` object with two channels, the second having a channel binding object for `anypointmq`: + +```yaml +channels: + user/signup: + description: | + This application receives command messages from this channel about users to sign up. + Minimal configuration, omitting a channel binding object. + publish: + #... + user/signedup: + description: | + This application sends events to this channel about users that have signed up. + Explicitly provides a channel binding object. + bindings: + anypointmq: + destination: user-signup-exchg + destinationType: exchange + bindingVersion: '0.0.1' + subscribe: + #... +``` + + +## Operation Binding Object + +This object MUST NOT contain any properties. Its name is reserved for future use. + + +## Message Binding Object + +The Anypoint MQ [Message Binding Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#message-bindings-object) is defined by a [JSON Schema](json_schemas/message.json), which defines these fields: + +Field Name | Type | Description +---|:---:|--- +`headers` | [Schema Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#schemaObject) | **OPTIONAL**. A Schema object containing the definitions for Anypoint MQ-specific headers (so-called protocol headers). This schema MUST be of type `object` and have a `properties` key. Examples of Anypoint MQ protocol headers are `messageId` and `messageGroupId`. +`bindingVersion` | string | **OPTIONAL**, defaults to `latest`. The version of this binding. + +Note that application headers must be specified in the [`headers` field of the standard Message Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#messageObjectHeaders) and are transmitted in the [`properties` section of the Anypoint MQ message](https://anypoint.mulesoft.com/exchange/portals/anypoint-platform/f1e97bc6-315a-4490-82a7-23abe036327a.anypoint-platform/anypoint-mq-broker/). +In contrast, protocol headers such as `messageId` must be specified in the [`headers` field of the message binding object](#messageBindingObjectHeaders) and are transmitted in the [`headers` section of the Anypoint MQ message](https://anypoint.mulesoft.com/exchange/portals/anypoint-platform/f1e97bc6-315a-4490-82a7-23abe036327a.anypoint-platform/anypoint-mq-broker/). + +### Examples + +The following example shows a `channels` object with two channels, each having one operation (`subscribe` or `publish`) with one message. Only the message of the `subscribe` operation has a message binding object for `anypointmq`: + +```yaml +channels: + user/signup: + publish: + message: + #... + user/signedup: + subscribe: + message: + headers: + type: object + properties: + correlationId: + description: Correlation ID set by application + type: string + payload: + type: object + properties: + #... + correlationId: + description: Correlation ID is specified as a header and transmitted in the Anypoint MQ message properties section + location: $message.header#/correlationId + bindings: + anypointmq: + headers: + type: object + properties: + messageId: + type: string + bindingVersion: '0.0.1' +``` + +## Complete Example + +The following is a complete, simple AsyncAPI document illustrating the usage of all binding objects defined in this bindings specification, with all their fields. + +```yaml +asyncapi: '2.0.0' +info: + title: Example with Anypoint MQ + version: '0.0.1' + +servers: + development: + protocol: anypointmq + protocolVersion: v1 + url: https://mq-us-east-1.anypoint.mulesoft.com/api + description: | + Anypoint MQ broker for development, in the US East (N. Virginia) runtime plane + under management of the US control plane. + security: + - oauthDev: [] + production: + protocol: anypointmq + protocolVersion: v1 + url: https://mq-eu-central-1.eu1.anypoint.mulesoft.com/api + description: | + Anypoint MQ broker for production, in the EU Central (Frankfurt) runtime plane + under management of the EU control plane. + security: + - oauthProd: [] + bindings: + anypointmq: + bindingVersion: '0.0.1' + +channels: + user/signup: + description: | + This application receives command messages from this channel about users to sign up. + bindings: + anypointmq: + destination: user-signup-queue + destinationType: fifo-queue + bindingVersion: '0.0.1' + publish: + operationId: signUpUser + description: | + This application receives command messages via this operation about users to sign up. + message: + contentType: application/json + headers: + type: object + properties: + correlationId: + description: Correlation ID set by application + type: string + payload: + type: object + properties: + username: + type: string + minLength: 3 + correlationId: + description: Correlation ID is specified as a header and transmitted in the Anypoint MQ message properties section + location: $message.header#/correlationId + bindings: + anypointmq: + headers: + type: object + properties: + messageId: + type: string + bindingVersion: '0.0.1' + +components: + securitySchemes: + oauthDev: + type: oauth2 + flows: + clientCredentials: + tokenUrl: https://mq-us-east-1.anypoint.mulesoft.com/api/v1/authorize + scopes: {} + oauthProd: + type: oauth2 + flows: + clientCredentials: + tokenUrl: https://mq-eu-central-1.eu1.anypoint.mulesoft.com/api/v1/authorize + scopes: {} +``` diff --git a/anypointmq/json_schemas/channel.json b/anypointmq/json_schemas/channel.json new file mode 100644 index 00000000..eece0c50 --- /dev/null +++ b/anypointmq/json_schemas/channel.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/anypointmq/channel.json", + "title": "Channel Schema", + "description": "This object contains configuration for describing an Anypoint MQ exchange, queue, or FIFO queue as an AsyncAPI channel. This objects only contains configuration that can not be provided in the AsyncAPI standard channel object.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "destination": { + "type": "string", + "description": "The destination (queue or exchange) name for this channel. SHOULD only be specified if the channel name differs from the actual destination name, such as when the channel name is not a valid destination name in Anypoint MQ. Defaults to the channel name." + }, + "destinationType": { + "type": "string", + "enum": ["exchange", "queue", "fifo-queue"], + "default": "queue", + "description": "The type of destination. SHOULD be specified to document the messaging model (publish/subscribe, point-to-point, strict message ordering) supported by this channel." + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.0.1" + ], + "description": "The version of this binding. If omitted, 'latest' MUST be assumed." + } + + }, + "examples": [ + { + "destination": "user-signup-exchg", + "destinationType": "exchange", + "bindingVersion": "0.0.1" + } + ] +} diff --git a/anypointmq/json_schemas/message.json b/anypointmq/json_schemas/message.json new file mode 100644 index 00000000..fa63cb5d --- /dev/null +++ b/anypointmq/json_schemas/message.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/anypointmq/message.json", + "title": "Message Schema", + "description": "This object contains configuration for describing an Anypoint MQ message as an AsyncAPI message. This objects only contains configuration that can not be provided in the AsyncAPI standard message object.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "headers": { + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/schema", + "description": "A Schema object containing the definitions for Anypoint MQ-specific headers (protocol headers). This schema MUST be of type 'object' and have a 'properties' key. Examples of Anypoint MQ protocol headers are 'messageId' and 'messageGroupId'." + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.0.1" + ], + "description": "The version of this binding. If omitted, 'latest' MUST be assumed." + } + + }, + "examples": [ + { + "headers": { + "type": "object", + "properties": { + "messageId": { + "type": "string" + } + } + }, + "bindingVersion": "0.0.1" + } + ] +} diff --git a/googlepubsub/README.md b/googlepubsub/README.md new file mode 100644 index 00000000..6b691d09 --- /dev/null +++ b/googlepubsub/README.md @@ -0,0 +1,170 @@ +# Google Cloud Pub/Sub Bindings + +This document defines how to describe Google Cloud Pub/Sub specific information with AsyncAPI. + + + +## Version + +Current version is `0.1.0`. + + + +## Channel Binding Object + +The `Channel Bindings Object` is used to describe the Google Cloud Pub/Sub specific +[Topic](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics/create) details with AsyncAPI. + +Field Name | Type | Description +---|---|--- +`bindingVersion`|String|The current version is `0.1.0` +`labels`|Object|An object of key-value pairs _(These are used to categorize Cloud Resources like Cloud Pub/Sub Topics.)_ +`messageRetentionDuration`|String|Indicates the minimum duration to retain a message after it is published to the topic _(Must be a valid [Duration](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Duration).)_ +`messageStoragePolicy`|[Message Storage Policy Object](#message-storage-policy-object)|Policy constraining the set of Google Cloud Platform regions where messages published to the topic may be stored +`schemaSettings`|[Schema Settings Object](#schema-settings-object)|Settings for validating messages published against a schema +`topic`|String|The Google Cloud Pub/Sub Topic name + + + +### Message Storage Policy Object + +The `Message Storage Policy Object` is used to describe the Google Cloud Pub/Sub +[MessageStoragePolicy](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics#MessageStoragePolicy) +Object with AsyncAPI. + +Field Name | Type | Description +---|---|--- +`allowedPersistenceRegions`|String[]|A list of IDs of GCP regions where messages that are published to the topic may be persisted in storage + + + +### Schema Settings Object + +The `Schema Settings Object` is used to describe the Google Cloud Pub/Sub +[SchemaSettings](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics#SchemaSettings) Object with +AsyncAPI. + +Field Name | Type | Description +---|---|--- +`encoding`|String|The encoding of the message _(Must be one of the possible [Encoding](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics#encoding) values.)_ +`firstRevisionId`|String|The minimum _(inclusive)_ revision allowed for validating messages +`lastRevisionId`|String|The maximum _(inclusive)_ revision allowed for validating messages +`name`|String|The name of the schema that messages published should be validated against _(The format is `projects/{project}/schemas/{schema}`.)_ + + + +### Example + +```yaml +# ... +channels: + topic-avro-schema: + bindings: + googlepubsub: + topic: projects/your-project/topics/topic-avro-schema + schemaSettings: + encoding: json + name: projects/your-project/schemas/message-avro +# ... + topic-proto-schema: + bindings: + googlepubsub: + topic: projects/your-project/topics/topic-proto-schema + messageRetentionDuration: 86400s + messageStoragePolicy: + allowedPersistenceRegions: + - us-central1 + - us-central2 + - us-east1 + - us-east4 + - us-east5 + - us-east7 + - us-south1 + - us-west1 + - us-west2 + - us-west3 + - us-west4 + schemaSettings: + encoding: binary + name: projects/your-project/schemas/message-proto +# ... +``` + + + +## Message Binding Object + +The `Message Binding Object` is used to describe the Google Cloud Pub/Sub specific +[PubsubMessage](https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage) details, alongside with pertintent +parts of the Google Cloud Pub/Sub +[Schema](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.schemas#Schema) +Object, with AsyncAPI. + +Field Name | Type | Description +---|---|--- +`bindingVersion`|String|The current version is `0.1.0` +`attributes`|Object|Attributes for this message _(If this field is empty, the message must contain non-empty data. This can be used to filter messages on the subscription.)_ +`orderingKey`|String|If non-empty, identifies related messages for which publish order should be respected _(For more information, see [ordering messages](https://cloud.google.com/pubsub/docs/ordering).)_ +`schema`|[Schema Definition Object](#schema-definition-object)|Describes the schema used to validate the payload of this message + + + +### Schema Definition Object + +The `Schema Definition Object` is used to describe the Google Cloud Pub/Sub +[Schema]([Schema](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.schemas#Schema)) Object with AsyncAPI. +While some of this information could be, or is, described using native AsyncAPI, for consistency it makes sense to +provide this information here at all times, especially for cases where AsyncAPI does not natively support describing +payloads using a supported Google Cloud Pub/Sub schema format like Protobuf. + +Field Name | Type | Description +---|---|--- +`name`|String|The name of the schema +`type`|String|The type of the schema + + + +### Example + +```yaml +# ... +components: + messages: + messageAvro: + bindings: + googlepubsub: + schema: + name: projects/your-project/schemas/message-avro + type: avro + contentType: application/json + name: MessageAvro + payload: + fields: + - name: message + type: string + name: Message + type: record + schemaFormat: application/vnd.apache.avro+yaml;version=1.9.0 + messageProto: + bindings: + googlepubsub: + schema: + name: projects/your-project/schemas/message-proto + type: protobuf + contentType: application/octet-stream + name: MessageProto + payload: true +# ... +``` + + + +## Operation Binding Object + +This object MUST NOT contain any properties. Its name is reserved for future use. + + + +## Server Binding Object + +This object MUST NOT contain any properties. Its name is reserved for future use. diff --git a/googlepubsub/json_schemas/channel.json b/googlepubsub/json_schemas/channel.json new file mode 100644 index 00000000..02596c39 --- /dev/null +++ b/googlepubsub/json_schemas/channel.json @@ -0,0 +1,82 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/googlepubsub/channel.json", + "title": "Cloud Pub/Sub Channel Schema", + "description": "This object contains information about the channel representation for Google Cloud Pub/Sub.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "bindingVersion": { + "type": "string", + "enum": [ + "0.1.0" + ], + "description": "The version of this binding." + }, + "labels": { + "type": "object" + }, + "messageRetentionDuration": { + "type": "string" + }, + "messageStoragePolicy": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowedPersistenceRegions": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "schemaSettings": { + "type": "object", + "additionalItems": false, + "properties": { + "encoding": { + "type": "string" + }, + "firstRevisionId": { + "type": "string" + }, + "lastRevisionId": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": ["encoding", "name"] + }, + "topic": { + "type": "string" + } + }, + "required": ["schemaSettings", "topic"], + "examples": [ + { + "labels": { + "label1": "value1", + "label2": "value2" + }, + "messageRetentionDuration": "86400s", + "messageStoragePolicy": { + "allowedPersistenceRegions": [ + "us-central1", + "us-east1" + ] + }, + "schemaSettings": { + "encoding": "json", + "name": "projects/your-project-id/schemas/your-schema" + } + } + ] +} \ No newline at end of file diff --git a/googlepubsub/json_schemas/message.json b/googlepubsub/json_schemas/message.json new file mode 100644 index 00000000..a647d743 --- /dev/null +++ b/googlepubsub/json_schemas/message.json @@ -0,0 +1,55 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/googlepubsub/message.json", + "title": "Cloud Pub/Sub Channel Schema", + "description": "This object contains information about the message representation for Google Cloud Pub/Sub.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "bindingVersion": { + "type": "string", + "enum": [ + "0.1.0" + ], + "description": "The version of this binding." + }, + "attributes": { + "type": "object" + }, + "orderingKey": { + "type": "string" + }, + "schema": { + "type": "object", + "additionalItems": false, + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": ["name", "type"] + } + }, + "examples": [ + { + "schema": { + "name": "projects/your-project-id/schemas/your-avro-schema-id", + "type": "avro" + } + }, + { + "schema": { + "name": "projects/your-project-id/schemas/your-protobuf-schema-id", + "type": "protobuf" + } + } + ] +} \ No newline at end of file diff --git a/http/README.md b/http/README.md index e036b4b5..fbdda833 100644 --- a/http/README.md +++ b/http/README.md @@ -33,7 +33,7 @@ This object MUST NOT contain any properties. Its name is reserved for future use Field Name | Type | Description ---|:---:|--- -`type` | string | **Required**. Type of operation. Its value MUST be either `request` or `response`. +`type` | string | **REQUIRED**. Type of operation. Its value MUST be either `request` or `response`. `method` | string | When `type` is `request`, this is the HTTP method, otherwise it MUST be ignored. Its value MUST be one of `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`, `CONNECT`, and `TRACE`. `query` | [Schema Object][schemaObject] | A Schema object containing the definitions for each query parameter. This schema MUST be of type `object` and have a `properties` key. `bindingVersion` | string | The version of this binding. If omitted, "latest" MUST be assumed. diff --git a/ibmmq/README.md b/ibmmq/README.md index 57cc75e5..f3496f7e 100644 --- a/ibmmq/README.md +++ b/ibmmq/README.md @@ -142,6 +142,8 @@ servers: bindingVersion: 0.1.0 ``` + + ## Channel Binding Object @@ -202,8 +204,17 @@ channels: bindingVersion: 0.1.0 ``` + + +## Operation Binding Object + +This object MUST NOT contain any properties. Its name is reserved for future use. + + + + ## Message Binding Object @@ -252,7 +263,7 @@ channels: bindingVersion: 0.1.0 ``` -# AsyncAPI example with IBM MQ binding +# AsyncAPI example with IBM MQ binding ##### Example for AsyncAPI user signup diff --git a/ibmmq/json_schemas/channel.json b/ibmmq/json_schemas/channel.json index 5075486c..62154b9c 100644 --- a/ibmmq/json_schemas/channel.json +++ b/ibmmq/json_schemas/channel.json @@ -84,9 +84,6 @@ "properties": { "destinationType": { "const": "topic" } }, - "required": [ - "topic" - ], "not": { "required": [ "queue" diff --git a/jms/README.md b/jms/README.md index 4e4d327d..28f2b0d6 100644 --- a/jms/README.md +++ b/jms/README.md @@ -3,25 +3,97 @@ This document defines how to describe JMS-specific information on AsyncAPI. +## Versions -## Version +The version of this bindings specification is `0.0.1`. +This is also the `bindingVersion` for all binding objects defined by this specification. +In any given binding object, `latest` MAY alternatively be used to refer to the currently latest published version of this bindings specification. -Current version is `0.1.0`. +## Terminology + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this bindings specification are to be interpreted as described in IETF [RFC2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Protocol + +These bindings use the `jms` [protocol](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#definitionsProtocol) in AsyncAPI documents to denote connections to and interactions with JMS message brokers. + +JMS is not technically a protocol, rather it is an API. A JMS Provider implements the JMS API and may define a protocol for implementing JMS API operations. Regardless, for the purposes of AsyncAPI we can treat it like a "protocol" to enable AsyncAPI definitions that are somewhat portable between various JMS Providers. If necessary, the user is free to combine this binding with other bindings that implement a JMS Provider (e.g. [Apache Pulsar](https://github.com/asyncapi/bindings/tree/master/pulsar), [Amazon SQS](https://github.com/asyncapi/bindings/tree/master/sqs), [IBM MQ](https://github.com/asyncapi/bindings/tree/master/ibmmq), etc.) to detail JMS Provider specific configuration. + +**NOTE** that from protocol version 3.0, this binding is compatible with [Jakarta Messaging](https://jakarta.ee/specifications/messaging). + +## Server Object + +The fields of the standard [Server Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#serverObject) are constrained and interpreted as follows: + +Server Object Field Name | Values for JMS Protocol | Description +---|:---|:--- +`protocol` | `jms` | **REQUIRED**. MUST be `jms` for the scope of this specification. +`url` | e.g., `jms://host:port` | **REQUIRED**. MUST be a URL containing the hostname and port of a JMS Broker. +`protocolVersion` | e.g., `3.1` | **OPTIONAL**, defaults to `3.1`. If present MUST be the version indicator of the JMS API. Valid values are `1.0`, `1.0.1`, `1.0.1a`, `1.0.2`, `1.0.2a`, `1.0.2b`, `1.1`, `2.0`, `2.0a`, `2.1`, or `3.0`, `3.1.`. ## Server Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. - +The JMS [Server Binding Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#server-bindings-object) is defined by a [JSON Schema](json_schemas/server.json), which defines these fields: + +Field Name | Type | Description +---|:---:|--- +`jmsConnectionFactory` | string | **REQUIRED**. The classname of the [ConnectionFactory](https://docs.oracle.com/javaee/7/api/javax/jms/ConnectionFactory.html) implementation for the JMS Provider. +`properties` | [Schema Array](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#schemaArray) | **OPTIONAL**. Additional properties to set on the JMS ConnectionFactory implementation for the JMS Provider. +`clientID` | string | **OPTIONAL**. A client identifier for applications that use this JMS connection factory. If the Client ID Policy is set to 'Restricted' (the default), then configuring a Client ID on the [ConnectionFactory](https://docs.oracle.com/javaee/7/api/javax/jms/ConnectionFactory.html) prevents more than one JMS client from using a connection from this factory. +`bindingVersion` | string | **OPTIONAL**, defaults to `latest`. The version of this binding. + +### Examples + +The following example shows a `servers` object with a server binding object for `jms` with JMS specific properties: + +```yaml +servers: + production: + url: jms://my-activemq-broker:61616 + protocol: jms + protocolVersion: '1.1' + description: The production ActiveMQ broker accessed via JMS. + bindings: + jms: + # JMS protocol specific server details + jmsConnectionFactory: org.apache.activemq.ActiveMQConnectionFactory + properties: + - name: disableTimeStampsByDefault + value: false + clientID: my-application-1 +``` - ## Channel Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. +The JMS [Channel Binding Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#channel-bindings-object) is defined by a [JSON Schema](json_schemas/channel.json), which defines these fields: + +Field Name | Type | Description +---|:---:|--- +`destination` | string | **OPTIONAL**, defaults to the channel name. The destination (queue) name for this channel. SHOULD only be specified if the channel name differs from the actual destination name, such as when the channel name is not a valid destination name according to the JMS Provider. +`destinationType` | string | **OPTIONAL**, defaults to `queue`. The type of destination, which MUST be either `queue`, or `fifo-queue`. SHOULD be specified to document the messaging model (point-to-point, or strict message ordering) supported by this channel. +`bindingVersion` | string | **OPTIONAL**, defaults to `latest`. The version of this binding. + +### Examples + +The following example shows a `channels` object with two channels, the second having a channel binding object for `jms`: + +```yaml +channels: + user.signup: + description: This application receives command messages from this channel about users to sign up. + bindings: + jms: + destination: user-sign-up + destinationType: fifo-queue + bindingVersion: '0.0.1' + publish: + #... +``` @@ -37,4 +109,50 @@ This object MUST NOT contain any properties. Its name is reserved for future use ## Message Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. +The JMS [Message Binding Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#message-bindings-object) is defined by a [JSON Schema](json_schemas/message.json), which defines these fields: + +Field Name | Type | Description +---|:---:|--- +`headers` | [Schema Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#schemaObject) | **OPTIONAL**. A Schema object containing the definitions for JMS specific headers (so-called protocol headers). This schema MUST be of type `object` and have a `properties` key. Examples of JMS protocol headers are `JMSMessageID`, `JMSTimestamp`, and `JMSCorrelationID`. +`bindingVersion` | string | **OPTIONAL**, defaults to `latest`. The version of this binding. + +Note that application headers must be specified in the [`headers` field of the standard Message Object](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#messageObjectHeaders) and are set as [Message Properties](https://docs.oracle.com/javaee/7/api/javax/jms/Message.html#Message%20Properties) of the JMS Message; how they are transmitted is defined by the JMS Provider and need not be considered here. +In contrast, protocol headers such as `JMSMessageID` must be specified in the [`headers` field of the message binding object](#messageBindingObjectHeaders) and are transmitted in the [`headers` section of the JMS message](https://docs.oracle.com/javaee/7/api/javax/jms/Message.html#Message%20Headers). + +### Examples + +The following example shows a `message` object with both application specific headers, and a message binding object for `jms` with JMS specific headers: + +```yaml +message: + messageId: my-message-1 + bindings: + jms: + headers: + # JMS protocol specific message headers + required: + - JMSMessageID + properties: + JMSMessageID: + name: JMSMessageID + description: A unique message identifier. This may be set by your JMS Provider on your behalf. + type: string + JMSReplyTo: + name: JMSReplyTo + description: The queue or topic that the message sender expects replies to. + type: string + headers: + # Application specific message headers + required: + - MyToken + - MyOperationID + properties: + MyToken: + name: MyToken + description: Some sort of identificaton token for the publishing application. + type: string + MyOperationID: + name: MyOperationID + description: Some sort of unique identifier for the application operation to perform. + type: string +``` diff --git a/jms/json_schemas/channel.json b/jms/json_schemas/channel.json new file mode 100644 index 00000000..4f441cc4 --- /dev/null +++ b/jms/json_schemas/channel.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/jms/channel.json", + "title": "Channel Schema", + "description": "This object contains configuration for describing a JMS queue, or FIFO queue as an AsyncAPI channel. This objects only contains configuration that can not be provided in the AsyncAPI standard channel object.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "destination": { + "type": "string", + "description": "The destination (queue) name for this channel. SHOULD only be specified if the channel name differs from the actual destination name, such as when the channel name is not a valid destination name according to the JMS Provider. Defaults to the channel name." + }, + "destinationType": { + "type": "string", + "enum": ["queue", "fifo-queue"], + "default": "queue", + "description": "The type of destination. SHOULD be specified to document the messaging model (point-to-point, or strict message ordering) supported by this channel." + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.0.1" + ], + "description": "The version of this binding. If omitted, 'latest' MUST be assumed." + } + + }, + "examples": [ + { + "destination": "user-signed-up", + "destinationType": "fifo-queue", + "bindingVersion": "0.0.1" + } + ] +} diff --git a/jms/json_schemas/message.json b/jms/json_schemas/message.json new file mode 100644 index 00000000..4c14347e --- /dev/null +++ b/jms/json_schemas/message.json @@ -0,0 +1,73 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/jms/message.json", + "title": "Message Schema", + "description": "This object contains configuration for describing a JMS message as an AsyncAPI message. This objects only contains configuration that can not be provided in the AsyncAPI standard message object.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "headers": { + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/schema", + "description": "A Schema object containing the definitions for JMS headers (protocol headers). This schema MUST be of type 'object' and have a 'properties' key. Examples of JMS protocol headers are 'JMSMessageID', 'JMSTimestamp', and 'JMSCorrelationID'." + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.0.1" + ], + "description": "The version of this binding. If omitted, 'latest' MUST be assumed." + } + + }, + "examples": [ + { + "headers": { + "type": "object", + "required": ["JMSMessageID"], + "properties": { + "JMSMessageID": { + "type": ["string", "null"], + "description": "A unique message identifier. This may be set by your JMS Provider on your behalf." + }, + "JMSTimestamp": { + "type": "integer", + "description": "The time the message was sent. This may be set by your JMS Provider on your behalf. The time the message was sent. The value of the timestamp is the amount of time, measured in milliseconds, that has elapsed since midnight, January 1, 1970, UTC." + }, + "JMSDeliveryMode": { + "type": "string", + "enum": ["PERSISTENT", "NON_PERSISTENT"], + "default": "PERSISTENT", + "description": "Denotes the delivery mode for the message. This may be set by your JMS Provider on your behalf." + }, + "JMSPriority": { + "type": "integer", + "default": 4, + "description": "The priority of the message. This may be set by your JMS Provider on your behalf." + }, + "JMSExpires": { + "type": "integer", + "description": "The time at which the message expires. This may be set by your JMS Provider on your behalf. A value of zero means that the message does not expire. Any non-zero value is the amount of time, measured in milliseconds, that has elapsed since midnight, January 1, 1970, UTC, at which the message will expire." + }, + "JMSType": { + "type": ["string", "null"], + "description": "The type of message. Some JMS providers use a message repository that contains the definitions of messages sent by applications. The 'JMSType' header field may reference a message's definition in the provider's repository. The JMS API does not define a standard message definition repository, nor does it define a naming policy for the definitions it contains. Some messaging systems require that a message type definition for each application message be created and that each message specify its type. In order to work with such JMS providers, JMS clients should assign a value to 'JMSType', whether the application makes use of it or not. This ensures that the field is properly set for those providers that require it." + }, + "JMSCorrelationID": { + "type": ["string", "null"], + "description": "The correlation identifier of the message. A client can use the 'JMSCorrelationID' header field to link one message with another. A typical use is to link a response message with its request message. Since each message sent by a JMS provider is assigned a message ID value, it is convenient to link messages via message ID, such message ID values must start with the 'ID:' prefix. Conversely, application-specified values must not start with the 'ID:' prefix; this is reserved for provider-generated message ID values." + }, + "JMSReplyTo": { + "type": "string", + "description": "The queue or topic that the message sender expects replies to." + } + } + }, + "bindingVersion": "0.0.1" + } + ] +} diff --git a/jms/json_schemas/server.json b/jms/json_schemas/server.json new file mode 100644 index 00000000..c2810f7e --- /dev/null +++ b/jms/json_schemas/server.json @@ -0,0 +1,69 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/jms/server.json", + "title": "Server Schema", + "description": "This object contains configuration for describing a JMS broker as an AsyncAPI server. This objects only contains configuration that can not be provided in the AsyncAPI standard server object.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/specificationExtension" + } + }, + "required": ["jmsConnectionFactory"], + "properties": { + "jmsConnectionFactory": { + "type": "string", + "description": "The classname of the ConnectionFactory implementation for the JMS Provider." + }, + "properties": { + "type": "array", + "items": { + "$ref": "#/schemas/property" + }, + "description": "Additional properties to set on the JMS ConnectionFactory implementation for the JMS Provider." + }, + "clientID": { + "type": "string", + "description": "A client identifier for applications that use this JMS connection factory. If the Client ID Policy is set to 'Restricted' (the default), then configuring a Client ID on the ConnectionFactory prevents more than one JMS client from using a connection from this factory." + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.0.1" + ], + "description": "The version of this binding. If omitted, 'latest' MUST be assumed." + } + + }, + "schemas": { + "property": { + "type": "object", + "required": ["name", "value"], + "properties": { + "name": { + "type": "string", + "description": "The name of a property" + }, + "value": { + "type": ["string", "boolean", "number", "null"], + "description": "The name of a property" + } + } + } + }, + "examples": [ + { + "jmsConnectionFactory": "org.apache.activemq.ActiveMQConnectionFactory", + "properties": [ + { + "name": "disableTimeStampsByDefault", + "value": false + } + ], + "clientID": "my-application-1", + "bindingVersion": "0.0.1" + } + ] + } + \ No newline at end of file diff --git a/kafka/README.md b/kafka/README.md index c3ea466f..cc75c77c 100644 --- a/kafka/README.md +++ b/kafka/README.md @@ -6,38 +6,114 @@ This document defines how to describe Kafka-specific information on AsyncAPI. ## Version -Current version is `0.1.0`. +Current version is `0.4.0`. ## Server Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. +This object contains information about the server representation in Kafka. +##### Fixed Fields + +Field Name | Type | Description | Applicability [default] | Constraints +---|:---:|:---:|:---:|--- +`schemaRegistryUrl` | string (url) | API URL for the Schema Registry used when producing Kafka messages (if a Schema Registry was used) | OPTIONAL | - +`schemaRegistryVendor` | string | The vendor of Schema Registry and Kafka serdes library that should be used (e.g. `apicurio`, `confluent`, `ibm`, or `karapace`) | OPTIONAL | MUST NOT be specified if `schemaRegistryUrl` is not specified +`bindingVersion` | string | The version of this binding. | OPTIONAL [`latest`] + +##### Example +```yaml +servers: + production: + bindings: + kafka: + schemaRegistryUrl: 'https://my-schema-registry.com' + schemaRegistryVendor: 'confluent' + bindingVersion: '0.4.0' +``` ## Channel Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. +This object contains information about the channel representation in Kafka (eg. a Kafka topic). + +##### Fixed Fields + +Field Name | Type | Description | Applicability [default] | Constraints +---|:------------------------------------------------:|:-------------------------------------------------------------------------------------------------------:|:-----------------------:|--- +`topic` | string | Kafka topic name if different from channel name. | OPTIONAL | - +`partitions` | integer | Number of partitions configured on this topic (useful to know how many parallel consumers you may run). | OPTIONAL | Must be positive +`replicas` | integer | Number of replicas configured on this topic. | OPTIONAL | MUST be positive +`topicConfiguration` | [TopicConfiguration Object](#topicConfiguration) | Topic configuration properties that are relevant for the API. | OPTIONAL | - +`bindingVersion` | string | The version of this binding. If omitted, "latest" MUST be assumed. | OPTIONAL [`latest`] | - + + +This object MUST contain only the properties defined above. + +##### Example + +This example is valid for any Confluent compatible schema registry. Here we describe the implementation using the first 4 bytes in payload to store schema identifier. + +```yaml +channels: + user-signedup: + bindings: + kafka: + topic: 'my-specific-topic-name' + partitions: 20 + replicas: 3 + topicConfiguration: + cleanup.policy: ["delete", "compact"] + retention.ms: 604800000 + retention.bytes: 1000000000 + delete.retention.ms: 86400000 + max.message.bytes: 1048588 + bindingVersion: '0.4.0' +``` + +## TopicConfiguration Object + +This objects contains information about the API relevant topic configuration in Kafka. + +Field Name | Type | Description | Applicability [default] | Constraints +---|:-------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-----------------------:|--- +`cleanup.policy` | array | The [`cleanup.policy`](https://kafka.apache.org/documentation/#topicconfigs_cleanup.policy) configuration option. | OPTIONAL | array may only contain `delete` and/or `compact` +`retention.ms` | integer | The [`retention.ms`](https://kafka.apache.org/documentation/#topicconfigs_retention.ms) configuration option. | OPTIONAL | see kafka documentation +`retention.bytes` | integer | The [`retention.bytes`](https://kafka.apache.org/documentation/#topicconfigs_retention.bytes) configuration option. | OPTIONAL | see kafka documentation +`delete.retention.ms` | integer | The [`delete.retention.ms`](https://kafka.apache.org/documentation/#topicconfigs_delete.retention.ms) configuration option. | OPTIONAL | see kafka documentation +`max.message.bytes` | integer | The [`max.message.bytes`](https://kafka.apache.org/documentation/#topicconfigs_max.message.bytes) configuration option. | OPTIONAL | see kafka documentation +This object MUST contain only the properties defined above. + +##### Example + +```yaml +topicConfiguration: + cleanup.policy: ["delete", "compact"] + retention.ms: 604800000 + retention.bytes: 1000000000 + delete.retention.ms: 86400000 + max.message.bytes: 1048588 +``` ## Operation Binding Object -This object contains information about the operation representation in Kafka. +This object contains information about the operation representation in Kafka (eg. the way to consume messages) ##### Fixed Fields -Field Name | Type | Description ----|:---:|--- -`groupId` | [Schema Object][schemaObject] | Id of the consumer group. -`clientId` | [Schema Object][schemaObject] | Id of the consumer inside a consumer group. -`bindingVersion` | string | The version of this binding. If omitted, "latest" MUST be assumed. +Field Name | Type | Description | Applicability [default] | Constraints +---|:---:|:---:|:---:|--- +`groupId` | [Schema Object][schemaObject] | Id of the consumer group. | OPTIONAL | - +`clientId` | [Schema Object][schemaObject] | Id of the consumer inside a consumer group. | OPTIONAL | - +`bindingVersion` | string | The version of this binding. If omitted, "latest" MUST be assumed. | OPTIONAL [`latest`] | - This object MUST contain only the properties defined above. @@ -46,7 +122,7 @@ This object MUST contain only the properties defined above. ```yaml channels: user-signedup: - publish: + subscribe: bindings: kafka: groupId: @@ -55,7 +131,7 @@ channels: clientId: type: string enum: ['myClientId'] - bindingVersion: '0.1.0' + bindingVersion: '0.4.0' ``` @@ -69,11 +145,32 @@ This object contains information about the message representation in Kafka. Field Name | Type | Description ---|:---:|--- -`key` | [Schema Object][schemaObject] | The message key. +`key` | [Schema Object][schemaObject] \| [AVRO Schema Object](https://avro.apache.org/docs/current/spec.html) | The message key. **NOTE**: You can also use the [reference object](https://asyncapi.io/docs/specifications/v2.4.0#referenceObject) way. +`schemaIdLocation` | string | If a Schema Registry is used when performing this operation, tells where the id of schema is stored (e.g. `header` or `payload`). | OPTIONAL | MUST NOT be specified if `schemaRegistryUrl` is not specified at the Server level +`schemaIdPayloadEncoding` | string | Number of bytes or vendor specific values when schema id is encoded in payload (e.g `confluent`/ `apicurio-legacy` / `apicurio-new`). | OPTIONAL | MUST NOT be specified if `schemaRegistryUrl` is not specified at the Server level +`schemaLookupStrategy` | string | Freeform string for any naming strategy class to use. Clients should default to the vendor default if not supplied. | OPTIONAL | MUST NOT be specified if `schemaRegistryUrl` is not specified at the Server level `bindingVersion` | string | The version of this binding. If omitted, "latest" MUST be assumed. This object MUST contain only the properties defined above. +This example is valid for any Confluent compatible schema registry. Here we describe the implementation using the first 4 bytes in payload to store schema identifier. + +```yaml +channels: + test: + publish: + message: + bindings: + kafka: + key: + type: string + enum: ['myKey'] + schemaIdLocation: 'payload' + schemaIdPayloadEncoding: '4' + bindingVersion: '0.4.0' +``` + +This is another example that describes the use if Apicurio schema registry. We describe the `apicurio-new` way of serializing without details on how it's implemented. We reference a [specific lookup strategy](https://www.apicur.io/registry/docs/apicurio-registry/2.2.x/getting-started/assembly-using-kafka-client-serdes.html#registry-serdes-concepts-strategy_registry) that may be used to retrieve schema Id from registry during serialization. ```yaml channels: @@ -85,7 +182,10 @@ channels: key: type: string enum: ['myKey'] - bindingVersion: '0.1.0' + schemaIdLocation: 'payload' + schemaIdPayloadEncoding: 'apicurio-new' + schemaLookupStrategy: 'TopicIdStrategy' + bindingVersion: '0.4.0' ``` -[schemaObject]: https://www.asyncapi.com/docs/specifications/2.0.0/#schemaObject +[schemaObject]: https://www.asyncapi.com/docs/specifications/2.4.0/#schemaObject diff --git a/kafka/json_schemas/channel.json b/kafka/json_schemas/channel.json new file mode 100644 index 00000000..b20a1452 --- /dev/null +++ b/kafka/json_schemas/channel.json @@ -0,0 +1,81 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/kafka/channel.json", + "title": "Channel Schema", + "description": "This object contains information about the channel representation in Kafka.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "topic": { + "type": "string", + "description": "Kafka topic name if different from channel name." + }, + "partitions": { + "type": "integer", + "minimum": 1, + "description": "Number of partitions configured on this topic." + }, + "replicas": { + "type": "integer", + "minimum": 1, + "description": "Number of replicas configured on this topic." + }, + "topicConfiguration" : { + "description": "Topic configuration properties that are relevant for the API.", + "type": "object", + "additionalProperties": false, + "properties": { + "cleanup.policy": { + "description": "The [`cleanup.policy`](https://kafka.apache.org/documentation/#topicconfigs_cleanup.policy) configuration option.", + "type": "array", + "items":{ + "type": "string", + "enum": ["compact", "delete"] + } + }, + "retention.ms": { + "description": "The [`retention.ms`](https://kafka.apache.org/documentation/#topicconfigs_retention.ms) configuration option.", + "type": "integer", + "minimum": -1 + }, + "retention.bytes": { + "description": "The [`retention.bytes`](https://kafka.apache.org/documentation/#topicconfigs_retention.bytes) configuration option.", + "type": "integer", + "minimum": -1 + }, + "delete.retention.ms": { + "description": "The [`delete.retention.ms`](https://kafka.apache.org/documentation/#topicconfigs_delete.retention.ms) configuration option.", + "type": "integer", + "minimum": 0 + }, + "max.message.bytes": { + "description": "The [`max.message.bytes`](https://kafka.apache.org/documentation/#topicconfigs_max.message.bytes) configuration option.", + "type": "integer", + "minimum": 0 + } + } + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.4.0" + ], + "description": "The version of this binding. If omitted, 'latest' MUST be assumed." + } + + }, + "examples": [ + { + "topic": "my-specific-topic", + "partitions": 20, + "replicas": 3, + "bindingVersion": "0.3.0" + } + ] + } + \ No newline at end of file diff --git a/kafka/json_schemas/message.json b/kafka/json_schemas/message.json index 8509ebb7..04a58158 100644 --- a/kafka/json_schemas/message.json +++ b/kafka/json_schemas/message.json @@ -6,21 +6,35 @@ "additionalProperties": false, "patternProperties": { "^x-[\\w\\d\\.\\-\\_]+$": { - "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/specificationExtension" + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" } }, "properties": { "key": { - "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/schema", + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.14.0/schemas/2.4.0.json#/definitions/schema", "description": "The message key." }, + "schemaIdLocation": { + "type": "string", + "description": "If a Schema Registry is used when performing this operation, tells where the id of schema is stored.", + "enum": ["header", "payload"] + }, + "schemaIdPayloadEncoding": { + "type": "string", + "description": "Number of bytes or vendor specific values when schema id is encoded in payload." + }, + "schemaLookupStrategy": { + "type": "string", + "description": "Freeform string for any naming strategy class to use. Clients should default to the vendor default if not supplied." + }, "bindingVersion": { "type": "string", "enum": [ - "0.1.0" + "0.4.0" ], "description": "The version of this binding. If omitted, 'latest' MUST be assumed." } + }, "examples": [ { @@ -30,7 +44,18 @@ "myKey" ] }, - "bindingVersion": "0.1.0" + "schemaIdLocation": "payload", + "schemaIdPayloadEncoding": "apicurio-new", + "schemaLookupStrategy": "TopicIdStrategy", + "bindingVersion": "0.3.0" + }, + { + "key": { + "$ref": "path/to/user-create.avsc#/UserCreate" + }, + "schemaIdLocation": "payload", + "schemaIdPayloadEncoding": "4", + "bindingVersion": "0.3.0" } ] } diff --git a/kafka/json_schemas/operation.json b/kafka/json_schemas/operation.json index 500744ca..60d14eda 100644 --- a/kafka/json_schemas/operation.json +++ b/kafka/json_schemas/operation.json @@ -7,22 +7,22 @@ "additionalProperties": false, "patternProperties": { "^x-[\\w\\d\\.\\-\\_]+$": { - "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/specificationExtension" + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" } }, "properties": { "groupId": { - "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/schema", + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/schema", "description": "Id of the consumer group." }, "clientId": { - "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/schema", + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/schema", "description": "Id of the consumer inside a consumer group." }, "bindingVersion": { "type": "string", "enum": [ - "0.1.0" + "0.4.0" ], "description": "The version of this binding. If omitted, 'latest' MUST be assumed." } @@ -42,7 +42,7 @@ "myClientId" ] }, - "bindingVersion": "0.1.0" + "bindingVersion": "0.3.0" } ] } diff --git a/kafka/json_schemas/server.json b/kafka/json_schemas/server.json new file mode 100644 index 00000000..2a09755f --- /dev/null +++ b/kafka/json_schemas/server.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/kafka/server.json", + "title": "Server Schema", + "description": "This object contains server connection information to a Kafka broker. This object contains additional information not possible to represent within the core AsyncAPI specification.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "schemaRegistryUrl": { + "type": "string", + "description": "API URL for the Schema Registry used when producing Kafka messages (if a Schema Registry was used)." + }, + "schemaRegistryVendor": { + "type": "string", + "description": "The vendor of the Schema Registry and Kafka serdes library that should be used." + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.4.0" + ], + "description": "The version of this binding." + } + }, + "examples": [ + { + "schemaRegistryUrl": "https://my-schema-registry.com", + "schemaRegistryVendor": "confluent", + "bindingVersion": "0.3.0" + } + ] +} diff --git a/mqtt/README.md b/mqtt/README.md index f1bed8a3..0161fdb9 100644 --- a/mqtt/README.md +++ b/mqtt/README.md @@ -20,7 +20,7 @@ This object contains information about the server representation in MQTT. Field Name | Type | Description ---|:---:|--- `clientId` | string | The client identifier. -`cleanSession` | boolean | Whether to create a persisten connection or not. When `false`, the connection will be persistent. +`cleanSession` | boolean | Whether to create a persistent connection or not. When `false`, the connection will be persistent. `lastWill` | object | Last Will and Testament configuration. `lastWill.topic` | string | The topic where the Last Will and Testament message will be sent. `lastWill.qos` | integer | Defines how hard the broker/client will try to ensure that the Last Will and Testament message is received. Its value MUST be either 0, 1 or 2. diff --git a/mqtt5/README.md b/mqtt5/README.md index 66e961b9..3e794ba0 100644 --- a/mqtt5/README.md +++ b/mqtt5/README.md @@ -6,16 +6,44 @@ This document defines how to describe MQTT 5-specific information on AsyncAPI. ## Version -Current version is `0.1.0`. +Current version is `0.2.0`. ## Server Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. - - +This object contains information about the server representation in MQTT5. + +##### Fixed Fields + +Field Name | Type | Description +---|:---:|--- +`sessionExpiryInterval` | [Schema Object][schemaObject] \| [Reference Object](referenceObject) \| integer | Session Expiry Interval in seconds or a Schema Object containing the definition of the interval. +`bindingVersion` | string | The version of this binding. If omitted, "latest" MUST be assumed. + +This object MUST contain only the properties defined above. + +##### Example + +```yaml +servers: + production: + bindings: + mqtt5: + sessionExpiryInterval: 60 + bindingVersion: 0.2.0 +``` +```yaml +servers: + production: + bindings: + mqtt5: + sessionExpiryInterval: + type: integer + minimum: 100 + bindingVersion: 0.2.0 +``` @@ -38,4 +66,7 @@ This object MUST NOT contain any properties. Its name is reserved for future use ## Message Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. \ No newline at end of file +This object MUST NOT contain any properties. Its name is reserved for future use. + +[schemaObject]: https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#schemaObject +[referenceObject]: https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#referenceObject diff --git a/mqtt5/json_schemas/server.json b/mqtt5/json_schemas/server.json new file mode 100644 index 00000000..22241a49 --- /dev/null +++ b/mqtt5/json_schemas/server.json @@ -0,0 +1,52 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/mqtt5/server.json", + "title": "Server Schema", + "description": "This object contains information about the server representation in MQTT5.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/specificationExtension" + } + }, + "properties": { + + "sessionExpiryInterval": { + "oneOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "$ref": "https://asyncapi.com/definitions/2.4.0/schema.json" + }, + { + "$ref": "https://asyncapi.com/definitions/2.4.0/Reference.json" + } + ], + "description": "Session Expiry Interval in seconds or a Schema Object containing the definition of the interval." + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.2.0" + ], + "description": "The version of this binding. If omitted, 'latest' MUST be assumed." + } + }, + "examples": [ + { + "sessionExpiryInterval": 60, + "bindingVersion": "0.2.0" + }, + { + "sessionExpiryInterval": { + "type": "integer", + "minimum": 100 + }, + "bindingVersion": "0.2.0" + } + ] +} + diff --git a/nats/README.md b/nats/README.md index c8e726e1..24e0dfef 100644 --- a/nats/README.md +++ b/nats/README.md @@ -8,33 +8,26 @@ This document defines how to describe NATS-specific information on AsyncAPI. Current version is `0.1.0`. - ## Server Binding Object - This object MUST NOT contain any properties. Its name is reserved for future use. - - ## Channel Binding Object - This object MUST NOT contain any properties. Its name is reserved for future use. - - ## Operation Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. - - - - +Field Name | Type | Description +---|:---:|--- +| `queue` | string | Defines the name of the queue to use. It MUST NOT exceed 255 characters. | +| `bindingVersion` | string | The version of this binding. If omitted, "latest" MUST be assumed. | + ## Message Binding Object This object MUST NOT contain any properties. Its name is reserved for future use. diff --git a/nats/json_schemas/operation.json b/nats/json_schemas/operation.json new file mode 100644 index 00000000..d98851e8 --- /dev/null +++ b/nats/json_schemas/operation.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/nats/operation.json", + "title": "Operation Schema", + "description": "This object contains information about the operation representation in NATS.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "queue": { + "type": "string", + "description": "Defines the name of the queue to use. It MUST NOT exceed 255 characters.", + "maxLength": 255 + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.1.0" + ], + "description": "The version of this binding. If omitted, 'latest' MUST be assumed." + } + }, + "examples": [ + { + "queue": "MyCustomQueue", + "bindingVersion": "0.1.0" + } + ] +} diff --git a/pulsar/README.md b/pulsar/README.md new file mode 100644 index 00000000..0a48c71f --- /dev/null +++ b/pulsar/README.md @@ -0,0 +1,86 @@ +# Pulsar Bindings +This document defines how to describe Apache Pulsar specific information with AsyncAPI. + + + +## Version + +Current version is `0.1.0`. + + +## Server Binding Object + +This object contains information about the server representation in Pulsar. + +##### Fixed Fields + +Field Name | Type | Required | Description | Default value | +---|:---:|:---:|:---|:---| +`tenant` | String | No | The pulsar tenant. If omitted, "public" MUST be assumed. | `public` | +`bindingVersion` | String | No | The version of this binding. If omitted, "latest" MUST be assumed. | `latest` | + +##### Example + +```yaml +servers: + production: + bindings: + pulsar: + tenant: contoso + bindingVersion: '0.1.0' +``` + + +## Channel Binding Object +This object contains information about the channel representation in Pulsar + +##### Fixed Fields + +Field Name | Type | Required | Description | Default value | +---|:---:|:---:|:---|:---| +`namespace` | String | Yes | The namespace the channel is associated with. | N/A | +`persistence` | String | Yes | Persistence of the topic in Pulsar. It MUST be either `persistent` or `non-persistent`. | N/A | +`compaction`| Integer | No | Topic compaction threshold given in Megabytes. | N/A | +`geo-replication` | String[] | No | A list of clusters the topic is replicated to. | N/A | +`retention` | [Retention Definition Object](#retention-definition-object) | No | Topic retention policy. | N/A | +`ttl` | Integer | No | Message time-to-live in seconds. | N/A | +`deduplication` | Boolean | No | Message deduplication. When true, it ensures that each message produced on Pulsar topics is persisted to disk only once. | N/A | +`bindingVersion` | String | No | The version of this binding. If omitted, "latest" MUST be assumed. | `latest` | + + +### Retention Definition Object +The `Retention Definition Object` is used to describe the Pulsar [Retention](https://pulsar.apache.org/docs/cookbooks-retention-expiry/) policy. + +Field Name | Type | Required | Description | Default value | +---|:---:|:---:|:---|:---| +`time`|Integer| No | Time given in Minutes. | `0` | +`size`|Integer| No |Size given in MegaBytes. | `0` | + +##### Example + +```yaml +channels: + user-signedup: + bindings: + pulsar: + namespace: 'staging' + persistence: 'persistent' + compaction: 1000 + geo-replication: + - 'us-east1' + - 'us-west1' + retention: + time: 7 + size: 1000 + ttl: 360 + deduplication: false + bindingVersion: '0.1.0' +``` + + +## Operation binding fields +This object MUST NOT contain any properties. Its name is reserved for future use. + + +## Message binding fields +This object MUST NOT contain any properties. Its name is reserved for future use. \ No newline at end of file diff --git a/pulsar/json_schemas/channel.json b/pulsar/json_schemas/channel.json new file mode 100644 index 00000000..021bf3a2 --- /dev/null +++ b/pulsar/json_schemas/channel.json @@ -0,0 +1,91 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/pulsar/channel.json", + "title": "Channel Schema", + "description": "This object contains information about the channel representation in Pulsar, which covers namespace and topic level admin configuration. This object contains additional information not possible to represent within the core AsyncAPI specification.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v4.0.0/schemas/2.5.0.json#/definitions/http://asyncapi.com/definitions/2.5.0/specificationExtension.json" + } + }, + "required": [ "namespace", "persistence" ], + "properties": { + "namespace": { + "type": "string", + "description": "The namespace, the channel is associated with." + }, + "persistence": { + "type": "string", + "enum": [ + "persistent", + "non-persistent" + ], + "description": "persistence of the topic in Pulsar." + }, + "compaction": { + "type": "integer", + "minimum": 0, + "description": "Topic compaction threshold given in MB" + }, + "geo-replication": { + "type": "array", + "description": "A list of clusters the topic is replicated to.", + "items": { + "type": "string" + } + }, + "retention": { + "type": "object", + "additionalProperties": false, + "properties": { + "time": { + "type": "integer", + "minimum": 0, + "description": "Time given in Minutes. `0` = Disable message retention." + }, + "size": { + "type": "integer", + "minimum": 0, + "description": "Size given in MegaBytes. `0` = Disable message retention." + } + } + }, + "ttl": { + "type": "integer", + "description": "TTL in seconds for the specified topic" + }, + "deduplication": { + "type": "boolean", + "description": "Whether deduplication of events is enabled or not." + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.1.0" + ], + "description": "The version of this binding. If omitted, 'latest' MUST be assumed." + } + + }, + "examples": [ + { + "namespace": "ns1", + "persistence": "persistent", + "compaction": 1000, + "retention": { + "time": 15, + "size": 1000 + }, + "ttl": 360, + "geo-replication": [ + "us-west", + "us-east" + ], + "deduplication": true, + "bindingVersion": "0.1.0" + } + ] + } + \ No newline at end of file diff --git a/pulsar/json_schemas/server.json b/pulsar/json_schemas/server.json new file mode 100644 index 00000000..4167d2d9 --- /dev/null +++ b/pulsar/json_schemas/server.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/pulsar/server.json", + "title": "Server Schema", + "description": "This object contains server information of Pulsar broker, which covers cluster and tenant admin configuration. This object contains additional information not possible to represent within the core AsyncAPI specification.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "tenant": { + "type": "string", + "description": "The pulsar tenant. If omitted, 'public' MUST be assumed." + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.1.0" + ], + "description": "The version of this binding. If omitted, 'latest' MUST be assumed." + } + }, + "examples": [ + { + "tenant": "contoso", + "bindingVersion": "0.1.0" + } + ] +} diff --git a/sns/README.md b/sns/README.md index 6a0f81d9..4ef43ec5 100644 --- a/sns/README.md +++ b/sns/README.md @@ -15,23 +15,283 @@ Current version is `0.1.0`. This object MUST NOT contain any properties. Its name is reserved for future use. - - ## Channel Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. +This object contains information about the channel representation in SNS. + +We represent an AsyncAPI Channel with a Topic in SNS. The bindings here allow definition of a topic within SNS. We provide properties on the binding that allow creation of a topic in infrastructure-as-code scenarios. Be aware that although the binding offers that flexibility, it may be more maintainable to specify properties such as SNS Access Control Policy outside of AsyncAPI. + +SNS supports many optional properties. To mark a channel as SNS, but use default values for the channel properties, just use an empty object {}. + +### Fields + +|Field Name | Type | Description| +|---|:---:|---| +| `name` | string | **Required.** The name of the topic. Can be different from the channel name to allow flexibility around AWS resource naming limitations.| +| `ordering` | [ordering](#ordering)| **Optional.** By default, we assume an unordered SNS topic. This field allows configuration of a FIFO SNS Topic. | +| `policy` |[policy](#policy) | **Optional.** The security policy for the SNS Topic | +| `tags` |Object | **Optional.** Key-value pairs that represent AWS tags on the topic. | +|`bindingVersion` | string | **Optional**, defaults to `latest`. The version of this binding.| + +### Schemas + +#### Ordering +|Field Name | Type | Description| +|---|:---:|---| +| `type` | string | **Required.** Defines the type of SNS Topic. Can be either `standard` or `FIFO`. | +| `contentBasedDeduplication` | boolean | **Optional.** Whether the de-duplication of messages should be turned on. Defaults to `false`| + +#### Policy +|Field Name | Type | Description| +|---|:---:|---| +| `statements` | [[Statement](#statement)] | **Required.** An array of Statement objects, each of which controls a permission for this topic | + +#### Statement +|Field Name | Type | Description| +|---|:---:|---| +| `effect` | string |**Required.** Either "Allow" or "Deny"| +| `principal` | string or array of string |**Required.** The AWS account or resource ARN that this statement applies to| +| `action` | string or array of string |**Required.** The SNS permission being allowed or denied e.g. sns:Publish| + +##### Examples +Just use defaults +```yaml +channels: + user-signedup: + description: A user has signed up to our service + bindings: + sns: {} +``` + +Minimal definition, just policy + +```yaml +channels: + user-signedup: + description: A user has signed up to our service + bindings: + sns: + policy: + statements: + - effect : Allow + principal: * + action: SNS:Publish +``` ## Operation Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. +This object contains information operation binding in SNS. + +We represent SNS producers via a **subscribe** Operation Object. In simple cases this may not require configuration, and can be shown as an empty SNS Binding Object i.e. {} if you need to explicitly indicate how a producer publishes to the channel. + +We represent SNS consumers via a **publish** Operation Object. These consumers need an SNS Subscription that defines how they consume from SNS i.e. the protocol that they use, and any filters applied. + +The SNS binding does not describe the receiver.If you wish to define the receiver, add a **publish** Operation Binding Object for that receiver. For example, if you send message to an SQS queue from an SNS Topic, you would add a protocol of 'sqs' and an Identifier object for the queue. That identifier could be an ARN of a queue defined outside of the scope of AsyncAPI, but if you wanted to define the receiver you would use the name of a queue defined in an SQS Binding on the **publish** Operation Binding Object. + +We support an array of consumers via the **consumers** field. This allows you to represent multiple protocols consuming an SNS Topic in one file. You may also use it for multiple consumers with the same protocol, instead of representing each consumer in a separate file. + +### Fields + +| Field Name | Type | Applies To | Description | +|---|:---:|:---:|---| +| `topic` | [identifier](#identifier) |Publish, Subscribe| **Optional.** Often we can assume that the SNS Topic is the channel name-we provide this field in case the you need to supply the ARN, or the Topic name is not the channel name in the AsyncAPI document.| +| `consumers` | [[Consumer](#consumer)] |Publish| **Required.** The protocols that listen to this topic and their endpoints.| +| `deliveryPolicy` | [deliveryPolicy](#delivery-policy) |Subscribe| **Optional.** Policy for retries to HTTP. The field is the default for HTTP receivers of the [SNS Topic](https://docs.aws.amazon.com/sns/latest/api/API_CreateTopic.html) which may be overridden by a specific consumer.| +|`bindingVersion` | string |Publish, Subscribe| **Optional**, defaults to `latest`. The version of this binding.| + +### Schemas + +#### Consumer + +| Field Name | Type | Description | +|---|:---:|---| +| `protocol` | string | **Required.** The protocol that this endpoint receives messages by. Can be `http`, `https`, `email`, `email-json`, `sms`, `sqs`, `application`, `lambda` or `firehose` | +| `endpoint` |[identifier](#identifier)| **Required.** The endpoint messages are delivered to. | +| `filterPolicy` | object | **Optional.** Only receive a subset of messages from the channel, determined by this policy. | +| `filterPolicyScope` | string | **Optional.** Determines whether the FilterPolicy applies to MessageAttributes (default) or MessageBody. | +| `rawMessageDelivery` | boolean | **Required.** If *true* AWS SNS attributes are removed from the body, and for SQS, SNS message attributes are copied to SQS message attributes. If *false* the SNS attributes are included in the body. | +| `redrivePolicy` | [redrivePolicy](#redrive-policy) | **Optional.** Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. | +| `deliveryPolicy` | [deliveryPolicy](#delivery-policy) | **Optional.** Policy for retries to HTTP. The parameter is for that [SNS Subscription](https://docs.aws.amazon.com/sns/latest/api/API_Subscribe.html) and overrides any policy on the [SNS Topic](https://docs.aws.amazon.com/sns/latest/api/API_CreateTopic.html). | +| `displayName` | string |**Optional.** The display name to use with an SMS subscription | + + +#### Delivery Policy +|Field Name | Type | Description| +|---|:---:|---| +| `minDelayTarget` | integer | **Optional.** The minimum delay for a retry in seconds | +| `maxDelayTarget` | integer | **Optional.** The maximum delay for a retry in seconds | +| `numRetries` | integer | **Optional.** The total number of retries, including immediate, pre-backoff, backoff, and post-backoff retries | +| `numNoDelayRetries` | integer | **Optional.** The number of immediate retries (with no delay) | +| `numMinDelayRetries` | integer | **Optional.** The number of immediate retries (with delay) | +| `numMaxDelayRetries` | integer | **Optional.** The number of post-backoff phase retries, with the maximum delay between retries | +| `backoffFunction` | string, one of: arithmetic, exponential, geometric or linear | **Optional.** The algorithm for backoff between retries | +| `maxReceivesPerSecond` | integer | **Optional.** The maximum number of deliveries per second, per subscription | + +#### Identifier +|Field Name | Type | Description| +|---|:---:|---| +|`url` |string| **Optional.** The endpoint is a URL | +|`email` |string| **Optional.** The endpoint is an email address | +|`phone` |string| **Optional.** The endpoint is a phone number| +|`arn` |string| **Optional.** The target is an [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html). For example, for SQS, the identifier may be an ARN, which will be of the form: ["arn:aws:sqs:{region}:{account-id}:{queueName}"](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html)| +|`name` |string| **Optional.** The endpoint is identified by a name, which corresponds to an identifying field called 'name' of a binding for that protocol on this **publish** Operation Object. For example, if the protocol is 'sqs' then the name refers to the name field **sqs** binding. We don't use $ref because we are referring, not including. | + +We provide an Identifer Object to support providing the identifier of an externally defined endpoint for this SNS *publication* to target, or an endpoint on another binding against this Operation Object (via the name field). + +#### Redrive Policy + +|Field Name | Type | Description| +|---|:---:|---| +| `deadLetterQueue` |[Identifier](#identifier)| **Required.** The SQS queue to use as a dead letter queue (DLQ). Note that you may have a Redrive Policy to put messages that cannot be delivered to an SQS queue, even if you use another protocol to consume messages from the queue, so it is defined at the level of the SNS Operation Binding Object in a Consumer Object (and is applied as part of an [SNS Subscription](https://docs.aws.amazon.com/sns/latest/dg/sns-create-subscribe-endpoint-to-topic.html)). The SQS Binding describes how to define an SQS Binding that supports defining the target SQS of the Redrive Policy. | +| `maxReceiveCount` |integer| **Optional.** The number of times a message is delivered to the source queue before being moved to the dead-letter queue. Defaults to 10. | + +### Examples + +#### SNS to SQS Pub-Sub + +[](SNS-SQS-Pub-Sub.png) + +We are producing to an SNS channel + +```yaml +channels: + user-signedup: + description: A user has signed up for our service + binding : + sns: {} # Indicates that the channel is an SNS Topic + subscribe: + operationId: sendMessage + description: send messages to the topic + bindings: + sns: + consumers: + - protocol: sqs + endpoint: + name: myQueue + rawMessageDelivery: false +``` + +We are consuming an SNS channel, using an SQS queue. A separate file specifies the producer, and has the SNS Bindings for the channel. For this reason we do not repeat the SNS binding information for the channel here, to avoid duplicated definitions diverging. Instead we just define the **publish** Operation Binding. + +In this version, the SQS queue is defined elsewhere, and we just reference via its ARN. It is worth noting that this couples the specification to the AWS *region* and *account*, which are part of the ARN, and if we moved the queue to a new region or account was this specification would need to be updated to reflect that. + + +```yaml +channels: + user-signedup: + description: A user has signed up for our service + publish: + operationId: receiveMessage + description: receive messages from the topic + bindings: + sns: + consumers: + - protocol: sqs + endpoint: + arn: arn:aws:sqs:us-west-2:123456789012:UserSignedUpQueue + rawMessageDelivery: true +``` + +We are consuming an SNS channel, using an SQS queue. A separate file specifies the producer, and has the SNS Bindings for the channel. For this reason we do not repeat the SNS binding information for the channel here, to avoid duplicated definitions diverging. Instead we just define the **publish** Operation Binding. + +In this version, the SQS queue is defined in this file, and we reference it by name. For brevity that definition is not shown here. See the SQS Binding Object for more. + +```yaml +channels: + user-signedup: + description: A user has signed up for our service + publish: + operationId: receiveMessage + description: receive messages from the topic + bindings: + sns: + consumers: + - protocol: sqs + endpoint: + name: user-signedup-queue # refers to a queue defined in this file, but not shown in this example + rawMessageDelivery: true + filterPolicy: + reason: + anything-but: password-reset + redrivePolicy: + deadLetterQueue: + name: user-signedup-queue-dlq # refers toa queue defined in this file, but not show in this example +``` + +#### SNS to HTTP Pub Sub + +[](SNS-HTTP.png) + +We are producing to an SNS channel. + +In this version, we define a default delivery policy for any HTTP based consumers + +```yaml +channels: + user-signedup: + description: A user has signed up for our service + bindings: + sns: + policy: + statements: + - effect : Allow + principal: * + action: SNS:Publish + subscribe: + operationId: sendMessage + description: send messages to the topic + bindings: + sns: + deliveryPolicy: + minDelayTarget: 1 + maxDelayTarget: 60 + numRetries: 50 + numNoDelayRetries: 3 + numMinDelayRetries: 2 + numMaxDelayRetries: 35 + backoffFunction: exponential + maxReceivesPerSecond: 10 +``` +We are consuming an SNS channel, using an HTTP endpoint, which is defined in this AsyncAPI file. For brevity we do not show an http endpoint here. The delivery policy here is defined for the http consumer and overrides any policy set by the producer +```yaml +channels: + user-signedup: + description: A user has signed up for our service + bindings: + sns: {} # Indicates that the channel is an SNS Topic, but assumes defined by producer + publish: + operationId: receiveMessage + description: receive messages from the topic + bindings: + sns: + - protocol: http + endpoint: + url: http://login.my.com/user/new + filterPolicy: + reason: + anything-but: password-reset + filterPolicyScope: MessageBody + deliveryPolicy: + minDelayTarget: 1 + maxDelayTarget: 120 + numRetries: 30 + numNoDelayRetries: 3 + numMinDelayRetries: 2 + numMaxDelayRetries: 25 + backoffFunction: exponential + maxReceivesPerSecond: 20 + redrivePolicy: + deadLetterQueue: + name: user-signedup-queue-dlq # refers toa queue defined in this file, but not show in this example +``` diff --git a/sns/SNS-HTTP.png b/sns/SNS-HTTP.png new file mode 100644 index 00000000..72e52c73 Binary files /dev/null and b/sns/SNS-HTTP.png differ diff --git a/sns/SNS-SQS-Pub-Sub.png b/sns/SNS-SQS-Pub-Sub.png new file mode 100644 index 00000000..033d6de7 Binary files /dev/null and b/sns/SNS-SQS-Pub-Sub.png differ diff --git a/sns/json_schemas/channel.json b/sns/json_schemas/channel.json new file mode 100644 index 00000000..6ab5e2f6 --- /dev/null +++ b/sns/json_schemas/channel.json @@ -0,0 +1,150 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/sns/channel.json", + "title": "Channel Schema", + "description": "This object contains information about the channel representation in SNS.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "name": { + "type": "string", + "description": "The name of the topic. Can be different from the channel name to allow flexibility around AWS resource naming limitations." + }, + "ordering": { + "$ref": "#/definitions/ordering" + }, + "policy": { + "$ref": "#/definitions/policy" + }, + "tags": { + "type": "object", + "description": "Key-value pairs that represent AWS tags on the topic." + }, + "bindingVersion": { + "type": "string", + "description": "The version of this binding.", + "default": "latest" + } + }, + "required": [ + "name" + ], + "definitions": { + "ordering": { + "type": "object", + "description": "By default, we assume an unordered SNS topic. This field allows configuration of a FIFO SNS Topic.", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "type": { + "type": "string", + "description": "Defines the type of SNS Topic.", + "enum": [ + "standard", + "FIFO" + ] + }, + "contentBasedDeduplication": { + "type": "boolean", + "description": "True to turn on de-duplication of messages for a channel." + } + }, + "required": [ + "type" + ] + }, + "policy": { + "type": "object", + "description": "The security policy for the SNS Topic.", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "statements": { + "type": "array", + "description": "An array of statement objects, each of which controls a permission for this topic", + "items": { + "$ref": "#/definitions/statement" + } + } + }, + "required": [ + "statements" + ] + }, + "statement": { + "type": "object", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "effect": { + "type": "string", + "enum": [ + "Allow", + "Deny" + ] + }, + "principal": { + "description": "The AWS account or resource ARN that this statement applies to.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "action": { + "description": "The SNS permission being allowed or denied e.g. sns:Publish", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": [ + "effect", + "principal", + "action" + ] + } + }, + "examples": [ + { + "name": "my-sns-topic", + "policy": { + "statements": [ + { + "effect": "Allow", + "principal": "*", + "action": "SNS:Publish" + } + ] + } + } + ] +} diff --git a/sns/json_schemas/operation.json b/sns/json_schemas/operation.json new file mode 100644 index 00000000..25eb5935 --- /dev/null +++ b/sns/json_schemas/operation.json @@ -0,0 +1,273 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/sns/operation.json", + "title": "Operation Schema", + "description": "This object contains information about the operation representation in SNS.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "topic": { + "$ref": "#/definitions/identifier", + "description": "Often we can assume that the SNS Topic is the channel name-we provide this field in case the you need to supply the ARN, or the Topic name is not the channel name in the AsyncAPI document." + }, + "consumers": { + "type": "array", + "description": "The protocols that listen to this topic and their endpoints.", + "items": { + "$ref": "#/definitions/consumer" + }, + "minItems": 1 + }, + "deliveryPolicy": { + "$ref": "#/definitions/deliveryPolicy", + "description": "Policy for retries to HTTP. The field is the default for HTTP receivers of the SNS Topic which may be overridden by a specific consumer." + }, + "bindingVersion": { + "type": "string", + "description": "The version of this binding.", + "default": "latest" + } + }, + "required": [ + "consumers" + ], + "definitions": { + "identifier": { + "type": "object", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "url": { + "type": "string", + "description": "The endpoint is a URL." + }, + "email": { + "type": "string", + "description": "The endpoint is an email adress." + }, + "phone": { + "type": "string", + "description": "The endpoint is a phone number." + }, + "arn": { + "type": "string", + "description": "The target is an ARN. For example, for SQS, the identifier may be an ARN, which will be of the form: arn:aws:sqs:{region}:{account-id}:{queueName}" + }, + "name": { + "type": "string", + "description": "The endpoint is identified by a name, which corresponds to an identifying field called 'name' of a binding for that protocol on this publish Operation Object. For example, if the protocol is 'sqs' then the name refers to the name field sqs binding. We don't use $ref because we are referring, not including." + } + } + }, + "consumer": { + "type": "object", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "protocol": { + "description": "The protocol that this endpoint receives messages by.", + "type": "string", + "enum": [ + "http", + "https", + "email", + "email-json", + "sms", + "sqs", + "application", + "lambda", + "firehose" + ] + }, + "endpoint": { + "description": "The endpoint messages are delivered to.", + "$ref": "#/definitions/identifier" + }, + "filterPolicy": { + "type": "object", + "description": "Only receive a subset of messages from the channel, determined by this policy. Depending on the FilterPolicyScope, a map of either a message attribute or message body to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint.", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "additionalProperties": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + }, + { + "type": "object" + } + ] + } + }, + "filterPolicyScope": { + "type": "string", + "description": "Determines whether the FilterPolicy applies to MessageAttributes or MessageBody.", + "enum": [ + "MessageAttributes", + "MessageBody" + ], + "default": "MessageAttributes" + }, + "rawMessageDelivery": { + "type": "boolean", + "description": "If true AWS SNS attributes are removed from the body, and for SQS, SNS message attributes are copied to SQS message attributes. If false the SNS attributes are included in the body." + }, + "redrivePolicy": { + "$ref": "#/definitions/redrivePolicy" + }, + "deliveryPolicy": { + "$ref": "#/definitions/deliveryPolicy", + "description": "Policy for retries to HTTP. The parameter is for that SNS Subscription and overrides any policy on the SNS Topic." + }, + "displayName": { + "type": "string", + "description": "The display name to use with an SNS subscription" + } + }, + "required": [ + "protocol", + "endpoint", + "rawMessageDelivery" + ] + }, + "deliveryPolicy": { + "type": "object", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "minDelayTarget": { + "type": "integer", + "description": "The minimum delay for a retry in seconds." + }, + "maxDelayTarget": { + "type": "integer", + "description": "The maximum delay for a retry in seconds." + }, + "numRetries": { + "type": "integer", + "description": "The total number of retries, including immediate, pre-backoff, backoff, and post-backoff retries." + }, + "numNoDelayRetries": { + "type": "integer", + "description": "The number of immediate retries (with no delay)." + }, + "numMinDelayRetries": { + "type": "integer", + "description": "The number of immediate retries (with delay)." + }, + "numMaxDelayRetries": { + "type": "integer", + "description": "The number of post-backoff phase retries, with the maximum delay between retries." + }, + "backoffFunction": { + "type": "string", + "description": "The algorithm for backoff between retries.", + "enum": [ + "arithmetic", + "exponential", + "geometric", + "linear" + ] + }, + "maxReceivesPerSecond": { + "type": "integer", + "description": "The maximum number of deliveries per second, per subscription." + } + } + }, + "redrivePolicy": { + "type": "object", + "description": "Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue.", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "deadLetterQueue": { + "$ref": "#/definitions/identifier", + "description": "The SQS queue to use as a dead letter queue (DLQ)." + }, + "maxReceiveCount": { + "type": "integer", + "description": "The number of times a message is delivered to the source queue before being moved to the dead-letter queue.", + "default": 10 + } + }, + "required": [ + "deadLetterQueue" + ] + } + }, + "examples": [ + { + "topic": { + "name": "someTopic" + }, + "consumers": [ + { + "protocol": "sqs", + "endpoint": { + "name": "someQueue" + }, + "filterPolicy": { + "store": [ + "asyncapi_corp" + ], + "event": [ + { + "anything-but": "order_cancelled" + } + ], + "customer_interests": [ + "rugby", + "football", + "baseball" + ] + }, + "filterPolicyScope": "MessageAttributes", + "rawMessageDelivery": false, + "redrivePolicy": { + "deadLetterQueue": { + "arn": "arn:aws:SQS:eu-west-1:0000000:123456789" + }, + "maxReceiveCount": 25 + }, + "deliveryPolicy": { + "minDelayTarget": 10, + "maxDelayTarget": 100, + "numRetries": 5, + "numNoDelayRetries": 2, + "numMinDelayRetries": 3, + "numMaxDelayRetries": 5, + "backoffFunction": "linear", + "maxReceivesPerSecond": 2 + } + } + ] + } + ] +} diff --git a/solace/README.md b/solace/README.md new file mode 100644 index 00000000..56d1a404 --- /dev/null +++ b/solace/README.md @@ -0,0 +1,163 @@ +# Solace Bindings + +This document defines how to describe Solace-specific information with AsyncAPI. + + + +## Version + +Current version is `0.3.0`. + + + +## Server Binding Object + +Field Name | Type | Description +---|---|--- +`bindingVersion`|String|The current version is 0.3.0 +`msgVpn`|String|The Virtual Private Network name on the Solace broker. + + + + +## Channel Binding Object + +This object MUST NOT contain any properties. Its name is reserved for future use. + + + + + +## Operation Binding Object + +We need the ability to support several bindings for each operation, see the [Example](#example) section below for details. + +Field Name | Type | Description +---|---|--- +`bindingVersion`|String|The current version is 0.3.0 +`destinations`|List of Destination Objects|Destination Objects are described next. + +### Destination Object + +Each destination has the following structure: + +| Field Name | Type | Description | +| -------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `destinationType` | Enum | 'queue' or 'topic'. If the type is queue, then the subscriber can bind to the queue, which in turn will subscribe to the topic as represented by the channel name or to the provided topicSubscriptions. | +| `deliveryMode` | Enum | 'direct' or 'persistent'. This determines the quality of service for publishing messages as documented [here.](https://docs.solace.com/Get-Started/Core-Concepts-Message-Delivery-Modes.htm) Default is 'persistent'. | +| `queue.name` | String | The name of the queue, only applicable when destinationType is 'queue'. | +| `queue.topicSubscriptions` | List of String | A list of topics that the queue subscribes to, only applicable when destinationType is 'queue'. If none is given, the queue subscribes to the topic as represented by the channel name. | +| `queue.accessType` | Enum | 'exclusive' or 'nonexclusive'. This is documented [here.](https://docs.solace.com/Messaging/Guaranteed-Msg/Endpoints.htm#Queues) Only applicable when destinationType is 'queue'. | +| `queue.maxMsgSpoolSize` | String | The maximum amount of message spool that the given queue may use. This is documented [here.](https://docs.solace.com/Messaging/Guaranteed-Msg/Message-Spooling.htm#max-spool-usage) Only applicable when destinationType is 'queue'. | +| `queue.maxTtl` | String | The maximum TTL to apply to messages to be spooled. This is documented [here.](https://docs.solace.com/Messaging/Guaranteed-Msg/Configuring-Queues.htm) Only applicable when destinationType is 'queue'. | +| `topic.topicSubscriptions` | List of String | A list of topics that the client subscribes to, only applicable when destinationType is 'topic'. If none is given, the client subscribes to the topic as represented by the channel name. | + + + +## Message Binding Object + +This object MUST NOT contain any properties. Its name is reserved for future use. + + + + + +## Example with two destinations ## + +Here is an example of when we could need two Solace destinations. + +Imagine a system where there is a schema called Person, and there are topics: + +`person/{personId}/created` + +and + +`person/{personId}/updated` + +and you have one application that receives both events. We also want each to be on its own queue. The AsyncAPI file could look like this: + +```yaml +components: + schemas: + Person: + type: string + messages: + PersonEvent: + payload: + $ref: '#/components/schemas/Person' + schemaFormat: application/vnd.aai.asyncapi+json;version=2.0.0 + contentType: application/json +channels: + 'person/{personId}/{eventType}': + publish: + bindings: + solace: + bindingVersion: 0.3.0 + destinations: + - destinationType: queue + queue: + name: CreatedHREvents + topicSubscriptions: + - person/*/created + - destinationType: queue + queue: + name: UpdatedHREvents + topicSubscriptions: + - person/*/updated + message: + $ref: '#/components/messages/PersonEvent' + parameters: + personId: + schema: + type: string + eventType: + schema: + type: string +asyncapi: 2.4.0 +info: + title: HRApp + version: 0.0.1 +``` + +The expected behaviour would be that the application binds to both queues, and each queue has its own topic subscription, one to created and one to updated events. + + +## Example with a wildcard subscription ## + +This example shows how a client could receive all the topics under `person/` using a wildcard subscription: + +```yaml +components: + schemas: + Person: + type: string + messages: + PersonEvent: + payload: + $ref: '#/components/schemas/Person' + schemaFormat: application/vnd.aai.asyncapi+json;version=2.0.0 + contentType: application/json +channels: + 'person/{personId}/{eventType}': + publish: + bindings: + solace: + bindingVersion: 0.3.0 + destinations: + - destinationType: topic + topicSubscriptions: + - person/> + message: + $ref: '#/components/messages/PersonEvent' + parameters: + personId: + schema: + type: string + eventType: + schema: + type: string +asyncapi: 2.4.0 +info: + title: HRApp + version: 0.0.1 +``` diff --git a/solace/json_schemas/operation.json b/solace/json_schemas/operation.json new file mode 100644 index 00000000..60e59fc3 --- /dev/null +++ b/solace/json_schemas/operation.json @@ -0,0 +1,114 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/solace/operation.json", + "title": "Operation Schema", + "description": "This object contains information about the operation representation in Solace.", + "type": "object", + "additionalProperties": false, + "properties": { + "destinations": { + "description": "The list of Solace destinations referenced in the operation.", + "type": "array", + "items": { + "type": "object", + "properties": { + "deliveryMode": { + "type": "string", + "enum": [ + "direct", + "persistent" + ] + } + }, + "oneOf": [ + { + "properties": { + "destinationType": { + "type": "string", + "const": "queue", + "description": "If the type is queue, then the subscriber can bind to the queue. The queue subscribes to the given topicSubscriptions. If no topicSubscriptions are provied, the queue will subscribe to the topic as represented by the channel name." + }, + "queue": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the queue" + }, + "topicSubscriptions": { + "type": "array", + "description": "The list of topics that the queue subscribes to.", + "items": { + "type": "string" + } + }, + "accessType": { + "type": "string", + "enum": [ + "exclusive", + "nonexclusive" + ] + }, + "maxTtl": { + "type": "string", + "description": "The maximum TTL to apply to messages to be spooled." + }, + "maxMsgSpoolUsage": { + "type": "string", + "description": "The maximum amount of message spool that the given queue may use" + } + } + } + } + }, + { + "properties": { + "destinationType": { + "type": "string", + "const": "topic", + "description": "If the type is topic, then the subscriber subscribes to the given topicSubscriptions. If no topicSubscriptions are provided, the client will subscribe to the topic as represented by the channel name." + }, + "topicSubscriptions": { + "type": "array", + "description": "The list of topics that the client subscribes to.", + "items": { + "type": "string" + } + } + } + } + ] + } + } + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.2.0" + ], + "description": "The version of this binding. If omitted, \"latest\" MUST be assumed." + }, + "examples": [ + { + "bindingVersion": "0.2.0", + "destinations": [ + { + "destinationType": "queue", + "queue": { + "name": "sampleQueue", + "topicSubscriptions": [ + "samples/*" + ], + "accessType": "nonexclusive" + } + }, + { + "destinationType": "topic", + "topicSubscriptions": [ + "samples/*" + ] + } + ] + } + ] +} diff --git a/solace/json_schemas/server.json b/solace/json_schemas/server.json new file mode 100644 index 00000000..e42c9d55 --- /dev/null +++ b/solace/json_schemas/server.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/solace/server.json", + "title": "Server Schema", + "description": "This object contains server connection information about the Solace broker. This object contains additional connectivity information not possible to represent within the core AsyncAPI specification.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/asyncapi-node/v2.7.7/schemas/2.0.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "msgVpn": { + "type": "string", + "description": "The name of the Virtual Private Network to connect to on the Solace broker." + }, + "bindingVersion": { + "type": "string", + "enum": [ + "0.2.0" + ], + "description": "The version of this binding." + } + }, + "examples": [ + { + "msgVpn": "ProdVPN", + "bindingVersion": "0.2.0" + } + ] +} diff --git a/sqs/README.md b/sqs/README.md index 8bccca3f..f632b66d 100644 --- a/sqs/README.md +++ b/sqs/README.md @@ -2,36 +2,236 @@ This document defines how to describe SQS-specific information on AsyncAPI. +SQS can be used both stand-alone as a point-to-point and paired with SNS and as a publish-subscribe channel (where SQS is the endpoint that SNS delivers messages to). For this reason we define a Queue schema, and reference that schema from both a Channel Binding Object and a **publish** Operation Binding Object. + +For point-to-point scenarios, use the Channel Binding Object, as producers send to the queue and consumers receive from it directly. + +For publish-subscribe scenarios, use as a **publish** Operation Binding Object, as the producer sends to SNS and the consumer receives via SQS. + ## Version Current version is `0.1.0`. - ## Server Binding Object This object MUST NOT contain any properties. Its name is reserved for future use. - - -## Channel Binding Object - -This object MUST NOT contain any properties. Its name is reserved for future use. - +## Channel Binding Object + +Use the Channel Binding Operation for Point-to-Point SQS channels. + +There are three likely scenarios for use of the Channel Binding Object: + +- One file defines both publish and subscribe operations, for example if we were implementing the work queue pattern to offload work from an HTTP API endpoint to a worker process. In this case the channel would be defined on the Channel Object in that single file. +- The producer and consumer both have an AsyncAPI specification file, and the producer is raising an event, for example interop between microservices, and the producer 'owns' the channel definition and thus has the SQS Binding on its Channel Object. +- The producer and consumer both have an AsyncAPI specification file, and the consumer receives commands, for example interop between microservices, and the consumer 'owns' the channel definition and thus has the SQS Binding on its Channel Object. + +An SQS queue can set up a Dead Letter Queue as part of a Redelivery Policy. To support this requirement, the Channel Binding Object allows you to define both a Queue Object to use as the Channel or target in a *publish* Operation and a Dead Letter Queue. You can then refer to the Dead letter Queue in the Redrive Policy using the Identifier Object and setting the *name* field to match the *name* field of your Dead Letter Queue Object. (If you define the DLQ externally, the Identifier also supports an ARN). + +### Fields +|Field Name | Type | Description| +|---|:---:|---| +| `queue` | [Queue](#queue)| **Required.** A definition of the queue that will be used as the channel. | +| `deadLetterQueue` | [Queue](#queue)| **Optional.** A definition of the queue that will be used for un-processable messages. | +|`bindingVersion` | string | **Optional**, defaults to `latest`. The version of this binding.| + +### Schemas + +#### Queue +|Field Name | Type | Description| +|---|:---:|---| +| `name` | string | **Required.** The name of the queue. When an [SNS Operation Binding Object]() references an SQS queue by name, the identifier should be the one in this field.| +| `fifoQueue` | boolean | **Required.** Is this a FIFO queue? | +| `deliveryDelay` | integer | **Optional.** The number of seconds to delay before a message sent to the queue can be received. Used to create a *delay queue*. Range is 0 to 15 minutes. Defaults to 0. | +| `visibilityTimeout` |integer| **Optional.** The length of time, in seconds, that a consumer locks a message - hiding it from reads - before it is unlocked and can be read again. Range from 0 to 12 hours (43200 seconds). Defaults to 30 seconds. | +| `receiveMessageWaitTime` |integer| **Optional.** Determines if the queue uses [short polling](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html) or [long polling](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html). Set to zero (the default) the queue reads available messages and returns immediately. Set to a non-zero integer, long polling waits the specified number of seconds for messages to arrive before returning. | +| `messageRetentionPeriod` |integer| **Optional.** How long to retain a message on the queue in seconds, unless deleted. The range is 60 (1 minute) to 1,209,600 (14 days). The default is 345,600 (4 days). | +| `redrivePolicy` | [Redrive Policy](#redrive-policy) | **Optional.** Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue.| +| `policy` |[Policy](#policy) | **Optional.** The security policy for the SQS Queue | +| `tags` |Object | **Optional.** Key-value pairs that represent AWS tags on the queue. | + +#### Identifier +|Field Name | Type | Description| +|---|:---:|---| +|`arn` |string| **Optional.** The target is an [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html). For example, for SQS, the identifier may be an ARN, which will be of the form: ["arn:aws:sqs:{region}:{account-id}:{queueName}"](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html)| +|`name` |string| **Optional.** The endpoint is identified by a name, which corresponds to an identifying field called 'name' of a binding for that protocol on this **publish** Operation Object. For example, if the protocol is 'sqs' then the name refers to the name field **sqs** binding| + +#### Policy +|Field Name | Type | Description| +|---|:---:|---| +| `Statements` | [Statement](#statement) | **Required.** An array of Statement objects, each of which controls a permission for this queue. | + +#### Redrive Policy +|Field Name | Type | Description| +|---|:---:|---| +| `deadLetterQueue` |[Identifier](#identifier)| The SQS queue to use as a dead letter queue (DLQ) | +| `maxReceiveCount` |integer| **Optional.** The number of times a message is delivered to the source queue before being moved to the dead-letter queue. Default is 10. | + +#### Statement +|Field Name | Type | Description| +|---|:---:|---| +| `effect` | string |**Required.** Either "Allow" or "Deny"| +| `principal` | string or array of string |**Required.** The AWS account or resource ARN that this statement applies to| +| `action` | string or array of string |**Required.** The SQS permission being allowed or denied e.g. sqs:ReceiveMessage | ## Operation Binding Object -This object MUST NOT contain any properties. Its name is reserved for future use. +### SQS Point-To-Point + +Because we have defined Queue as part of the Channel Binding Binding object, we do not require Binding information for the **publish** Operation Object of the **subscribe** Operation Object. You can use an empty Queue object ({}) to denote the Binding on the Operation Object, if you want to indicate the protocol used to send or receive for generation purposes such as Infrastructure As Code. + +### SNS to SQS Pub-Sub + +Use the Operation Binding Object when SQS is listening to an SNS Topic. In this case we need to define both an SQS Operation Binding Objects on the receiver **publish** Operation Object to represent the queue definition and we need to define an SNS Operation Binding Object to define the Subscription to SNS that makes your queue a receiver of that endpoint. + +Assuming you have separate AsyncAPI specifications for the producer and the consumer, we would assume the following bindings would appear for an SNS producer and an SQS consumer. +Producer: SNS Channel Binding Object, SNS **subscribe** Operation Binding Object [if required] +Consumer: SNS **publish** Operation Binding Object, SQS **publish** Operation Binding Object +- We assume that the SNS binding information only needs to be present in the producer file (although defining it in both is allowable) and any infrastructure as code dependencies can recognize this. + + +On an Operation Binding Object we support an array of Queue objects. Members of this array may be Queue Objects that define the *endpoint* field required by an [SNS Operation Object]() delivering by the SQS protocol or Queue Objects that define the Dead Letter Queue used by either the Redrive Policy of the SNS Subscription (see the SNS Binding Object) or the [Redrive Policy of the SQS Queue](#redrive-policy). The name of the Queue Object is used by an Identifier field on either the *endpoint* field of the SNS Operation Object of *deadLetterQueue* on the Redrive Policy to identify the required member of this array. + + +### Fields +|Field Name | Type | Description| +|---|:---:|---| +| `queues` | [[Queue](#queue)]| **Required.** Queue objects that are either the *endpoint* for an SNS Operation Binding Object, or the *deadLetterQueue* of the SQS Operation Binding Object | +|`bindingVersion` | string | **Optional**, defaults to `latest`. The version of this binding.| + +### Examples + +#### SQS Point-To-Point + +[](SQS-Point-To-Point.png) + +In this example, we are using SQS for a point-to-point channel. For this example, we assume that we are defining two microservices that communicate over a shared SQS channel, with the consumer receiving events over that channel and the producer owning the channel definition. + +The producer file would look like this: + +```yaml +channels: + user-signedup: + bindings: + sqs: + queue: + name: user-signedup-queue + fifoQueue: false + receiveMessageWaitTime: 4 + redrivePolicy: + deadLetterQueue: + name: user-signedup-dlq + policy: + statements: + - effect : Allow + principal: * + action: Sqs:SendMessage + - effect : Allow + principal: * + action: Sqs:ReceiveMessage + deadLetterQueue: + name: user-signedup-dlq + messageRetentionPeriod: 1209600 + fifoQueue: false + subscribe: + operationId: sendMessage + description: sends messages when a user has signed up + bindings: + sqs: {} + +``` +In this case we can minimize duplicated information by omitting the binding in our specification, and assume it is picked up from the producer file. We can use an empty object to indicate the SQS Binding on the **publish** Operation Object, if need a marker for generation, otherwise we could omit the Operation Binding Object. + +```yaml +channels: + user-signedup: + publish: + operationId: receiveMessage + description: receives a messages when a user has signed up + bindings: + sqs: {} + +``` + +#### SNS to SQS Pub-Sub + +[](SNS-SQS-Pub-Sub.png) + +In this example, we are using SNS for the channel, and SQS to receive from SNS. + + +The producer files looks like this (see the [SNS Binding]() for more). + +```yaml +channels: + user-signedup: + description: A user has signed up for our service + binding : + sns: {} # Indicates that the channel is an SNS Topic + subscribe: + operationId: sendMessage + description: send messages to the topic + bindings: + sns: + policy: + statements: + - effect : Allow + principal: * + action: SNS:Publish +``` + +And the consumer file would look like this. Note that for simplicity, we choose not to repeat the SNS Binding on the Consumer as it does not 'own' the channel. + + +```yaml +channels: + user-signedup: + description: A user has signed up for our service + publish: + operationId: receiveMessage + description: receive messages from the topic + bindings: + sns: + consumers: + - protocol: sqs + endpoint: + name: user-signedup-queue + rawMessageDelivery: true + filterPolicy: + attributes: + reason: + anything-but: password-reset + redrivePolicy: + deadLetterQueue: + name: user-signedup-queue-dlq + sqs: + queues: + - name: user-signedup-queue + fifoQueue: false + receiveMessageWaitTime: 4 + policy: + statements: + - effect : Allow + principal: * + action: Sqs:SendMessage + - effect : Allow + principal: * + action: Sqs:ReceiveMessage + - name: user-signedup-dlq + messageRetentionPeriod: 1209600 + fifoQueue: false +``` diff --git a/sqs/SNS-SQS-Pub-Sub.png b/sqs/SNS-SQS-Pub-Sub.png new file mode 100644 index 00000000..033d6de7 Binary files /dev/null and b/sqs/SNS-SQS-Pub-Sub.png differ diff --git a/sqs/SQS-Point-To-Point.png b/sqs/SQS-Point-To-Point.png new file mode 100644 index 00000000..a94cfc7b Binary files /dev/null and b/sqs/SQS-Point-To-Point.png differ diff --git a/sqs/json_schemas/channel.json b/sqs/json_schemas/channel.json new file mode 100644 index 00000000..3930780a --- /dev/null +++ b/sqs/json_schemas/channel.json @@ -0,0 +1,246 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/sqs/channel.json", + "title": "Channel Schema", + "description": "This object contains information about the channel representation in SQS.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "queue": { + "description": "A definition of the queue that will be used as the channel.", + "$ref": "#/definitions/queue" + }, + "deadLetterQueue": { + "description": "A definition of the queue that will be used for un-processable messages.", + "$ref": "#/definitions/queue" + }, + "bindingVersion": { + "type": "string", + "description": "The version of this binding.", + "default": "latest" + } + }, + "required": [ + "queue" + ], + "definitions": { + "queue": { + "type": "object", + "description": "A definition of a queue.", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "name": { + "type": "string", + "description": "The name of the queue. When an SNS Operation Binding Object references an SQS queue by name, the identifier should be the one in this field." + }, + "fifoQueue": { + "type": "boolean", + "description": "Is this a FIFO queue?", + "default": false + }, + "deliveryDelay": { + "type": "integer", + "description": "The number of seconds to delay before a message sent to the queue can be received. used to create a delay queue.", + "minimum": 0, + "maximum": 15, + "default": 0 + }, + "visibilityTimeout": { + "type": "integer", + "description": "The length of time, in seconds, that a consumer locks a message - hiding it from reads - before it is unlocked and can be read again.", + "minimum": 0, + "maximum": 43200, + "default": 30 + }, + "receiveMessageWaitTime": { + "type": "integer", + "description": "Determines if the queue uses short polling or long polling. Set to zero the queue reads available messages and returns immediately. Set to a non-zero integer, long polling waits the specified number of seconds for messages to arrive before returning.", + "default": 0 + }, + "messageRetentionPeriod": { + "type": "integer", + "description": "How long to retain a message on the queue in seconds, unless deleted.", + "minimum": 60, + "maximum": 1209600, + "default": 345600 + }, + "redrivePolicy": { + "$ref": "#/definitions/redrivePolicy" + }, + "policy": { + "$ref": "#/definitions/policy" + }, + "tags": { + "type": "object", + "description": "Key-value pairs that represent AWS tags on the queue." + } + }, + "required": [ + "name", + "fifoQueue" + ] + }, + "redrivePolicy": { + "type": "object", + "description": "Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue.", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "deadLetterQueue": { + "$ref": "#/definitions/identifier" + }, + "maxReceiveCount": { + "type": "integer", + "description": "The number of times a message is delivered to the source queue before being moved to the dead-letter queue.", + "default": 10 + } + }, + "required": [ + "deadLetterQueue" + ] + }, + "identifier": { + "type": "object", + "description": "The SQS queue to use as a dead letter queue (DLQ).", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "arn": { + "type": "string", + "description": "The target is an ARN. For example, for SQS, the identifier may be an ARN, which will be of the form: arn:aws:sqs:{region}:{account-id}:{queueName}" + }, + "name": { + "type": "string", + "description": "The endpoint is identified by a name, which corresponds to an identifying field called 'name' of a binding for that protocol on this publish Operation Object. For example, if the protocol is 'sqs' then the name refers to the name field sqs binding." + } + } + }, + "policy": { + "type": "object", + "description": "The security policy for the SQS Queue", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "statements": { + "type": "array", + "description": "An array of statement objects, each of which controls a permission for this queue.", + "items": { + "$ref": "#/definitions/statement" + } + } + }, + "required": [ + "statements" + ] + }, + "statement": { + "type": "object", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "effect": { + "type": "string", + "enum": [ + "Allow", + "Deny" + ] + }, + "principal": { + "description": "The AWS account or resource ARN that this statement applies to.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "action": { + "description": "The SQS permission being allowed or denied e.g. sqs:ReceiveMessage", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": [ + "effect", + "principal", + "action" + ] + } + }, + "examples": [ + { + "queue": { + "name": "myQueue", + "fifoQueue": true, + "deliveryDelay": 15, + "visibilityTimeout": 60, + "receiveMessageWaitTime": 0, + "messageRetentionPeriod": 86400, + "redrivePolicy": { + "deadLetterQueue": { + "arn": "arn:aws:SQS:eu-west-1:0000000:123456789" + }, + "maxReceiveCount": 15 + }, + "policy": { + "statements": [ + { + "effect": "Deny", + "principal": "arn:aws:iam::123456789012:user/dec.kolakowski", + "action": [ + "sqs:SendMessage", + "sqs:ReceiveMessage" + ] + } + ] + }, + "tags": { + "owner": "AsyncAPI.NET", + "platform": "AsyncAPIOrg" + } + }, + "deadLetterQueue": { + "name": "myQueue_error", + "deliveryDelay": 0, + "visibilityTimeout": 0, + "receiveMessageWaitTime": 0, + "messageRetentionPeriod": 604800 + } + } + ] +} \ No newline at end of file diff --git a/sqs/json_schemas/operation.json b/sqs/json_schemas/operation.json new file mode 100644 index 00000000..9f26e977 --- /dev/null +++ b/sqs/json_schemas/operation.json @@ -0,0 +1,239 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://asyncapi.com/bindings/sqs/operation.json", + "title": "Operation Schema", + "description": "This object contains information about the operation representation in SQS.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "queues": { + "type": "array", + "description": "Queue objects that are either the endpoint for an SNS Operation Binding Object, or the deadLetterQueue of the SQS Operation Binding Object.", + "items": { + "$ref": "#/definitions/queue" + } + }, + "bindingVersion": { + "type": "string", + "description": "The version of this binding.", + "default": "latest" + } + }, + "required": [ + "queues" + ], + "definitions": { + "queue": { + "type": "object", + "description": "A definition of a queue.", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "$ref": { + "type": "string", + "description": "Allows for an external definition of a queue. The referenced structure MUST be in the format of a Queue. If there are conflicts between the referenced definition and this Queue's definition, the behavior is undefined." + }, + "name": { + "type": "string", + "description": "The name of the queue. When an SNS Operation Binding Object references an SQS queue by name, the identifier should be the one in this field." + }, + "fifoQueue": { + "type": "boolean", + "description": "Is this a FIFO queue?", + "default": false + }, + "deliveryDelay": { + "type": "integer", + "description": "The number of seconds to delay before a message sent to the queue can be received. Used to create a delay queue.", + "minimum": 0, + "maximum": 15, + "default": 0 + }, + "visibilityTimeout": { + "type": "integer", + "description": "The length of time, in seconds, that a consumer locks a message - hiding it from reads - before it is unlocked and can be read again.", + "minimum": 0, + "maximum": 43200, + "default": 30 + }, + "receiveMessageWaitTime": { + "type": "integer", + "description": "Determines if the queue uses short polling or long polling. Set to zero the queue reads available messages and returns immediately. Set to a non-zero integer, long polling waits the specified number of seconds for messages to arrive before returning.", + "default": 0 + }, + "messageRetentionPeriod": { + "type": "integer", + "description": "How long to retain a message on the queue in seconds, unless deleted.", + "minimum": 60, + "maximum": 1209600, + "default": 345600 + }, + "redrivePolicy": { + "$ref": "#/definitions/redrivePolicy" + }, + "policy": { + "$ref": "#/definitions/policy" + }, + "tags": { + "type": "object", + "description": "Key-value pairs that represent AWS tags on the queue." + } + }, + "required": [ + "name" + ] + }, + "redrivePolicy": { + "type": "object", + "description": "Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue.", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "deadLetterQueue": { + "$ref": "#/definitions/identifier" + }, + "maxReceiveCount": { + "type": "integer", + "description": "The number of times a message is delivered to the source queue before being moved to the dead-letter queue.", + "default": 10 + } + }, + "required": [ + "deadLetterQueue" + ] + }, + "identifier": { + "type": "object", + "description": "The SQS queue to use as a dead letter queue (DLQ).", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "arn": { + "type": "string", + "description": "The target is an ARN. For example, for SQS, the identifier may be an ARN, which will be of the form: arn:aws:sqs:{region}:{account-id}:{queueName}" + }, + "name": { + "type": "string", + "description": "The endpoint is identified by a name, which corresponds to an identifying field called 'name' of a binding for that protocol on this publish Operation Object. For example, if the protocol is 'sqs' then the name refers to the name field sqs binding." + } + } + }, + "policy": { + "type": "object", + "description": "The security policy for the SQS Queue", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "statements": { + "type": "array", + "description": "An array of statement objects, each of which controls a permission for this queue.", + "items": { + "$ref": "#/definitions/statement" + } + } + }, + "required": [ + "statements" + ] + }, + "statement": { + "type": "object", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/v2.14.0/schemas/2.4.0.json#/definitions/specificationExtension" + } + }, + "properties": { + "effect": { + "type": "string", + "enum": [ + "Allow", + "Deny" + ] + }, + "principal": { + "description": "The AWS account or resource ARN that this statement applies to.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "action": { + "description": "The SQS permission being allowed or denied e.g. sqs:ReceiveMessage", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": [ + "effect", + "principal", + "action" + ] + } + }, + "examples": [ + { + "queues": [ + { + "name": "myQueue", + "deliveryDelay": 10, + "redrivePolicy": { + "deadLetterQueue": { + "name": "myQueue_error" + }, + "maxReceiveCount": 15 + }, + "policy": { + "statements": [ + { + "effect": "Deny", + "principal": "arn:aws:iam::123456789012:user/dec.kolakowski", + "action": [ + "sqs:SendMessage", + "sqs:ReceiveMessage" + ] + } + ] + } + }, + { + "name": "myQueue_error", + "deliveryDelay": 10 + } + ] + } + ] +}