├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ ├── ci.yml.skip │ ├── conformance-run.yml.skip │ ├── interoperability-report.yml.skip │ ├── onboard-register.yml │ ├── onboard-rotate.yml │ ├── publish-editors-draft.yml │ ├── publish-openapi-spec.yml │ ├── publish-report-index.yml │ ├── regression-did-web-discovery.yml.skip │ └── regression-vc-jwt.yml.skip ├── .gitignore ├── .pr-preview.json ├── .prettierrc.js ├── AGENDA.md ├── CGFR └── 2024-10-10 │ └── index.html ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE.md ├── MEETINGS.md ├── OPENAPI.md ├── README.md ├── docs ├── README.md ├── imported-collection.png ├── openapi │ ├── index.html │ ├── openapi.yml │ ├── parameters │ │ ├── _index.yml │ │ ├── header │ │ │ └── accept.yml │ │ ├── path │ │ │ ├── credential-id.yml │ │ │ └── did.yml │ │ └── query │ │ │ └── limit.yml │ ├── resources │ │ ├── api-configuration.yml │ │ ├── credential-issuer.yml │ │ ├── credential-status.yml │ │ ├── credential-verifier.yml │ │ ├── credential.yml │ │ ├── did.yml │ │ ├── presentation-available.yml │ │ ├── presentation-prover.yml │ │ ├── presentation-submissions.yml │ │ ├── presentation-verifier.yml │ │ └── presentations.yml │ ├── responses │ │ ├── BadRequest.yml │ │ ├── ErrInvalidDID.yml │ │ ├── ErrUnknownIssuer.yml │ │ ├── NotFound.yml │ │ ├── NullResponse.yml │ │ ├── Unauthenticated.yml │ │ ├── Unauthorized.yml │ │ ├── UnexpectedError.yml │ │ └── _index.yml │ └── schemas │ │ ├── Credential.yml │ │ ├── DidResolutionResponse.yml │ │ ├── Error.yml │ │ ├── Holder.yml │ │ ├── IssueCredentialOptions.yml │ │ ├── NotifyPresentationAvailableRequest.yml │ │ ├── NotifyPresentationAvailableResponse.yml │ │ ├── Presentation.yml │ │ ├── PresentationOptions.yml │ │ ├── SerializedVerifiableCredential.yml │ │ ├── SerializedVerifiablePresentation.yml │ │ ├── StatusListCredential.yml │ │ ├── TraceabilityAPIDIDWebDocument.yml │ │ ├── TraceablePresentation.yml │ │ ├── VC-JWT.yml │ │ ├── VP-JWT.yml │ │ ├── VerifiableCredential.yml │ │ ├── VerifiablePresentation.yml │ │ ├── Verification.yml │ │ ├── VerificationResult.yml │ │ ├── Workflow.yml │ │ └── _index.yml ├── reports │ ├── .gitkeep │ └── archive │ │ └── index.html ├── spec │ ├── index.html │ ├── resources │ │ ├── standards-stack.png │ │ ├── workflow-def-ex.png │ │ └── workflow-inst-ex.png │ └── sections │ │ ├── abstract.html │ │ ├── api-spec.html │ │ ├── authorization.html │ │ ├── mandatory-to-implement-algorithms.html │ │ ├── selective-disclosure.html │ │ └── use-case-requirements.html ├── test-data │ └── .gitkeep ├── tutorials │ ├── .gitignore │ ├── README.md │ ├── authentication │ │ ├── README.md │ │ ├── authentication.postman_collection.json │ │ ├── example.env │ │ └── resources │ │ │ ├── create-workspace.png │ │ │ ├── environment-variables.png │ │ │ ├── get-access-token-request.png │ │ │ ├── get-access-token-response.png │ │ │ └── get-access-token-tests.png │ ├── credentials-status-update │ │ ├── README.md │ │ ├── credentials-status-update.postman_collection.json │ │ └── resources │ │ │ ├── credentials-status-update-auth.png │ │ │ ├── credentials-status-update-body.png │ │ │ ├── credentials-status-update-headers.png │ │ │ ├── credentials-status-update-prereq.png │ │ │ ├── credentials-status-update-tests-fail.png │ │ │ ├── credentials-verification-response.png │ │ │ ├── credentials-verification-tests-fail.png │ │ │ └── select-environment.png │ ├── did-web-discovery │ │ ├── README.md │ │ ├── did-web-discovery.postman_collection.json │ │ ├── example.env │ │ └── resources │ │ │ ├── get-organization-dids-auth.png │ │ │ ├── get-organization-dids-headers.png │ │ │ ├── get-organization-dids-response.png │ │ │ ├── get-organization-dids-tests-fail.png │ │ │ ├── persist-access-token.png │ │ │ └── select-environment.png │ ├── interoperability_suite.postman_environment.json │ ├── presentations-exchange-oauth │ │ ├── README.md │ │ ├── presentations-exchange-oauth.json │ │ └── resources │ │ │ ├── confirm-import.png │ │ │ ├── import-button.png │ │ │ ├── import-complete.png │ │ │ └── import-from-link.png │ ├── report-generation │ │ ├── README.md │ │ ├── build-index.js │ │ ├── newman-json-sanitizer.js │ │ └── report-tester.collection.json │ ├── resources │ │ ├── configure-environment.png │ │ ├── create-workspace-details.png │ │ ├── create-workspace-start.png │ │ ├── import-collection-confirm.png │ │ ├── import-collection-link.png │ │ ├── import-environment-confirm.png │ │ ├── import-environment-link.png │ │ ├── import-start.png │ │ └── select-environment.png │ └── traceable-presentation-workflow │ │ ├── README.md │ │ └── traceable-presentation-workflow.postman_collection.json └── weekly-minutes │ ├── README.md │ └── resources │ ├── add-group-file-name.png │ ├── add-group-file.png │ ├── click-download-audio.png │ ├── commit-group-file.png │ ├── download-audio.png │ ├── files-uploaded.png │ ├── play-audio.png │ ├── raw-irc-logs.png │ ├── select-meeting.png │ ├── upload-files-confirm.png │ └── upload-files.png ├── environment-setup ├── README.md ├── create-pat.png ├── onboard-register.sh ├── onboard-rotate.sh └── pubkey.asc ├── lerna.json ├── package-lock.json ├── package.json ├── reporting ├── README.md ├── assets │ ├── conformance.j2 │ └── interoperability.j2 ├── environment.yml ├── postman_reporter │ ├── __init__.py │ ├── report_charts.py │ ├── report_config.py │ ├── report_dashboard.py │ ├── report_data.py │ ├── report_static.py │ └── reporter_util.py ├── reporter.py └── requirements.txt └── tests ├── README.md ├── conformance_suite.postman_collection.json ├── conformance_suite.postman_environment.json ├── resources ├── configure-environment.png ├── create-workspace-details.png ├── create-workspace-start.png ├── import-collection-confirm.png ├── import-collection-link.png ├── import-environment-confirm.png ├── import-environment-link.png ├── import-start.png └── select-environment.png ├── traceability-v1.jsonld ├── update_conformance_schemas.sh ├── update_conformance_vcs.js └── valid-credential.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = false 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es2021: true, 6 | 'jest/globals': true, 7 | }, 8 | plugins: ['jest', '@html-eslint'], 9 | extends: ['airbnb-base'], 10 | parserOptions: { 11 | ecmaVersion: 12, 12 | sourceType: 'module' 13 | }, 14 | overrides: [ 15 | { 16 | files: ['*.html'], 17 | parser: '@html-eslint/parser', 18 | extends: ['plugin:@html-eslint/recommended'], 19 | }, 20 | ], 21 | settings: { 22 | 'html/report-bad-indent': 'error', 23 | }, 24 | rules: { 25 | indent: 0, 26 | '@html-eslint/indent': ['error', 2], 27 | 'no-restricted-syntax': [0, 'ForOfStatement'], 28 | 'max-len': [2, 140], 29 | 'global-require': 0, 30 | 'no-console': 0, 31 | 'no-unused-vars': 0, 32 | 'comma-dangle': 0, 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml.skip: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | postman: 11 | runs-on: ubuntu-latest 12 | # With the exception of GITHUB_TOKEN, secrets are not passed to the runner 13 | # when a workflow is triggered from a forked repository. 14 | if: "!contains(github.event.head_commit.message, '[skip ci]') && github.repository == github.event.repository.full_name" 15 | 16 | steps: 17 | - name: Check out git repository 18 | uses: actions/checkout@v2 19 | 20 | - name: Set up Node.js 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: 16.15.1 24 | 25 | - name: Install 26 | run: npm i 27 | 28 | - name: Lint 29 | run: npm run lint 30 | -------------------------------------------------------------------------------- /.github/workflows/conformance-run.yml.skip: -------------------------------------------------------------------------------- 1 | name: Conformance Testing 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | # Every day at midnight 7 | - cron: '0 0 * * *' 8 | 9 | jobs: 10 | 11 | # conformance-run works through a matrix of actors and runs the conformance 12 | # test suite for each of them. Results are stored in a `reports` artifact for 13 | # later compilation and publishing. 14 | conformance-run: 15 | name: Run Conformance Suite 16 | runs-on: ubuntu-latest 17 | strategy: 18 | # Job must not stop iterating through matrix on failure 19 | fail-fast: false 20 | matrix: 21 | include: 22 | - name: "Mavennet" 23 | actor: "MAVENNET_STAGING" 24 | - name: "mesur.io" 25 | actor: "MESUR_IO_PRODUCTION" 26 | - name: "GS1US" 27 | actor: "GS1US" 28 | - name: 'BCGov' 29 | actor: 'BCGOV' 30 | steps: 31 | # Check out repo, set up node, and install dependencies. 32 | # @see https://github.com/actions/setup-node#usage 33 | - uses: actions/checkout@v3 34 | - uses: actions/setup-node@v3 35 | with: 36 | node-version: 16.15.1 37 | cache: 'npm' 38 | - run: npm ci 39 | - name: Run Tests 40 | env: 41 | organization_did_web: ${{secrets[format('{0}_ORGANIZATION_DID_WEB', matrix.actor)]}} 42 | client_id: ${{secrets[format('{0}_CLIENT_ID', matrix.actor)]}} 43 | client_secret: ${{secrets[format('{0}_CLIENT_SECRET', matrix.actor)]}} 44 | token_audience: ${{secrets[format('{0}_TOKEN_AUDIENCE', matrix.actor)]}} 45 | token_endpoint: ${{secrets[format('{0}_TOKEN_ENDPOINT', matrix.actor)]}} 46 | api_base_url: ${{secrets[format('{0}_API_BASE_URL', matrix.actor)]}} 47 | run: | 48 | npx newman run ./tests/conformance_suite.postman_collection.json \ 49 | --env-var ORGANIZATION_DID_WEB=$organization_did_web \ 50 | --env-var CLIENT_ID=$client_id \ 51 | --env-var CLIENT_SECRET=$client_secret \ 52 | --env-var TOKEN_AUDIENCE=$token_audience \ 53 | --env-var TOKEN_ENDPOINT=$token_endpoint \ 54 | --env-var API_BASE_URL=$api_base_url \ 55 | --reporters cli,htmlextra,json \ 56 | --reporter-htmlextra-skipSensitiveData \ 57 | --reporter-htmlextra-export "newman/${{format('{0}-{1}-{2}.html', github.run_id, github.job, matrix.name)}}" \ 58 | --reporter-json-export "newman/${{format('{0}-{1}-{2}.json', github.run_id, github.job, matrix.name)}}" 59 | 60 | # Write sanitized Newman output to `./docs/reports`. 61 | - name: Sanitize Report Output 62 | if: always() # Run even when postman tests fail 63 | run: npm run report:sanitize 64 | 65 | # Sanitized reports are needed by subsequent jobs 66 | - uses: actions/upload-artifact@v3 67 | if: always() # Run even when postman tests fail 68 | with: 69 | name: reports 70 | path: docs/reports/ 71 | 72 | # conformance-compile compiles the sanitized raw report output into an 73 | # interactive HTML conformance report suitable for publishing. 74 | conformance-compile: 75 | name: Compile Reports 76 | runs-on: ubuntu-latest 77 | 78 | # Run even when postman tests fail 79 | if: always() 80 | 81 | # Wait for jobs which generate reporting input. 82 | needs: 83 | - conformance-run 84 | 85 | steps: 86 | # Check out repo, setup node, and install dependencies. 87 | # @see https://github.com/actions/setup-node#usage 88 | - uses: actions/checkout@v3 89 | - uses: actions/setup-node@v3 90 | with: 91 | node-version: 16.15.1 92 | cache: 'npm' 93 | - run: npm ci 94 | 95 | # Download 'reports' artifact 96 | - uses: actions/download-artifact@v3 97 | with: 98 | name: reports 99 | path: docs/reports/ 100 | 101 | # Build report index 102 | - name: Build JSON Index 103 | run: npm run report:index -- --folder conformance 104 | 105 | # Set up Python 3.10 106 | - name: Set up Python 3.10 107 | uses: actions/setup-python@v4 108 | with: 109 | python-version: "3.10" 110 | cache: pip 111 | - run: pip install -r ./reporting/requirements.txt 112 | 113 | # Compile interactive report 114 | - name: Build Conformance Report 115 | working-directory: ./reporting 116 | run: ./reporter.py --mode ci --conformance 117 | 118 | # Compiled report must be added to artifact 119 | - uses: actions/upload-artifact@v3 120 | with: 121 | name: reports 122 | path: docs/reports/ 123 | 124 | # conformance-publish publishes sanitized raw and interactive HTML reports to 125 | # the `reports/conformance` directory in GitHub pages. This job is restricted 126 | # to commits on the `main` branch to prevent feature branches from overwriting 127 | # production reports. 128 | conformance-publish: 129 | name: Publish Report 130 | runs-on: ubuntu-latest 131 | 132 | # Publishing must only happen when triggered on the 'main' branch to prevent 133 | # tests on other branches from polluting the published reports. Note that 134 | # `always()`` is a special flag that is REQUIRED in order to get this job to 135 | # run if jobs listed in the `needs:` section fail. 136 | if: github.ref == 'refs/heads/main' && always() 137 | 138 | # Report must be compiled before publishing 139 | needs: 140 | - conformance-compile 141 | 142 | steps: 143 | # Check out repo 144 | - uses: actions/checkout@v3 145 | 146 | # Download 'reports' artifact 147 | - uses: actions/download-artifact@v3 148 | with: 149 | name: reports 150 | path: docs/reports/ 151 | 152 | # Publish report subfolder to GitHub Pages 153 | - name: Publish Conformance Report 154 | uses: peaceiris/actions-gh-pages@v3 155 | with: 156 | github_token: ${{ secrets.GITHUB_TOKEN }} 157 | publish_dir: ./docs/reports 158 | destination_dir: reports/conformance 159 | 160 | # Prepare an archive folder name and update the index.json appropriately 161 | - name: Prepare Conformance Archive 162 | id: archive 163 | run: | 164 | FOLDER=conformance-$(date +'%s') 165 | sed -i "s#reports/conformance/#reports/$FOLDER/#" ./docs/reports/index.json 166 | echo "::set-output name=folder::$FOLDER" 167 | 168 | # Publish report archive subfolder to GitHub Pages 169 | - name: Publish Conformance Archive 170 | uses: peaceiris/actions-gh-pages@v3 171 | with: 172 | github_token: ${{ secrets.GITHUB_TOKEN }} 173 | publish_dir: ./docs/reports 174 | destination_dir: reports/${{ steps.archive.outputs.folder }} 175 | -------------------------------------------------------------------------------- /.github/workflows/onboard-register.yml: -------------------------------------------------------------------------------- 1 | # This workflow will ingest and parse a gpg-encrypted, base64-encoded secrets 2 | # file used for the test suite registration process. 3 | # 4 | # Prerequisites: 5 | # - Create a GitHub PAT token with `public_repo` scope 6 | 7 | name: "Onboard: Register" 8 | 9 | on: 10 | workflow_dispatch: 11 | inputs: 12 | 13 | # A GitHub PAT with `public_repo` scope is required in order to manage 14 | # the values in GitHub secrets. 15 | token: 16 | description: GitHub Token 17 | required: true 18 | type: string 19 | 20 | # This is a vendor-specific prefix to add to the secret names in the 21 | # provided file. This should be unique per vendor, and should include 22 | # the trailing underscore. 23 | prefix: 24 | description: Secret Prefix, e.g., 'VENDOR_SLUG_' 25 | required: true 26 | type: string 27 | 28 | # This is the base64-encoded GPG file provided by the organization that 29 | # is registering for the test suite. 30 | dotenv: 31 | description: Base64-encoded GPG file 32 | required: true 33 | type: string 34 | 35 | # Check this box to skip the step that prevents existing secrets from 36 | # being overwritten with new data. 37 | overwrite: 38 | description: Overwrite? (DANGER) 39 | required: false 40 | type: boolean 41 | default: false 42 | 43 | env: 44 | GITHUB_TOKEN: ${{ inputs.token }} 45 | 46 | jobs: 47 | onboard-register: 48 | runs-on: ubuntu-latest 49 | steps: 50 | 51 | # Import the GPG key from GitHub secrets into the gpg keyring on the 52 | # test runner. This is required in order to be able to decrypt secrets 53 | # files encrypted with the corresponding public key. 54 | - name: Import GPG Key 55 | run: echo -e '${{ env.PRIVATE_KEY }}' | gpg --import --batch --no-tty 56 | env: 57 | PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} 58 | 59 | # To prevent formatting issues when maintainers paste the secrets file 60 | # into the workflow input, all data must be base64 encoded. This step 61 | # is able to handle both line-wrapped and un-wrapped `base64` output. 62 | # Once decoded, the file is passed to GPG to decrypt using the key that 63 | # was imported in the previous step and the passphrase from GitHub 64 | # secrets. 65 | - name: Decode Environment File 66 | run: | 67 | # Remove whitespace in case base64 is line-wrapped 68 | b64=$(echo '${{ inputs.dotenv }}' | tr -d "[:space:]") 69 | 70 | # Decoded dotenv may be binary or ASCII, store to disk 71 | echo $b64 | base64 -d > dotenv.gpg 72 | 73 | # Headless decryption of encrypted environment file with no recipient 74 | gpg --try-all-secrets --decrypt --pinentry-mode loopback \ 75 | --passphrase '${{ env.PASSPHRASE }}' --output dotenv.asc dotenv.gpg 76 | env: 77 | PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 78 | 79 | # Ensure that secrets files created on Windows are properly updated to 80 | # convert CRLF line endings to LF to prevent corruption while reading 81 | # the file line-by-line 82 | - name: Convert Line Endings 83 | run: | 84 | sudo apt-get install -y dos2unix 85 | dos2unix dotenv.asc 86 | 87 | # By default, this workflow will fail if any of the keys in the secrets 88 | # file (prepended with the `prefix` input) already exist in GitHub secrets. 89 | # If you know what you are doing, you can disable this step by checking the 90 | # `Overwrite? (DANGER)` checkbox when running this workflow. 91 | - name: Check for Overwrites 92 | if: ${{ !inputs.overwrite }} 93 | run: | 94 | SECRETS=($(gh secret list --repo ${{github.repository}} | awk '{print $1}')) 95 | 96 | # Portion of line before '=' may not match an existing key 97 | while read -r LINE 98 | do 99 | SECRET_NAME=${{inputs.prefix}}${LINE%=*} 100 | if [[ " ${SECRETS[*]} " =~ " ${SECRET_NAME} " ]] 101 | then 102 | echo "Refusing to overwrite existing variable '${SECRET_NAME}'" 103 | exit 1 104 | fi 105 | done < dotenv.asc 106 | 107 | # This step parses keys and values from the decrypted secrets files, adds 108 | # the provided input `prefix` to the variable name, and stores everything 109 | # in GitHub secrets. 110 | - name: Set GitHub Secrets 111 | run: | 112 | while read -r LINE 113 | do 114 | SECRET_NAME=${{inputs.prefix}}${LINE%=*} 115 | SECRET_VALUE=${LINE#*=} 116 | gh secret set "${SECRET_NAME}" --body "${SECRET_VALUE}" \ 117 | --repo ${{github.repository}} 118 | done < dotenv.asc 119 | -------------------------------------------------------------------------------- /.github/workflows/onboard-rotate.yml: -------------------------------------------------------------------------------- 1 | # This workflow rotates the GPG key used to encrypt/decrypt secrets files used 2 | # for test suite registration process. 3 | # 4 | # Prerequisites: 5 | # - Create a GitHub PAT token with `public_repo` scope 6 | 7 | name: "Onboard: Rotate" 8 | 9 | on: 10 | workflow_dispatch: 11 | inputs: 12 | 13 | # A GitHub PAT with `public_repo` scope is required in order to manage 14 | # the values in GitHub secrets. 15 | token: 16 | description: GitHub Token 17 | required: true 18 | type: string 19 | 20 | env: 21 | GITHUB_TOKEN: ${{ inputs.token }} 22 | 23 | jobs: 24 | onboard-rotate: 25 | runs-on: ubuntu-latest 26 | steps: 27 | 28 | # Generate a GPG encryption key with a random passphrase. The key will 29 | # have a UID of `w3c-ccg/traceability-interop`. 30 | - name: Generate GPG Key 31 | id: gpg-generate 32 | run: | 33 | PASSPHRASE=$(gpg --gen-random --armor 1 32) 34 | echo "::add-mask::${PASSPHRASE}" 35 | cat <<- EOF | gpg --batch --generate-key 36 | Key-Type: RSA 37 | Key-Length: 4096 38 | Key-Usage: encrypt 39 | Name-Real: w3c-ccg/traceability-interop 40 | Name-Comment: test suite registration 41 | Passphrase: ${PASSPHRASE} 42 | Expire-Date: 0 43 | EOF 44 | echo "::set-output name=PASSPHRASE::$PASSPHRASE" 45 | 46 | # The key fingerprint is used to refer to the newly generated key in 47 | # subsequent commands. This limits the number of edits required if we 48 | # choose to change the UID. 49 | - name: Set GPG Fingerprint Output 50 | id: gpg-fingerprint 51 | run: | 52 | FINGERPRINT=$(gpg --with-colons --list-keys | awk -F: '/^pub/ { print $5 }') 53 | echo "::set-output name=FINGERPRINT::$FINGERPRINT" 54 | 55 | # Store the randomly generated passphrase and the secret key in GitHub 56 | # secrets. These will be retrieved and used to bootstrap the GPG agent 57 | # in the workflow that imports secrets for test suite registration. 58 | - name: Update Secrets 59 | run: | 60 | gh secret set 'GPG_PASSPHRASE' --body "${{env.PASSPHRASE}}" --repo ${{github.repository}} 61 | gpg --armor --pinentry-mode loopback --passphrase "${{ env.PASSPHRASE }}" --export-secret-key ${{ env.KEY_ID }} \ 62 | | gh secret set 'GPG_PRIVATE_KEY' --repo ${{github.repository}} 63 | env: 64 | KEY_ID: ${{ steps.gpg-fingerprint.outputs.FINGERPRINT }} 65 | PASSPHRASE: ${{ steps.gpg-generate.outputs.PASSPHRASE }} 66 | 67 | # The public portion of the GPG key (ascii-armored) is exported so that 68 | # it can be accessed by end-users needing to encrypt secrets for test suite 69 | # registration. 70 | - uses: actions/checkout@v3 71 | - name: Update Public Key 72 | working-directory: ./environment-setup 73 | run: | 74 | gpg --armor --export ${{ env.KEY_ID }} > pubkey.asc 75 | env: 76 | KEY_ID: ${{ steps.gpg-fingerprint.outputs.FINGERPRINT }} 77 | 78 | # Create an automated git commit to add the exported ASCII public key 79 | # to the repository 80 | - name: Commit Key Changes 81 | working-directory: ./environment-setup 82 | run: | 83 | git config --global user.name ${GITHUB_ACTOR} 84 | git config --global user.email ${GITHUB_ACTOR}@users.noreply.github.com 85 | git add . # For first-time key creation 86 | git commit -am "chore: automated gpg key rotation" 87 | git push 88 | -------------------------------------------------------------------------------- /.github/workflows/publish-editors-draft.yml: -------------------------------------------------------------------------------- 1 | name: Publish Editor's Draft 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'docs/spec/**' 9 | 10 | jobs: 11 | publish-editors-draft: 12 | runs-on: ubuntu-latest 13 | steps: 14 | 15 | # Check out repo, setup node, and install dependencies. 16 | # @see https://github.com/actions/setup-node#usage 17 | - uses: actions/checkout@v3 18 | 19 | # Publish editor's draft to GitHub Pages 20 | - uses: peaceiris/actions-gh-pages@v3 21 | with: 22 | github_token: ${{ secrets.GITHUB_TOKEN }} 23 | publish_dir: ./docs/spec 24 | destination_dir: draft 25 | -------------------------------------------------------------------------------- /.github/workflows/publish-openapi-spec.yml: -------------------------------------------------------------------------------- 1 | name: OpenAPI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'docs/openapi/**' 9 | 10 | jobs: 11 | openapi-build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | 15 | # Check out repo, setup node, and install dependencies. 16 | # @see https://github.com/actions/setup-node#usage 17 | - uses: actions/checkout@v3 18 | - uses: actions/setup-node@v3 19 | with: 20 | node-version: 16.15.1 21 | cache: 'npm' 22 | - run: npm ci 23 | 24 | # OpenAPI YAML must pass linting 25 | - name: Lint OpenAPI Specification YAML 26 | run: npm run validate-spec 27 | 28 | # Build the JSON version of the OpenAPI specification from the YAML source 29 | # files prior to publishing. 30 | - name: Build Open API Specification JSON 31 | run: npm run preserve 32 | 33 | # Publish openapi subfolder to GitHub Pages 34 | - name: Publish Open API Specification 35 | uses: peaceiris/actions-gh-pages@v3 36 | with: 37 | github_token: ${{ secrets.GITHUB_TOKEN }} 38 | publish_dir: ./docs/openapi 39 | destination_dir: openapi 40 | 41 | -------------------------------------------------------------------------------- /.github/workflows/publish-report-index.yml: -------------------------------------------------------------------------------- 1 | name: Publish Report Index 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'docs/reports/archive/**' 9 | 10 | jobs: 11 | publish-index: 12 | runs-on: ubuntu-latest 13 | steps: 14 | 15 | # Check out repo, setup node, and install dependencies. 16 | # @see https://github.com/actions/setup-node#usage 17 | - uses: actions/checkout@v3 18 | 19 | # Publish reports/archive subfolder to GitHub Pages 20 | - name: Publish Open API Specification 21 | uses: peaceiris/actions-gh-pages@v3 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | publish_dir: ./docs/reports/archive 25 | destination_dir: reports/archive 26 | -------------------------------------------------------------------------------- /.github/workflows/regression-did-web-discovery.yml.skip: -------------------------------------------------------------------------------- 1 | name: DID Web Discovery 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | unit-did-web-discovery: 8 | name: DID Web Discovery 9 | runs-on: ubuntu-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | include: 14 | - name: "Mavennet" 15 | actor: "MAVENNET_STAGING" 16 | - name: "mesur.io" 17 | actor: "MESUR_IO_PRODUCTION" 18 | - name: "GS1US" 19 | actor: "GS1US" 20 | steps: 21 | # Check out repo, setup node, and install dependencies. 22 | # @see https://github.com/actions/setup-node#usage 23 | - uses: actions/checkout@v3 24 | - uses: actions/setup-node@v3 25 | with: 26 | node-version: 16.15.1 27 | cache: 'npm' 28 | - run: npm ci 29 | - name: Run Tests 30 | env: 31 | organization_did_web: ${{secrets[format('{0}_ORGANIZATION_DID_WEB', matrix.actor)]}} 32 | client_id: ${{secrets[format('{0}_CLIENT_ID', matrix.actor)]}} 33 | client_secret: ${{secrets[format('{0}_CLIENT_SECRET', matrix.actor)]}} 34 | token_audience: ${{secrets[format('{0}_TOKEN_AUDIENCE', matrix.actor)]}} 35 | token_endpoint: ${{secrets[format('{0}_TOKEN_ENDPOINT', matrix.actor)]}} 36 | api_base_url: ${{secrets[format('{0}_API_BASE_URL', matrix.actor)]}} 37 | run: | 38 | npx newman run ./docs/tutorials/did-web-discovery/did-web-discovery.postman_collection.json \ 39 | --env-var ORGANIZATION_DID_WEB=$organization_did_web \ 40 | --env-var CLIENT_ID=$client_id \ 41 | --env-var CLIENT_SECRET=$client_secret \ 42 | --env-var TOKEN_AUDIENCE=$token_audience \ 43 | --env-var TOKEN_ENDPOINT=$token_endpoint \ 44 | --env-var API_BASE_URL=$api_base_url \ 45 | --reporters cli,htmlextra,json \ 46 | --reporter-htmlextra-skipSensitiveData \ 47 | --reporter-htmlextra-export "newman/${{format('{0}-{1}-{2}.html', github.run_id, github.job, matrix.name)}}" \ 48 | --reporter-json-export "newman/${{format('{0}-{1}-{2}.json', github.run_id, github.job, matrix.name)}}" 49 | 50 | # Write sanitized Newman output to `./docs/reports`. 51 | - name: Sanitize Report Output 52 | if: always() # Run even when postman tests fail 53 | run: npm run report:sanitize 54 | 55 | # Sanitized reports are needed by subsequent jobs 56 | - uses: actions/upload-artifact@v3 57 | if: always() # Run even when postman tests fail 58 | with: 59 | name: reports 60 | path: docs/reports/ 61 | -------------------------------------------------------------------------------- /.github/workflows/regression-vc-jwt.yml.skip: -------------------------------------------------------------------------------- 1 | name: VC-JWT 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | # Matrix paths are independent, and failure for one provider should not 11 | # halt the running jobs testing another provider. 12 | fail-fast: false 13 | # See matrix of secrets... 14 | # https://sbulav.github.io/terraform/github-actions-matrix-secrets/ 15 | matrix: 16 | include: 17 | - organization_did_web: MAVENNET_STAGING_ORGANIZATION_DID_WEB 18 | client_id: MAVENNET_STAGING_CLIENT_ID 19 | client_secret: MAVENNET_STAGING_CLIENT_SECRET 20 | token_audience: MAVENNET_STAGING_TOKEN_AUDIENCE 21 | token_endpoint: MAVENNET_STAGING_TOKEN_ENDPOINT 22 | api_base_url: MAVENNET_STAGING_API_BASE_URL 23 | - organization_did_web: MESUR_IO_PRODUCTION_ORGANIZATION_DID_WEB 24 | client_id: MESUR_IO_PRODUCTION_CLIENT_ID 25 | client_secret: MESUR_IO_PRODUCTION_CLIENT_SECRET 26 | token_audience: MESUR_IO_PRODUCTION_TOKEN_AUDIENCE 27 | token_endpoint: MESUR_IO_PRODUCTION_TOKEN_ENDPOINT 28 | api_base_url: MESUR_IO_PRODUCTION_API_BASE_URL 29 | - organization_did_web: GS1US_PRODUCTION_ORGANIZATION_DID_WEB 30 | client_id: GS1US_PRODUCTION_CLIENT_ID 31 | client_secret: GS1US_PRODUCTION_CLIENT_SECRET 32 | token_audience: GS1US_PRODUCTION_TOKEN_AUDIENCE 33 | token_endpoint: GS1US_PRODUCTION_TOKEN_ENDPOINT 34 | api_base_url: GS1US_PRODUCTION_API_BASE_URL 35 | 36 | steps: 37 | - name: Begin CI... 38 | uses: actions/checkout@v2 39 | - name: Use Node 16 40 | uses: actions/setup-node@v1 41 | with: 42 | node-version: 16.15.1 43 | 44 | - name: Run Tests 45 | env: 46 | organization_did_web: ${{secrets[matrix.organization_did_web]}} 47 | client_id: ${{secrets[matrix.client_id]}} 48 | client_secret: ${{secrets[matrix.client_secret]}} 49 | token_audience: ${{secrets[matrix.token_audience]}} 50 | token_endpoint: ${{secrets[matrix.token_endpoint]}} 51 | api_base_url: ${{secrets[matrix.api_base_url]}} 52 | run: | 53 | npx newman run ./docs/tutorials/vc-jwt/vc-jwt.collection.json \ 54 | --env-var ORGANIZATION_DID_WEB=$organization_did_web \ 55 | --env-var CLIENT_ID=$client_id \ 56 | --env-var CLIENT_SECRET=$client_secret \ 57 | --env-var TOKEN_AUDIENCE=$token_audience \ 58 | --env-var TOKEN_ENDPOINT=$token_endpoint \ 59 | --env-var API_BASE_URL=$api_base_url \ 60 | --reporters cli,json 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # mac os 2 | .DS_Store 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # test runtime data 14 | test_data/** 15 | !test_data/.gitkeep 16 | newman 17 | __pycache__ 18 | 19 | reporting/data/** 20 | reporting/html/** 21 | 22 | # Diagnostic reports (https://nodejs.org/api/report.html) 23 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 24 | 25 | # Runtime data 26 | pids 27 | *.pid 28 | *.seed 29 | *.pid.lock 30 | 31 | # Directory for instrumented libs generated by jscoverage/JSCover 32 | lib-cov 33 | 34 | # Coverage directory used by tools like istanbul 35 | coverage 36 | *.lcov 37 | 38 | # nyc test coverage 39 | .nyc_output 40 | 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 42 | .grunt 43 | 44 | # Bower dependency directory (https://bower.io/) 45 | bower_components 46 | 47 | # node-waf configuration 48 | .lock-wscript 49 | 50 | # Compiled binary addons (https://nodejs.org/api/addons.html) 51 | build/Release 52 | 53 | # Dependency directories 54 | node_modules/ 55 | jspm_packages/ 56 | 57 | # Snowpack dependency directory (https://snowpack.dev/) 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | *.tsbuildinfo 62 | 63 | # Optional npm cache directory 64 | .npm 65 | 66 | # Optional eslint cache 67 | .eslintcache 68 | 69 | # Microbundle cache 70 | .rpt2_cache/ 71 | .rts2_cache_cjs/ 72 | .rts2_cache_es/ 73 | .rts2_cache_umd/ 74 | 75 | # Optional REPL history 76 | .node_repl_history 77 | 78 | # Output of 'npm pack' 79 | *.tgz 80 | 81 | # Yarn Integrity file 82 | .yarn-integrity 83 | 84 | # dotenv environment variables file 85 | .env 86 | .env.test 87 | .env.production 88 | 89 | # parcel-bundler cache (https://parceljs.org/) 90 | .cache 91 | .parcel-cache 92 | 93 | # Next.js build output 94 | .next 95 | out 96 | 97 | # Nuxt.js build / generate output 98 | .nuxt 99 | dist 100 | 101 | # Gatsby files 102 | .cache/ 103 | # Comment in the public line in if your project uses Gatsby and not Next.js 104 | # https://nextjs.org/blog/next-9-1#public-directory-support 105 | # public 106 | 107 | # vuepress build output 108 | .vuepress/dist 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | 132 | # OpenAPI JSON artifacts 133 | docs/openapi/openapi.json 134 | 135 | 136 | # Postman Reports 137 | # we actually do want to commit reports to source... 138 | # docs/reports 139 | 140 | .vscode -------------------------------------------------------------------------------- /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "docs/index.html", 3 | "type": "respec" 4 | } 5 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tabWidth: 2, 3 | semi: true, 4 | singleQuote: true, 5 | printWidth: 120 6 | }; -------------------------------------------------------------------------------- /AGENDA.md: -------------------------------------------------------------------------------- 1 | # Open API for Interoperable Traceability 2 | 3 | The agenda may be updated periodically if a special topic is under discussion, or a guest speaker is identified, etc. In the event that the agenda is updated, the updated agenda will be distributed to the W3C CCG mailing list. 4 | 5 | 1. IP Note, Agenda Review, Scribe Selection (5 minutes) 6 | 7 | - Agenda Review (2 minutes) 8 | 9 | - IP Note: (1 minute) 10 | 11 | > Anyone can participate in these calls. However, all substantive contributors to any CCG Work Items must be members of the CCG with full IPR agreements signed. https://www.w3.org/community/credentials/join 12 | 13 | * Ensure you have a W3 account: https://www.w3.org/accounts/request 14 | 15 | * W3C COMMUNITY CONTRIBUTOR LICENSE AGREEMENT (CLA): https://www.w3.org/community/about/agreements/cla/ 16 | 17 | - Call Notes (1 minute) 18 | 19 | - These minutes and an audio recording of everything said on this call are archived at https://w3c-ccg.github.io/meetings/ 20 | 21 | - We use Jitsi text chat to queue speakers during the call as well as to take minutes. 22 | 23 | - All attendees should type “present+” to get your name on the attendee list in the transcript. 24 | 25 | - In IRC type “q+” to add yourself to the queue, with an optional reminder, e.g., “q+ to mention something”. The “to” is required. 26 | 27 | - If you’re not on chat, simply ask to be put on the queue. 28 | 29 | - Please be brief so the rest of the queue get a chance to chime in. You can always q+ again. 30 | 31 | - NOTE: This meeting is held by voice, not by text. Off-topic text comments are subject to deletion from the record. We work hard to manage a single thread of conversation so everyone can participate and be heard. Please respect the group process by joining the queue when you have something to contribute. 32 | 33 | - Scribe Selection (2 minutes) - We need a volunteer to scribe. 34 | 35 | 2. GitHub PR & Issue review 36 | 37 | * Review pull requests (PRs) in order (least recently updated first) 38 | 39 | For this portion of the meeting, address PRs in the focus repository first, followed by PRs in the other repository. 40 | 41 | * Publish link to sorted PRs in chat box — 42 | * [trace-interop](https://github.com/w3c-ccg/traceability-interop/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-asc) — 43 | ```pre 44 | https://github.com/w3c-ccg/traceability-interop/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-asc 45 | ``` 46 | * [trace-vocab](https://github.com/w3c-ccg/traceability-vocab/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-asc) — 47 | ```pre 48 | https://github.com/w3c-ccg/traceability-vocab/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-asc 49 | ``` 50 | 51 | * Publish link to each PR being discussed in chat box 52 | 53 | * Review issues in order (least recently updated first) 54 | 55 | For this portion of the meeting, address issues in the focus repository first, followed by issues in the other repository. 56 | 57 | * Publish link to sorted issues in chat box — 58 | * [trace-interop](https://github.com/w3c-ccg/traceability-interop/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc) — 59 | ```pre 60 | https://github.com/w3c-ccg/traceability-interop/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc 61 | ``` 62 | * [trace-vocab](https://github.com/w3c-ccg/traceability-vocab/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc) — 63 | ```pre 64 | https://github.com/w3c-ccg/traceability-vocab/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc 65 | ``` 66 | 67 | * Publish link to each issue being discussed in chat box 68 | 69 | 4. Proposals to Address Challenges 70 | 71 | 5. Next Steps 72 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence, 3 | # they will be requested for review when someone opens a 4 | # pull request. 5 | * @mkhraisha @nissimsan @brownoxford 6 | 7 | # See CODEOWNERS syntax here: https://help.github.com/articles/about-codeowners/#codeowners-syntax 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # W3C Credentials Community Group 2 | 3 | Contributions to this repository are intended to become part of 4 | Recommendation-track documents governed by the 5 | [W3C Patent Policy](https://www.w3.org/Consortium/Patent-Policy-20040205/) and 6 | [Software and Document License](https://www.w3.org/Consortium/Legal/copyright-software). 7 | To make substantive contributions to specifications, you must either participate 8 | in the relevant W3C Working Group or make a non-member patent licensing commitment. 9 | 10 | If you are not the sole contributor to a contribution (pull request), please 11 | identify all contributors in the pull request comment. 12 | 13 | To add a contributor (other than yourself, that's automatic), mark them one 14 | per line as follows: 15 | 16 | ``` 17 | +@github_username 18 | ``` 19 | 20 | If you added a contributor by mistake, you can remove them in a comment with: 21 | 22 | ``` 23 | -@github_username 24 | ``` 25 | 26 | If you are making a pull request on behalf of someone else but you had no 27 | part in designing the feature, you can remove yourself with the above syntax. 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All Reports in this Repository are licensed by Contributors under the [W3C Software and Document 2 | License](https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). 3 | 4 | Contributions to Specifications are made under the 5 | [W3C CLA](https://www.w3.org/community/about/agreements/cla/). 6 | 7 | Contributions to Software, including sample implementations, are under the 8 | [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0). -------------------------------------------------------------------------------- /MEETINGS.md: -------------------------------------------------------------------------------- 1 | 2 | ## Meetings 3 | 4 | Meetings are held: 5 | 6 | - Thursdays at 15:00 ET ([other timezones, accurate at time of viewing, which may vary with Daylight Saving changes](http://www.timebie.com/std/newyork.php?q=15)) 7 | - Via [Jitsi](https://github.com/jitsi) in browser or 8 | [standalone app](https://github.com/jitsi/jitsi-meet-electron/releases) 9 | - using this link: [meet.w3c-ccg.org/traceability](https://meet.w3c-ccg.org/traceability) 10 | - With standing agenda to review open Pull Requests, oldest first 11 | ([trace-interop](https://github.com/w3c-ccg/traceability-interop/pulls?q=is%3Apr+is%3Aopen+sort%3Acreated-asc), 12 | [trace-vocab](https://github.com/w3c-ccg/traceability-vocab/pulls?q=is%3Apr+is%3Aopen+sort%3Acreated-asc)), 13 | then open Issues, least recentlyl updated first 14 | ([trace-interop](https://github.com/w3c-ccg/traceability-interop/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc), 15 | [trace-vocab](https://github.com/w3c-ccg/traceability-vocab/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc)), 16 | unless otherwise noted on the [mailing list](https://lists.w3.org/Archives/Public/public-credentials/) 17 | 18 | Historical archives for meetings [can be found here](https://github.com/w3c-ccg/meetings). 19 | 20 | ### Hosting instructions 21 | 22 | Any chair, editor, or other party authorized by CCG to manage recordings and 23 | minutes can do the following. 24 | 25 | #### Before the Meeting 26 | 27 | - Duplicate the 28 | [W3C-CCG Traceability Agenda Email Draft](https://docs.google.com/document/d/1Se_PIZNhIzZrwVftbYi-Z3oEMXvucQ7jNjxzjMVWCm4/edit) 29 | in Google Docs 30 | - Update all the items highlighted in yellow, in particular adding new agenda 31 | items for week starting with item 6. 32 | - If there are presentation materials, add them to the appropriate 33 | [dated meeting archives folder](https://github.com/w3c-ccg/meetings/) 34 | before the meeting. 35 | - Send agenda to public-credentials@w3.org before each meeting. Use the 36 | following format for the subject (modify date accordingly): 37 | ``` 38 | [AGENDA] W3C CCG Traceability Call - 2022-11-22 39 | ``` 40 | - Confirm in the 41 | [CCG mail archives](https://lists.w3.org/Archives/Public/public-credentials/) 42 | that the agenda was sent correctly 43 | 44 | #### During the Meeting 45 | - Be sure to click **`Start Recording`** and then **`Stop Subtitles`** 46 | - Read the IPR note 47 | 48 | > Anyone can participate in these calls. However, all substantive contributors to any CCG Work Items must be members of the CCG with full IPR agreements signed. Information about and links to the required license agreements can be found in the meeting agenda. 49 | 50 | - Make sure to link to the agenda at the beginning of the meeting (`Agenda: ...`) 51 | - Make sure the scribe is identified (`Scribe: ...` or `scribe+ ...` 52 | identifying someone else, or `scribe+` identifying oneself). Scribes, please 53 | familiarize yourself with general scribing guidance 54 | [here](https://www.w3.org/2008/04/scribe.html) and 55 | [here](https://www.w3.org/2008/xmlsec/Group/Scribe-Instructions.html). 56 | - Make sure topics are labeled when the topic changes (`Topic: ...`) 57 | - Make sure that action items are listed so that they can be added to issues 58 | later ("Action: ...") 59 | 60 | #### After the Meeting 61 | - Kick everyone from the meeting, and click **`Stop Recording`** 62 | - [Publish the minutes](https://github.com/w3c-ccg/traceability-interop/tree/main/docs/weekly-minutes) 63 | 64 | ## [Open API Specification](https://w3c-ccg.github.io/traceability-interop/) 65 | 66 | The spec contains documentation on use cases as well as required and optional 67 | API operations. 68 | -------------------------------------------------------------------------------- /OPENAPI.md: -------------------------------------------------------------------------------- 1 | # OpenAPI Specification 2 | 3 | ## Importing the OpenAPI Specification Into Postman 4 | 5 | If you are using Postman, you can import all of the available API endpoints 6 | into an easy to use collection by following the instructions below: 7 | 8 | 1. While inside a Postman Workspace, you should see an **`import`** button at 9 | the top of your list of collections. Click this to begin the importing process. 10 | 2. After clicking **`import`**, a dialog will open providing you with a couple 11 | of options for importing the collection. The easiest is to import using a 12 | **`Link`**. To do this, simply paste the following URL into the input box, 13 | and click **`Import`**: 14 | `https://w3c-ccg.github.io/traceability-interop/openapi/openapi.json`. 15 | Alternatively, you can use the **`Raw text`** option by copying the JSON 16 | found at 17 | `https://w3c-ccg.github.io/traceability-interop/openapi/openapi.json`, and 18 | pasting it into the input box. 19 | 20 | You should see something like this in your collections once you have 21 | successfully imported the spec: 22 | 23 |

24 | 25 | 26 | 27 |

28 | 29 | 30 | ## Included scripts for managing OpenAPI Specification 31 | 32 | 1. `npm run validate-spec`: This command can be used to validate the 33 | `openapi.yml` spec, ensuring there are no issues. 34 | 2. `npm run preserve`: This command is used to bundle our `openapi.yml` file 35 | into an `openapi.json` file that can be used to import the collection/spec. 36 | 37 | For additional documentation on how the `swagger-cli` can be used, visit 38 | [here](https://www.npmjs.com/package/swagger-cli). 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Traceability Interoperability Specification 2 | 3 | [![Interoperability Report](https://github.com/w3c-ccg/traceability-interop/actions/workflows/interoperability-report.yml/badge.svg)](https://github.com/w3c-ccg/traceability-interop/actions/workflows/interoperability-report.yml) 4 | [![Conformance Report](https://github.com/w3c-ccg/traceability-interop/actions/workflows/conformance-run.yml/badge.svg)](https://github.com/w3c-ccg/traceability-interop/actions/workflows/conformance-run.yml) 5 | 6 | ## About 7 | 8 | This specification describes an enterprise grade HTTP API for leveraging 9 | [W3C Decentralized Identifiers](https://www.w3.org/TR/did-core/) and 10 | [W3C Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) with 11 | [W3C CCG Traceability Vocabulary](https://w3c-ccg.github.io/traceability-vocab/) 12 | and the [VC API](https://w3c-ccg.github.io/vc-api/) when possible. 13 | 14 | We encourage contributions meeting the [Contribution 15 | Guidelines](CONTRIBUTING.md). While we prefer the creation of 16 | [Issues](https://github.com/w3c-ccg/traceability-interop/issues) and 17 | [Pull Requests](https://github.com/w3c-ccg/traceability-interop/pulls) in the 18 | GitHub repository, discussions often occur on the 19 | [public-credentials](http://lists.w3.org/Archives/Public/public-credentials/) 20 | mailing list as well, and at regular public meetings ([see `MEETINGS.md`](./MEETINGS.md)). 21 | 22 | ## Latest Spec 23 | 24 | The current version of the specification can be found at [specification](https://w3id.org/traceability/interoperability/openapi) 25 | 26 | ## Traceability Interoperability 27 | 28 | - [Getting Started](#getting-started) 29 | - [Meetings and Hosting Instructions](./MEETINGS.md) 30 | - [Latest conformance test suite results](https://w3id.org/traceability/interoperability/reports/conformance) 31 | - [Latest interoperability test suite results](https://w3id.org/traceability/interoperability/reports/interoperability) 32 | - [Historical test suite result archive](https://w3id.org/traceability/interoperability/reports/archive) 33 | 34 | ## Getting Started 35 | 36 | 37 | 38 | - [To generate a report](./reporting/README.md) 39 | - [To import the OpenAPI specification into Postman](./OPENAPI.md) 40 | - For local development of the specification you will need to run the following: 41 | ``` 42 | git clone git@github.com:w3c-ccg/traceability-interop.git 43 | cd traceability-interop 44 | npm i 45 | npm run serve 46 | ``` 47 | 48 | 49 | ## Reference Implementation 50 | 51 | To simplify the creation of test vectors for the spec, we intend to provide 52 | a reference implementation. 53 | 54 | This implementation will cover all required AND optional APIs, and will be 55 | used to ensure no breaking changes are accidentally contributed to the spec. 56 | 57 | ## Postman Test Suites 58 | 59 | To ensure conformance and interoperability, tests are conducted in a manner 60 | consistent with production environments. We maintain a set of Postman 61 | collections and client credential configurations containing 62 | [conformance](./tests) and [interoperability](./docs/tutorials) test suites. 63 | These tests are executed via GitHub actions, on demand by implementers, and 64 | on a nightly scheduled basis. Please review the linked documentation for 65 | instructions on importing these test suites into your own local Postman 66 | environment. 67 | 68 | This approach allows us to test implementations in production with the 69 | appropriate security and authorization policies in place. 70 | 71 | If you would like to register an implementation to be tested against the test 72 | suite, please 73 | [review the step-by-step instructions provided here](./environment-setup/README.md). 74 | 75 | Test suite registration is required for participation in upcoming technical 76 | demonstrations with various government and non-government entities related to 77 | trade and import/export data exchange. 78 | 79 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | This directory is served by github pages 2 | -------------------------------------------------------------------------------- /docs/imported-collection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/imported-collection.png -------------------------------------------------------------------------------- /docs/openapi/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/openapi/openapi.yml: -------------------------------------------------------------------------------- 1 | openapi: '3.0.0' 2 | info: 3 | version: 1.0.0 4 | title: Open API for Interoperable Traceability 5 | description: Identifier and Credentials APIs for DID. 6 | license: 7 | name: Apache 2.0 8 | url: https://www.apache.org/licenses/LICENSE-2.0.html 9 | 10 | servers: 11 | - url: https://api.did.actor 12 | 13 | tags: 14 | - name: Discovery 15 | - name: Identifiers 16 | - name: Credentials 17 | - name: Presentations 18 | 19 | paths: 20 | /did.json: 21 | $ref: './resources/api-configuration.yml' 22 | 23 | /identifiers/{did}: 24 | $ref: './resources/did.yml' 25 | 26 | /credentials/issue: 27 | $ref: './resources/credential-issuer.yml' 28 | /credentials/status: 29 | $ref: './resources/credential-status.yml' 30 | /credentials/verify: 31 | $ref: './resources/credential-verifier.yml' 32 | /credentials/{credential-id}: 33 | $ref: './resources/credential.yml' 34 | 35 | /presentations: 36 | $ref: './resources/presentations.yml' 37 | /presentations/prove: 38 | $ref: './resources/presentation-prover.yml' 39 | /presentations/verify: 40 | $ref: './resources/presentation-verifier.yml' 41 | /presentations/available: 42 | $ref: './resources/presentation-available.yml' 43 | /presentations/submissions: 44 | $ref: './resources/presentation-submissions.yml' 45 | 46 | components: 47 | securitySchemes: 48 | OAuth2: 49 | type: oauth2 50 | flows: 51 | clientCredentials: 52 | tokenUrl: https://example.com/oauth/token 53 | parameters: 54 | $ref: './parameters/_index.yml' 55 | schemas: 56 | $ref: './schemas/_index.yml' 57 | responses: 58 | $ref: './responses/_index.yml' 59 | -------------------------------------------------------------------------------- /docs/openapi/parameters/_index.yml: -------------------------------------------------------------------------------- 1 | # path 2 | did: 3 | $ref: "./path/did.yml" 4 | 5 | # limit 6 | limit: 7 | $ref: "./query/limit.yml" 8 | -------------------------------------------------------------------------------- /docs/openapi/parameters/header/accept.yml: -------------------------------------------------------------------------------- 1 | name: accept 2 | in: header 3 | required: true 4 | description: A verifiable credential format 5 | example: "application/vc+ld+json" 6 | schema: 7 | type: string 8 | enum: ["application/sd-jwt", "application/vc+ld+json"] 9 | -------------------------------------------------------------------------------- /docs/openapi/parameters/path/credential-id.yml: -------------------------------------------------------------------------------- 1 | name: credential-id 2 | in: path 3 | required: true 4 | description: A verifiable credential id 5 | example: "810b3c74-4e80-420d-a8fe-7bde4c978e91" 6 | schema: 7 | type: string 8 | -------------------------------------------------------------------------------- /docs/openapi/parameters/path/did.yml: -------------------------------------------------------------------------------- 1 | name: did 2 | in: path 3 | required: true 4 | description: A decentralized identifier 5 | schema: 6 | type: string 7 | example: "did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn" 8 | # example: "did:web:api.did.actor:api" 9 | -------------------------------------------------------------------------------- /docs/openapi/parameters/query/limit.yml: -------------------------------------------------------------------------------- 1 | name: limit 2 | in: query 3 | description: How many items to return at one time (max 100) 4 | required: false 5 | schema: 6 | type: integer 7 | format: int32 8 | -------------------------------------------------------------------------------- /docs/openapi/resources/api-configuration.yml: -------------------------------------------------------------------------------- 1 | get: 2 | summary: API Configuration 3 | operationId: getTraceabilityAPIConfiguration 4 | tags: 5 | - Discovery 6 | responses: 7 | "200": 8 | description: Expected response to a valid request 9 | content: 10 | application/json: 11 | schema: 12 | $ref: "../schemas/TraceabilityAPIDIDWebDocument.yml" 13 | default: 14 | $ref: "../responses/UnexpectedError.yml" 15 | -------------------------------------------------------------------------------- /docs/openapi/resources/credential-issuer.yml: -------------------------------------------------------------------------------- 1 | post: 2 | summary: Create 3 | operationId: issueCredential 4 | description: Issue a credential 5 | tags: 6 | - Credentials 7 | security: 8 | - OAuth2: [] 9 | parameters: 10 | - $ref: "../parameters/header/accept.yml" 11 | requestBody: 12 | description: Parameters for issuing the credential. 13 | content: 14 | application/json: 15 | schema: 16 | type: object 17 | required: 18 | - credential 19 | - options 20 | properties: 21 | credential: 22 | $ref: '../schemas/Credential.yml' 23 | options: 24 | $ref: '../schemas/IssueCredentialOptions.yml' 25 | 26 | responses: 27 | '201': 28 | description: Resource Created 29 | content: 30 | application/json: 31 | schema: 32 | type: object 33 | required: 34 | - verifiableCredential 35 | properties: 36 | verifiableCredential: 37 | $ref: '../schemas/SerializedVerifiableCredential.yml' 38 | '400': 39 | $ref: '../responses/BadRequest.yml' 40 | '401': 41 | $ref: '../responses/Unauthenticated.yml' 42 | '403': 43 | $ref: '../responses/Unauthorized.yml' 44 | '422': 45 | description: Unknown Issuer 46 | content: 47 | application/json: 48 | schema: 49 | $ref: '../responses/ErrUnknownIssuer.yml' 50 | '500': 51 | $ref: '../responses/UnexpectedError.yml' 52 | -------------------------------------------------------------------------------- /docs/openapi/resources/credential-status.yml: -------------------------------------------------------------------------------- 1 | post: 2 | summary: Update Status 3 | operationId: updateCredentialStatus 4 | description: Updates the status of an issued credential. 5 | tags: 6 | - Credentials 7 | security: 8 | - OAuth2: [] 9 | requestBody: 10 | description: Parameters for updating the status of the issued credential. 11 | content: 12 | application/json: 13 | schema: 14 | type: object 15 | description: Request for updating the status of an issued credential. 16 | required: 17 | - credentialId 18 | - credentialStatus 19 | properties: 20 | credentialId: 21 | type: string 22 | credentialStatus: 23 | type: array 24 | maxItems: 1 25 | items: 26 | type: object 27 | required: 28 | - type 29 | - status 30 | properties: 31 | type: 32 | type: string 33 | enum: ['StatusList2021Entry'] 34 | statusPurpose: 35 | type: string 36 | enum: ['revocation','suspension','status'] 37 | # https://www.w3.org/TR/vc-status-list/ 38 | # A value of `status` can be used to support multiple statuses, as described in the spec above. 39 | description: The value of `statusPurpose` should match the `statusPurpose` property on the StatusList2021Credential referred to by the credential being updated. 40 | status: 41 | # Traceability interoperability implementation must conform 42 | # with VC-API, which uses string for the status property. 43 | # https://w3c-ccg.github.io/vc-api/#update-status 44 | description: In the case of revocation or suspension status lists, use a string value of "1" (one) to indicate "revoked"/"suspended", or "0" (zero) to indicate otherwise. 45 | type: string 46 | enum: ['0', '1'] 47 | example: { 48 | 'credentialId': 'urn:uuid:45a44711-e457-4fa8-9b89-69fe0287c86a', 49 | 'statusPurpose': 'revocation', 50 | # // https://w3c-ccg.github.io/vc-status-rl-2020/ 51 | # // If the binary value of the position in the list is 1 (one), 52 | # // the verifiable credential is revoked, if it is 0 (zero) it is not revoked. 53 | 'credentialStatus': [{ 'type': 'StatusList2021Entry', 'status': '0' }], 54 | } 55 | responses: 56 | '200': 57 | description: Expected response to a valid request 58 | content: 59 | application/json: 60 | schema: 61 | type: object 62 | '400': 63 | $ref: '../responses/BadRequest.yml' 64 | '401': 65 | $ref: '../responses/Unauthenticated.yml' 66 | '403': 67 | $ref: '../responses/Unauthorized.yml' 68 | '404': 69 | $ref: '../responses/NotFound.yml' 70 | '500': 71 | $ref: '../responses/UnexpectedError.yml' 72 | -------------------------------------------------------------------------------- /docs/openapi/resources/credential-verifier.yml: -------------------------------------------------------------------------------- 1 | post: 2 | summary: Verify 3 | operationId: verifyCredential 4 | description: Verify a credential 5 | tags: 6 | - Credentials 7 | security: 8 | - OAuth2: [] 9 | parameters: 10 | - $ref: "../parameters/header/accept.yml" 11 | requestBody: 12 | description: Parameters for verifying a credential. 13 | content: 14 | application/json: 15 | schema: 16 | type: object 17 | required: 18 | - verifiableCredential 19 | properties: 20 | verifiableCredential: 21 | $ref: "../schemas/SerializedVerifiableCredential.yml" 22 | 23 | responses: 24 | "200": 25 | description: Expected response to a valid request 26 | content: 27 | application/json: 28 | schema: 29 | $ref: "../schemas/VerificationResult.yml" 30 | '400': 31 | $ref: '../responses/BadRequest.yml' 32 | '401': 33 | $ref: '../responses/Unauthenticated.yml' 34 | '403': 35 | $ref: '../responses/Unauthorized.yml' 36 | default: 37 | $ref: "../responses/UnexpectedError.yml" 38 | -------------------------------------------------------------------------------- /docs/openapi/resources/credential.yml: -------------------------------------------------------------------------------- 1 | 2 | get: 3 | summary: Get Credential 4 | operationId: getCredentialById 5 | description: Get a verifiable credential by id. Required to make revocable credentials. 6 | tags: 7 | - Credentials 8 | security: 9 | - {} 10 | - OAuth2: [] 11 | parameters: 12 | - $ref: "../parameters/path/credential-id.yml" 13 | responses: 14 | "200": 15 | description: Expected response to a valid request 16 | content: 17 | application/json: 18 | schema: 19 | $ref: "../schemas/StatusListCredential.yml" 20 | '401': 21 | $ref: '../responses/Unauthenticated.yml' 22 | '403': 23 | $ref: '../responses/Unauthorized.yml' 24 | '404': 25 | $ref: '../responses/NotFound.yml' 26 | default: 27 | $ref: "../responses/UnexpectedError.yml" 28 | -------------------------------------------------------------------------------- /docs/openapi/resources/did.yml: -------------------------------------------------------------------------------- 1 | get: 2 | summary: Resolve 3 | operationId: resolve 4 | description: Get a DID's latest keys, services and capabilities 5 | tags: 6 | - Identifiers 7 | security: 8 | - OAuth2: [] 9 | parameters: 10 | - $ref: "../parameters/path/did.yml" 11 | responses: 12 | "200": 13 | description: Expected response to a valid request 14 | content: 15 | application/json: 16 | schema: 17 | $ref: "../schemas/DidResolutionResponse.yml" 18 | "400": 19 | description: Bad Request 20 | content: 21 | application/json: 22 | schema: 23 | $ref: '../responses/ErrInvalidDID.yml' 24 | '401': 25 | $ref: '../responses/Unauthenticated.yml' 26 | '403': 27 | $ref: '../responses/Unauthorized.yml' 28 | "404": 29 | $ref: '../responses/NotFound.yml' 30 | default: 31 | $ref: "../responses/UnexpectedError.yml" 32 | -------------------------------------------------------------------------------- /docs/openapi/resources/presentation-available.yml: -------------------------------------------------------------------------------- 1 | post: 2 | summary: Notify 3 | operationId: notifyPresentationAvailable 4 | description: Start a presentation exchange flow 5 | tags: 6 | - Presentations 7 | requestBody: 8 | description: Description of the flow 9 | content: 10 | application/json: 11 | schema: 12 | $ref: "../schemas/NotifyPresentationAvailableRequest.yml" 13 | 14 | responses: 15 | "200": 16 | description: Expected response to a valid request 17 | content: 18 | application/json: 19 | schema: 20 | $ref: "../schemas/NotifyPresentationAvailableResponse.yml" 21 | default: 22 | $ref: "../responses/UnexpectedError.yml" 23 | -------------------------------------------------------------------------------- /docs/openapi/resources/presentation-prover.yml: -------------------------------------------------------------------------------- 1 | post: 2 | summary: Create 3 | operationId: provePresentation 4 | description: Create a presentation 5 | tags: 6 | - Presentations 7 | security: 8 | - OAuth2: [] 9 | requestBody: 10 | description: Parameters for creating the presentation. 11 | content: 12 | application/json: 13 | schema: 14 | type: object 15 | properties: 16 | presentation: 17 | $ref: "../schemas/Presentation.yml" 18 | options: 19 | allOf: 20 | - $ref: "../schemas/PresentationOptions.yml" 21 | - type: object 22 | properties: 23 | type: 24 | type: string 25 | description: A linked data suite proof type 26 | enum: ["Ed25519Signature2018"] 27 | example: Ed25519Signature2018 28 | responses: 29 | "201": 30 | description: Expected response to a valid request 31 | content: 32 | application/json: 33 | schema: 34 | type: object 35 | required: 36 | - verifiablePresentation 37 | properties: 38 | verifiablePresentation: 39 | $ref: "../schemas/SerializedVerifiablePresentation.yml" 40 | '401': 41 | $ref: '../responses/Unauthenticated.yml' 42 | '403': 43 | $ref: '../responses/Unauthorized.yml' 44 | default: 45 | $ref: "../responses/UnexpectedError.yml" 46 | -------------------------------------------------------------------------------- /docs/openapi/resources/presentation-submissions.yml: -------------------------------------------------------------------------------- 1 | post: 2 | summary: Submit 3 | operationId: storePresentation 4 | description: End a presentation exchange flow 5 | tags: 6 | - Presentations 7 | requestBody: 8 | description: A Verifiable Presentation to Store 9 | content: 10 | application/json: 11 | schema: 12 | $ref: "../schemas/VerifiablePresentation.yml" 13 | 14 | responses: 15 | "200": 16 | description: Expected response to a valid request 17 | content: 18 | application/json: 19 | schema: 20 | $ref: "../schemas/VerificationResult.yml" 21 | default: 22 | $ref: "../responses/UnexpectedError.yml" 23 | -------------------------------------------------------------------------------- /docs/openapi/resources/presentation-verifier.yml: -------------------------------------------------------------------------------- 1 | post: 2 | summary: Verify 3 | operationId: verifyPresentation 4 | description: Verify a presentation 5 | tags: 6 | - Presentations 7 | security: 8 | - OAuth2: [] 9 | requestBody: 10 | description: Parameters for verifying the presentation. 11 | content: 12 | application/json: 13 | schema: 14 | type: object 15 | properties: 16 | verifiablePresentation: 17 | $ref: "../schemas/SerializedVerifiablePresentation.yml" 18 | options: 19 | $ref: "../schemas/PresentationOptions.yml" 20 | responses: 21 | "200": 22 | description: Expected response to a valid request 23 | content: 24 | application/json: 25 | schema: 26 | $ref: "../schemas/VerificationResult.yml" 27 | '401': 28 | $ref: '../responses/Unauthenticated.yml' 29 | '403': 30 | $ref: '../responses/Unauthorized.yml' 31 | default: 32 | $ref: "../responses/UnexpectedError.yml" 33 | -------------------------------------------------------------------------------- /docs/openapi/resources/presentations.yml: -------------------------------------------------------------------------------- 1 | post: 2 | summary: Present 3 | operationId: submitPresentationWithOAuth2Security 4 | description: > 5 | Create a presentation. This endpoint allows clients holding a valid OAuth2 6 | access token to create a presentation. 7 | tags: 8 | - Presentations 9 | security: 10 | - OAuth2: [] 11 | requestBody: 12 | description: Description of the flow 13 | content: 14 | application/json: 15 | schema: 16 | $ref: '../schemas/TraceablePresentation.yml' 17 | responses: 18 | '200': 19 | description: Expected response to a valid request 20 | content: 21 | application/json: 22 | schema: 23 | type: object 24 | '401': 25 | $ref: '../responses/Unauthenticated.yml' 26 | '403': 27 | $ref: '../responses/Unauthorized.yml' 28 | default: 29 | $ref: '../responses/UnexpectedError.yml' 30 | -------------------------------------------------------------------------------- /docs/openapi/responses/BadRequest.yml: -------------------------------------------------------------------------------- 1 | description: Bad Request 2 | content: 3 | application/json: 4 | schema: 5 | $ref: '../schemas/Error.yml' 6 | -------------------------------------------------------------------------------- /docs/openapi/responses/ErrInvalidDID.yml: -------------------------------------------------------------------------------- 1 | description: | 2 | ErrInvalidDID is returned when the request path contains an invalid DID 3 | parameter. 4 | $ref: '../schemas/Error.yml' 5 | -------------------------------------------------------------------------------- /docs/openapi/responses/ErrUnknownIssuer.yml: -------------------------------------------------------------------------------- 1 | description: | 2 | ErrUnknownIssuer is returned when the implementation does not have private 3 | key material for the requested `issuer`, and is therefore unable to issue 4 | the requested credential. 5 | $ref: '../schemas/Error.yml' 6 | -------------------------------------------------------------------------------- /docs/openapi/responses/NotFound.yml: -------------------------------------------------------------------------------- 1 | description: Not Found 2 | content: 3 | application/json: 4 | schema: 5 | $ref: '../schemas/Error.yml' 6 | -------------------------------------------------------------------------------- /docs/openapi/responses/NullResponse.yml: -------------------------------------------------------------------------------- 1 | description: Null response 2 | -------------------------------------------------------------------------------- /docs/openapi/responses/Unauthenticated.yml: -------------------------------------------------------------------------------- 1 | description: Unauthorized 2 | content: 3 | application/json: 4 | schema: 5 | $ref: '../schemas/Error.yml' 6 | -------------------------------------------------------------------------------- /docs/openapi/responses/Unauthorized.yml: -------------------------------------------------------------------------------- 1 | description: Forbidden 2 | content: 3 | application/json: 4 | schema: 5 | $ref: '../schemas/Error.yml' 6 | -------------------------------------------------------------------------------- /docs/openapi/responses/UnexpectedError.yml: -------------------------------------------------------------------------------- 1 | description: Unexpected Error 2 | content: 3 | application/json: 4 | schema: 5 | $ref: '../schemas/Error.yml' 6 | -------------------------------------------------------------------------------- /docs/openapi/responses/_index.yml: -------------------------------------------------------------------------------- 1 | BadRequest: 2 | $ref: './BadRequest.yml' 3 | UnexpectedError: 4 | $ref: './UnexpectedError.yml' 5 | NullResponse: 6 | $ref: './NullResponse.yml' 7 | -------------------------------------------------------------------------------- /docs/openapi/schemas/Credential.yml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - '@context' 4 | - type 5 | - issuer 6 | - issuanceDate 7 | - credentialSubject 8 | properties: 9 | '@context': 10 | description: | 11 | The JSON-LD Context defining all terms in the Credential. This array 12 | SHOULD contain "https://w3id.org/traceability/v1". 13 | type: array 14 | items: 15 | type: string 16 | id: 17 | description: The IRI identifying the Credential 18 | type: string 19 | type: 20 | description: The Type of the Credential 21 | type: array 22 | items: 23 | type: string 24 | minItems: 1 25 | issuer: 26 | description: This value MUST match the assertionMethod used to create the Verifiable Credential. 27 | oneOf: 28 | - type: string 29 | - type: object 30 | required: 31 | - id 32 | properties: 33 | id: 34 | description: The IRI identifying the Issuer 35 | type: string 36 | issuanceDate: 37 | description: This value MUST be an XML Date Time String 38 | type: string 39 | credentialSubject: 40 | type: object 41 | properties: 42 | id: 43 | description: The IRI identifying the Subject 44 | type: string 45 | 46 | example: 47 | { 48 | '@context': [ 49 | 'https://www.w3.org/2018/credentials/v1', 50 | 'https://w3id.org/traceability/v1' 51 | ], 52 | 'id': 'urn:uuid:07aa969e-b40d-4c1b-ab46-ded252003ded', 53 | 'type': ['VerifiableCredential'], 54 | 'issuer': 'did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn', 55 | 'issuanceDate': '2010-01-01T19:23:24Z', 56 | 'credentialSubject': { 57 | 'id': 'did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn' 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /docs/openapi/schemas/DidResolutionResponse.yml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - didDocument 4 | properties: 5 | didDocument: 6 | type: object 7 | required: 8 | - service 9 | properties: 10 | service: 11 | type: array 12 | minItems: 1 13 | items: 14 | type: object 15 | required: 16 | - type 17 | - serviceEndpoint 18 | properties: 19 | type: 20 | oneOf: 21 | - type: string 22 | - type: array 23 | items: 24 | type: string 25 | serviceEndpoint: 26 | type: string 27 | didResolutionMetadata: 28 | type: object 29 | didDocumentMetadata: 30 | type: object 31 | -------------------------------------------------------------------------------- /docs/openapi/schemas/Error.yml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - message 4 | properties: 5 | message: 6 | type: string 7 | details: 8 | oneOf: 9 | - type: string 10 | - type: array 11 | items: 12 | type: string 13 | - type: object 14 | additionalProperties: true 15 | -------------------------------------------------------------------------------- /docs/openapi/schemas/Holder.yml: -------------------------------------------------------------------------------- 1 | title: Holder 2 | type: object 3 | properties: 4 | id: 5 | type: string 6 | example: 7 | { 8 | "id": "did:web:sender.example.com" 9 | } 10 | -------------------------------------------------------------------------------- /docs/openapi/schemas/IssueCredentialOptions.yml: -------------------------------------------------------------------------------- 1 | title: Issue Credential Options 2 | type: object 3 | description: Options for issuing a verifiable credential 4 | required: 5 | - type 6 | properties: 7 | type: 8 | type: string 9 | description: The securing mechanism requested. 10 | enum: ['Ed25519Signature2018'] 11 | created: 12 | type: string 13 | description: Date the proof was created. This value MUST be an XML Date Time String. 14 | credentialStatus: 15 | type: object 16 | description: The method of credential status. 17 | required: 18 | - type 19 | properties: 20 | type: 21 | type: string 22 | description: The type of credential status. 23 | enum: ['StatusList2021Entry'] 24 | example: 25 | { 26 | 'type': 'Ed25519Signature2018', 27 | 'created': '2020-04-02T18:28:08Z', 28 | 'credentialStatus': { 'type': 'StatusList2021Entry' }, 29 | } 30 | -------------------------------------------------------------------------------- /docs/openapi/schemas/NotifyPresentationAvailableRequest.yml: -------------------------------------------------------------------------------- 1 | title: Notify Presentation Available 2 | type: object 3 | properties: 4 | query: 5 | type: object 6 | description: See https://w3c-ccg.github.io/vp-request-spec/#format 7 | properties: 8 | type: 9 | type: string 10 | description: "The type of query the server should reply with." 11 | credentialQuery: 12 | type: object 13 | description: "Details of the client's available presentation" 14 | example: 15 | { 16 | "query": 17 | [ 18 | { 19 | "type": "QueryByExample", 20 | "credentialQuery": 21 | [ 22 | { 23 | "type": ["VerifiableCredential"], 24 | "reason": "We want to present credentials.", 25 | }, 26 | ], 27 | }, 28 | ], 29 | } 30 | -------------------------------------------------------------------------------- /docs/openapi/schemas/NotifyPresentationAvailableResponse.yml: -------------------------------------------------------------------------------- 1 | title: Notify Presentation Requirements 2 | type: object 3 | properties: 4 | query: 5 | type: object 6 | description: See https://w3c-ccg.github.io/vp-request-spec/#format 7 | domain: 8 | type: string 9 | description: See https://w3id.org/security#domain 10 | challenge: 11 | type: string 12 | description: See https://w3id.org/security#challenge 13 | example: 14 | { 15 | "query": 16 | [ 17 | { 18 | "type": "QueryByExample", 19 | "credentialQuery": 20 | { 21 | "example": 22 | { 23 | "@context": ["https://www.w3.org/2018/credentials/v1"], 24 | "type": ["VerifiableCredential"], 25 | }, 26 | }, 27 | }, 28 | ], 29 | "domain": "verifier.example.com", 30 | "challenge": "3182bdea-63d9-11ea-b6de-3b7c1404d57f", 31 | } 32 | -------------------------------------------------------------------------------- /docs/openapi/schemas/Presentation.yml: -------------------------------------------------------------------------------- 1 | title: Presentation 2 | type: object 3 | required: 4 | - '@context' 5 | - id 6 | - type 7 | properties: 8 | '@context': 9 | description: | 10 | The JSON-LD Context defining all terms in the Presentation. This array 11 | MUST contain "https://w3id.org/traceability/v1". 12 | type: array 13 | items: 14 | type: string 15 | id: 16 | type: string 17 | type: 18 | type: array 19 | items: 20 | type: string 21 | holder: 22 | type: string 23 | verifiableCredential: 24 | type: array 25 | items: 26 | $ref: './SerializedVerifiableCredential.yml' 27 | 28 | example: 29 | { 30 | '@context': [ 31 | 'https://www.w3.org/2018/credentials/v1', 32 | 'https://w3id.org/traceability/v1' 33 | ], 34 | 'id': 'urn:uuid:48a46c8d-9313-45ec-a0be-7984a527223a', 35 | 'type': ['VerifiablePresentation'], 36 | 'holder': 'did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn', 37 | 'verifiableCredential': 38 | [ 39 | { 40 | '@context': [ 41 | 'https://www.w3.org/2018/credentials/v1', 42 | 'https://w3id.org/traceability/v1' 43 | ], 44 | 'id': 'urn:uuid:07aa969e-b40d-4c1b-ab46-ded252003ded', 45 | 'type': ['VerifiableCredential'], 46 | 'issuer': 'did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn', 47 | 'issuanceDate': '2010-01-01T19:23:24Z', 48 | 'credentialSubject': { 49 | 'id': 'did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn' 50 | }, 51 | }, 52 | ], 53 | } 54 | -------------------------------------------------------------------------------- /docs/openapi/schemas/PresentationOptions.yml: -------------------------------------------------------------------------------- 1 | title: Prove Presentation Options 2 | type: object 3 | description: Options for proving a verifiable presentation 4 | required: 5 | - challenge 6 | properties: 7 | domain: 8 | type: string 9 | example: "jobs.example" 10 | challenge: 11 | type: string 12 | example: "1f44d55f-f161-4938-a659-f8026467f126" 13 | example: 14 | { 15 | 'domain': 'api.did.actor', 16 | 'challenge': 'example' 17 | } 18 | -------------------------------------------------------------------------------- /docs/openapi/schemas/SerializedVerifiableCredential.yml: -------------------------------------------------------------------------------- 1 | title: Serialized Verifiable Credential 2 | oneOf: 3 | - allOf: 4 | - $ref: "./VerifiableCredential.yml" 5 | - type: object 6 | required: 7 | - id 8 | - $ref: "./VC-JWT.yml" 9 | 10 | example: 11 | { 12 | "@context": [ 13 | "https://www.w3.org/2018/credentials/v1", 14 | "https://w3id.org/traceability/v1" 15 | ], 16 | "id": "urn:uuid:07aa969e-b40d-4c1b-ab46-ded252003ded", 17 | "type": ["VerifiableCredential"], 18 | "issuer": "did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn", 19 | "issuanceDate": "2010-01-01T19:23:24Z", 20 | "credentialSubject": { 21 | "id": "did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/openapi/schemas/SerializedVerifiablePresentation.yml: -------------------------------------------------------------------------------- 1 | title: Serialized Verifiable Presentation 2 | oneOf: 3 | - $ref: "./VerifiablePresentation.yml" 4 | - $ref: "./TraceablePresentation.yml" 5 | - $ref: "./VP-JWT.yml" 6 | 7 | example: 8 | { 9 | "@context": 10 | [ 11 | "https://www.w3.org/2018/credentials/v1", 12 | "https://w3id.org/traceability/v1" 13 | ], 14 | "id": "urn:uuid:48a46c8d-9313-45ec-a0be-7984a527223a", 15 | "type": ["VerifiablePresentation"], 16 | "holder": "did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn", 17 | "verifiableCredential": 18 | [ 19 | { 20 | "@context": 21 | [ 22 | "https://www.w3.org/2018/credentials/v1", 23 | "https://w3id.org/traceability/v1" 24 | ], 25 | "id": "urn:uuid:07aa969e-b40d-4c1b-ab46-ded252003ded", 26 | "type": ["VerifiableCredential"], 27 | "issuer": "did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn", 28 | "issuanceDate": "2010-01-01T19:73:24Z", 29 | "credentialSubject": { 30 | "id": "did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn" 31 | }, 32 | }, 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /docs/openapi/schemas/StatusListCredential.yml: -------------------------------------------------------------------------------- 1 | title: Status List Verifiable Credential 2 | type: object 3 | allOf: 4 | - $ref: './Credential.yml' 5 | - type: object 6 | required: 7 | - id 8 | example: 9 | { 10 | "@context": [ 11 | "https://www.w3.org/2018/credentials/v1", 12 | "https://w3id.org/vc/status-list/2021/v1" 13 | ], 14 | "credentialSubject": { 15 | "encodedList": "H4sIAAAAAAAA/2IYBaNgFIyCUTAKRsGIA4AAAAD//5666PEACAAA", 16 | "statusPurpose": "revocation", 17 | "statusSize": 1, 18 | "ttl": 5000, 19 | "type": "StatusList2021", 20 | "validFrom": "2023-06-19T23:31:48Z", 21 | "validUntil": "2023-06-20T23:31:48Z" 22 | }, 23 | "id": "https://vendor.example/credentials/status-list/42", 24 | "issuanceDate": "2023-06-19T23:31:48Z", 25 | "issuer": "did:web:vendor.example", 26 | "type": [ 27 | "VerifiableCredential", 28 | "StatusList2021Credential" 29 | ] 30 | } -------------------------------------------------------------------------------- /docs/openapi/schemas/TraceabilityAPIDIDWebDocument.yml: -------------------------------------------------------------------------------- 1 | title: Traceability API DID Web Document 2 | type: object 3 | properties: 4 | '@context': 5 | oneOf: 6 | - type: string 7 | - type: array 8 | uniqueItems: true 9 | items: 10 | oneOf: 11 | - type: string 12 | - type: object 13 | id: 14 | type: string 15 | service: 16 | type: array 17 | items: 18 | type: object 19 | properties: 20 | id: 21 | type: string 22 | type: 23 | type: array 24 | items: 25 | type: string 26 | serviceEndpoint: 27 | type: string 28 | 29 | example: 30 | { 31 | "@context": 32 | [ 33 | "https://www.w3.org/ns/did/v1", 34 | "https://w3id.org/traceability/v1", 35 | ], 36 | "id": "did:web:api.did.actor:api", 37 | "service": 38 | [ 39 | { 40 | "id": "did:web:api.did.actor:api#traceability-api", 41 | "type": ["TraceabilityAPI"], 42 | "serviceEndpoint": "https://api.did.actor/api", 43 | }, 44 | ], 45 | } 46 | -------------------------------------------------------------------------------- /docs/openapi/schemas/TraceablePresentation.yml: -------------------------------------------------------------------------------- 1 | title: Traceable Presentation 2 | type: object 3 | allOf: 4 | - $ref: "./VerifiablePresentation.yml" 5 | - type: object 6 | properties: 7 | holder: 8 | $ref: "./Holder.yml" 9 | workflow: 10 | $ref: "./Workflow.yml" 11 | example: 12 | { 13 | "verifiablePresentation": { 14 | "@context": [ 15 | "https://www.w3.org/2018/credentials/v1", 16 | "https://w3id.org/traceability/v1" 17 | ], 18 | "type": [ 19 | "VerifiablePresentation", 20 | "TraceablePresentation" 21 | ], 22 | "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", 23 | "holder":{ 24 | "id": "did:web:sender.example.com" 25 | }, 26 | "workflow": { 27 | "definition": [ 28 | "https://w3id.org/traceability#us-cbp-entry" 29 | ], 30 | "instance": [ 31 | "urn:uuid:f5fb6ce4-b0b1-41b8-89b0-331ni58b7ee0" 32 | ], 33 | }, 34 | "verifiableCredential": [ 35 | { 36 | "@context": [ 37 | "https://www.w3.org/2018/credentials/v1", 38 | "https://w3id.org/traceability/v1" 39 | ], 40 | "id": "urn:uuid:07aa969e-b40d-4c1b-ab46-ded252003ded", 41 | "type": ["VerifiableCredential"], 42 | "issuer": "did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn", 43 | "issuanceDate": "2010-01-01T19:23:24Z", 44 | "credentialSubject": { 45 | "id": "urn:uuid:55a0a7e6-6140-47ab-8c6d-n155f403710c" 46 | } 47 | } 48 | ] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /docs/openapi/schemas/VC-JWT.yml: -------------------------------------------------------------------------------- 1 | title: VC JWT 2 | type: string 3 | example: "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3RpU3pxRjlrcXdkVThWa2RCS3g1NkVZelhmcGduTlBVQUd6bnBpY05pV2ZuI3o2TWt0aVN6cUY5a3F3ZFU4VmtkQkt4NTZFWXpYZnBnbk5QVUFHem5waWNOaVdmbiJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWt0aVN6cUY5a3F3ZFU4VmtkQkt4NTZFWXpYZnBnbk5QVUFHem5waWNOaVdmbiIsInN1YiI6ImRpZDprZXk6ejZNa3RpU3pxRjlrcXdkVThWa2RCS3g1NkVZelhmcGduTlBVQUd6bnBpY05pV2ZuIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3czaWQub3JnL3NlY3VyaXR5L3N1aXRlcy9qd3MtMjAyMC92MSJdLCJpZCI6InVybjp1dWlkOjA3YWE5NjllLWI0MGQtNGMxYi1hYjQ2LWRlZDI1MjAwM2RlZCIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmtleTp6Nk1rdGlTenFGOWtxd2RVOFZrZEJLeDU2RVl6WGZwZ25OUFVBR3pucGljTmlXZm4iLCJpc3N1YW5jZURhdGUiOiIyMDEwLTAxLTAxVDE5OjIzOjI0WiIsImNyZWRlbnRpYWxTdWJqZWN0IjoiZGlkOmtleTp6Nk1rdGlTenFGOWtxd2RVOFZrZEJLeDU2RVl6WGZwZ25OUFVBR3pucGljTmlXZm4ifSwianRpIjoidXJuOnV1aWQ6MDdhYTk2OWUtYjQwZC00YzFiLWFiNDYtZGVkMjUyMDAzZGVkIiwibmJmIjoxMjYyMzczODA0fQ.ZXlKaGJHY2lPaUpGWkVSVFFTSXNJbUkyTkNJNlptRnNjMlVzSW1OeWFYUWlPbHNpWWpZMElsMTkuLlBUZ1N5UndTRmdRRmZRQXJRSkVfUm43c3cyNzJRZnhlTUZjYk16R05KZDRKVGtWS3d4a2p1UzRXQV9xTGdhM2NGYzRKd0JILXJPMk5haTFfRExsWEF3" 4 | -------------------------------------------------------------------------------- /docs/openapi/schemas/VP-JWT.yml: -------------------------------------------------------------------------------- 1 | title: VP JWT 2 | type: string 3 | example: "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3RpU3pxRjlrcXdkVThWa2RCS3g1NkVZelhmcGduTlBVQUd6bnBpY05pV2ZuI3o2TWt0aVN6cUY5a3F3ZFU4VmtkQkt4NTZFWXpYZnBnbk5QVUFHem5waWNOaVdmbiJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWt0aVN6cUY5a3F3ZFU4VmtkQkt4NTZFWXpYZnBnbk5QVUFHem5waWNOaVdmbiIsInN1YiI6ImRpZDprZXk6ejZNa3RpU3pxRjlrcXdkVThWa2RCS3g1NkVZelhmcGduTlBVQUd6bnBpY05pV2ZuIiwidnAiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL3czaWQub3JnL3NlY3VyaXR5L3N1aXRlcy9qd3MtMjAyMC92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iXSwiaG9sZGVyIjoiZGlkOmtleTp6Nk1rdGlTenFGOWtxd2RVOFZrZEJLeDU2RVl6WGZwZ25OUFVBR3pucGljTmlXZm4iLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6W3siQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiaWQiOiJ1cm46dXVpZDowN2FhOTY5ZS1iNDBkLTRjMWItYWI0Ni1kZWQyNTIwMDNkZWQiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImlzc3VlciI6ImRpZDprZXk6ejZNa3RpU3pxRjlrcXdkVThWa2RCS3g1NkVZelhmcGduTlBVQUd6bnBpY05pV2ZuIiwiaXNzdWFuY2VEYXRlIjoiMjAxMC0wMS0wMVQxOToyMzoyNFoiLCJjcmVkZW50aWFsU3ViamVjdCI6ImRpZDprZXk6ejZNa3RpU3pxRjlrcXdkVThWa2RCS3g1NkVZelhmcGduTlBVQUd6bnBpY05pV2ZuIiwicHJvb2YiOnsidHlwZSI6IkVkMjU1MTlTaWduYXR1cmUyMDE4IiwiY3JlYXRlZCI6IjIwMjEtMTAtMzBUMTk6MTY6MzBaIiwidmVyaWZpY2F0aW9uTWV0aG9kIjoiZGlkOmtleTp6Nk1rdGlTenFGOWtxd2RVOFZrZEJLeDU2RVl6WGZwZ25OUFVBR3pucGljTmlXZm4jejZNa3RpU3pxRjlrcXdkVThWa2RCS3g1NkVZelhmcGduTlBVQUd6bnBpY05pV2ZuIiwicHJvb2ZQdXJwb3NlIjoiYXNzZXJ0aW9uTWV0aG9kIiwiandzIjoiZXlKaGJHY2lPaUpGWkVSVFFTSXNJbUkyTkNJNlptRnNjMlVzSW1OeWFYUWlPbHNpWWpZMElsMTkuLnB1ZXRCWVMzcGtZbFl6QWVjQmlULVdraWdZQWxWYnNscno5d1BGbms5Slc0QXdqcnBKdmNzU2RaSlBoWnROeV9teU1KVU56Q19RYVl5dzNuaTFWMEJBIn19XX0sImF1ZCI6InN0cmluZyIsIm5vbmNlIjoiMWY0NGQ1NWYtZjE2MS00OTM4LWE2NTktZjgwMjY0NjdmMTI2In0.ZXlKaGJHY2lPaUpGWkVSVFFTSXNJbUkyTkNJNlptRnNjMlVzSW1OeWFYUWlPbHNpWWpZMElsMTkuLlcwT0dYSkVyT2xrSy1aSkxDR3Q1SWQ2WG9KYnZERGI2cGV2ZmhsblhWOW9CTWhQTTdhRFlBSmFRVFBKeG9neW4xd19MTHgzRHdnN3hvVGRwZllSTUJB" 4 | -------------------------------------------------------------------------------- /docs/openapi/schemas/VerifiableCredential.yml: -------------------------------------------------------------------------------- 1 | title: Verifiable Credential 2 | type: object 3 | allOf: 4 | - $ref: "./Credential.yml" 5 | - type: object 6 | example: 7 | { 8 | "@context": [ 9 | "https://www.w3.org/2018/credentials/v1", 10 | "https://w3id.org/traceability/v1" 11 | ], 12 | "id": "urn:uuid:07aa969e-b40d-4c1b-ab46-ded252003ded", 13 | "type": ["VerifiableCredential"], 14 | "issuer": "did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn", 15 | "issuanceDate": "2010-01-01T19:23:24Z", 16 | "credentialSubject": { 17 | "id": "did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/openapi/schemas/VerifiablePresentation.yml: -------------------------------------------------------------------------------- 1 | title: Verifiable Presentation 2 | type: object 3 | allOf: 4 | - $ref: "./Presentation.yml" 5 | - type: object 6 | example: 7 | { 8 | "verifiablePresentation": { 9 | "@context": [ 10 | "https://www.w3.org/2018/credentials/v1" 11 | ], 12 | "holder": "did:web:vc.mesur.io:v1", 13 | "id": "urn:uuid:738c4139-4dea-412e-94b7-c31fd079247a", 14 | "type": "VerifiablePresentation", 15 | "verifiableCredential": [ 16 | { 17 | "@context": [ 18 | "https://www.w3.org/2018/credentials/v1", 19 | "https://w3id.org/vc/status-list/2021/v1" 20 | ], 21 | "credentialStatus": { 22 | "id": "https://vc.mesur.io/v1/credentials/status#9", 23 | "statusListCredential": "https://vc.mesur.io/v1/credentials/status", 24 | "statusListIndex": 9, 25 | "statusPurpose": "revocation", 26 | "type": "StatusList2021Entry" 27 | }, 28 | "credentialSubject": { 29 | "id": "did:example:123" 30 | }, 31 | "id": "urn:uuid:2569b762-e23c-475d-8f38-66ce813b4d17", 32 | "issuanceDate": "2023-06-19T23:16:57.945Z", 33 | "issuer": "did:web:vc.mesur.io:v1", 34 | "type": [ 35 | "VerifiableCredential" 36 | ] 37 | } 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docs/openapi/schemas/Verification.yml: -------------------------------------------------------------------------------- 1 | title: Verification 2 | type: object 3 | properties: 4 | title: 5 | type: string 6 | enum: 7 | - Activation 8 | - Expired 9 | - Proof 10 | - Revocation 11 | - Signature 12 | status: 13 | type: string 14 | enum: 15 | - good 16 | - bad 17 | description: 18 | type: string 19 | required: 20 | - title 21 | - status 22 | example: 23 | { 24 | "title": "Activation", 25 | "status": "good", 26 | "description": "This credential activated 2 weeks ago" 27 | } 28 | -------------------------------------------------------------------------------- /docs/openapi/schemas/VerificationResult.yml: -------------------------------------------------------------------------------- 1 | title: Verification Result 2 | type: object 3 | properties: 4 | verified: 5 | type: boolean 6 | required: 7 | - verified 8 | additionalProperties: true 9 | example: 10 | { 11 | "verified": true 12 | } 13 | -------------------------------------------------------------------------------- /docs/openapi/schemas/Workflow.yml: -------------------------------------------------------------------------------- 1 | title: Workflow 2 | type: object 3 | properties: 4 | instance: 5 | type: array 6 | items: 7 | type: string 8 | definition: 9 | type: array 10 | items: 11 | type: string 12 | example: 13 | { 14 | "@context": ["https://w3id.org/traceability/v1"], 15 | "type": ["Workflow"], 16 | "workflow": 17 | { 18 | "instance": ["urn:uuid:f5fb6ce4-b0b1-41b8-89b0-331ni58b7ee0"], 19 | "definition": ["https://w3id.org/traceability#us-cbp-entry"], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /docs/openapi/schemas/_index.yml: -------------------------------------------------------------------------------- 1 | Error: 2 | $ref: "./Error.yml" 3 | -------------------------------------------------------------------------------- /docs/reports/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/reports/.gitkeep -------------------------------------------------------------------------------- /docs/reports/archive/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /docs/spec/resources/standards-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/spec/resources/standards-stack.png -------------------------------------------------------------------------------- /docs/spec/resources/workflow-def-ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/spec/resources/workflow-def-ex.png -------------------------------------------------------------------------------- /docs/spec/resources/workflow-inst-ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/spec/resources/workflow-inst-ex.png -------------------------------------------------------------------------------- /docs/spec/sections/abstract.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Abstract

4 |

5 | This specification describes discovery and exchange mechanisms of [[[VC-DATA-MODEL]]] [[VC-DATA-MODEL]], 6 | particularly as they relate to supply chain use cases. 7 | Exchanged Verifiable Credentials often implement schemas defined in 8 | the [[[TRACE-VOCAB]]] [[TRACE-VOCAB]].. 9 |

10 |

11 | Using Verifiable Credentials to represent supply chain data enables interoperability between disparate actors, 12 | and provides the ability to cryptographically verify the authenticity of the data. 13 |

14 |

15 | This specification uses [[[DID-CORE]]] [[DID-CORE]] 16 | to identify organizations within a supply chain. This enables organizations to interact with parties 17 | outside their traditional supply chains, and removes the need for a central authority to identify supply chain 18 | organizations. 19 |

20 |
21 | -------------------------------------------------------------------------------- /docs/spec/sections/api-spec.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Services

4 |

5 | This section describes the HTTP services a conformant Traceability API MUST 6 | implement. 7 |

8 | 9 |

See the Open API for Interoperable Traceability for the full API definition.

10 |
11 | -------------------------------------------------------------------------------- /docs/spec/sections/authorization.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Authentication

4 |

Implementations MUST support OAuth 2.0 Client Credentials based authentication.

5 | 6 |

7 | Client Credentials Grant is appropriate for verifying trusted application authorization for Internal/External APIs. 8 |

9 | 10 |

See these links for more information on Machine to Machine API authorization:

11 | 12 | 28 | 29 |
30 |

Presentation Authentication

31 |

32 | The following sequence of events is necessary for a Holder to present data to a Verifier: 33 |

34 | 45 |
46 | 47 |
48 | -------------------------------------------------------------------------------- /docs/spec/sections/mandatory-to-implement-algorithms.html: -------------------------------------------------------------------------------- 1 |
2 |

Mandatory Algorithms

3 |

4 | The following digital signature algorithms 5 | MUST be implemented by conforming issuers and verifiers. 6 |

7 | 12 |
13 | -------------------------------------------------------------------------------- /docs/spec/sections/selective-disclosure.html: -------------------------------------------------------------------------------- 1 |
2 |

Selective Disclosure

3 |

4 | The group is currently considering 5 | how to leverage the following drafts 6 | which are aimed at providing selective disclosure capabilities. 7 |

8 | 16 |

17 | Selective disclosure of certain values from signed credentials is a 18 | highly desireable feature that is actively being worked towards. 19 |

20 | At the moment, two general approaches exist for this particular feature: 21 | 31 |
32 | -------------------------------------------------------------------------------- /docs/test-data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/test-data/.gitkeep -------------------------------------------------------------------------------- /docs/tutorials/.gitignore: -------------------------------------------------------------------------------- 1 | *.env 2 | !example.env 3 | *.report.json -------------------------------------------------------------------------------- /docs/tutorials/README.md: -------------------------------------------------------------------------------- 1 | # Traceability Interop Tutorials and Postman Tests 2 | 3 | This folder contains the postman tests and tutorials that are executed to demonstrate compliance with the specification. 4 | 5 | For a guided tutorial-like experience, please begin with the [authentication](./authentication/) tutorial. 6 | 7 | If you are already experienced with using Postman and would like to jump right in with the test suite, please continue reading. 8 | 9 | ## Importing The Test Suite 10 | 11 | ### Create a workspace 12 | 13 | Postman test suites are imported into workspaces. You may either choose an existing workspace before importing, or create a new one by opening the "Workspaces" dropdown menu at the top left of the Postman window and clicking on the "Create Workspace" button. 14 | 15 | 16 | 17 | In the following examples, the "Traceability Interoperability" workspace will be created and used. 18 | 19 | 20 | 21 | ### Import the collections 22 | 23 | This test suite contains several different collections to import (repeat the steps in this section for each item in the following list that you wish to import): 24 | 25 | - [Authentication Tutorial](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/authentication/authentication.postman_collection.json) 26 | - [DID Web Discovery Tutorial](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/did-web-discovery/did-web-discovery.postman_collection.json) 27 | - [Credentials Issue Tutorial](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/credentials-issue/credentials-issue.postman_collection.json) 28 | - [Credentials Verify Tutorial](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/credentials-verify/credentials-verify.postman_collection.json) 29 | - [Credentials Status Tutorial](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/credentials-status-update/credentials-status-update.postman_collection.json) 30 | - [Presentations Exchange Tutorial](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/presentations-exchange/presentations-exchange.postman_collection.json) 31 | - [Presentations Verify Tutorial](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/presentations-verify/presentations-verify.postman_collection.json) 32 | - [OAuth Presentations Exchange Tutorial](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/presentations-exchange-oauth/presentations-exchange-oauth.json) 33 | - [VC API - Suite Agility](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/agility/agility.collection.json) 34 | - [VC API - Issue and Verify Mill Test Report Certificate](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/mill-test-report-certificate/vc-api.mtrc.collection.json) 35 | - [VC API - VC-JWT Support](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/vc-jwt/vc-jwt.collection.json) 36 | - [Test Report Demo](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/report-generation/report-tester.collection.json) 37 | - [Workflow Instance Join](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/workflow-join/workflow-instance-join.collection.json) 38 | 39 | Use the "Import" button to begin importing the interoperability collections. 40 | 41 | 42 | 43 | When the import modal window opens, select the "Link" option, paste the link to the Postman collection in the text input, and click "Continue". (Links for the postman collections can be found at the beginning of this section.) 44 | 45 | 46 | 47 | After you click "Continue", Postman will download and process the remote url and present a confirmation screen. Click the "Import" button to continue. 48 | 49 | 50 | 51 | Repeat the steps in this section for each collection being imported. 52 | 53 | ### Import the environment 54 | 55 | Use the "Import" button again to import the interoperability environment. 56 | 57 | 58 | 59 | When the import modal window opens, select the "Link" option, paste the [link to the Postman environment](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/interoperability_suite.postman_environment.json) in the text input, and click "Continue" 60 | 61 | 62 | 63 | After you click "Continue", Postman will download and process the remote url and present a confirmation screen. Click the "Import" button to continue. 64 | 65 | 66 | 67 | ### Configure the environment 68 | 69 | Once the environment finishes importing, you will need to add in the values specific to your implementation. Click on the "Environments" tab, then highlight the "Traceability Interoperability" environment and update the first six values under the "INITIAL VALUE" column. Click "Reset All" to copy the values to the "CURRENT VALUE" column and then click "Save". 70 | 71 | __NOTE: Do not update the variables which start with `ISSUER_` or `VERIFIER_`. These reference the corresponding values from the first six variables and exist because production tests actually use two different providers for interoperability testing. When running locally, you will only have access to your own information, so interop tests will occur using a single implementation.__ 72 | 73 | 74 | 75 | You are now ready to begin executing interoperability tests against your implementation! 76 | -------------------------------------------------------------------------------- /docs/tutorials/authentication/authentication.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "1e8344a6-a443-4ea9-b7bf-b22a79f51445", 4 | "name": "Authentication Tutorial", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Get Access Token", 10 | "event": [ 11 | { 12 | "listen": "test", 13 | "script": { 14 | "exec": [ 15 | "// Token requests are expected to return a `200 Success` response code. Any", 16 | "// other response code should trigger a failure.", 17 | "pm.test(\"must return `200 Success` status\", function() {", 18 | " pm.response.to.have.status(200);", 19 | "})", 20 | "", 21 | "// The response should include an `access_token` value - this will be presented", 22 | "// to authenticated API endpoints in the `Authentication` header (see the last", 23 | "// testing code block for details on how this is persisted).", 24 | "pm.test(\"response body must include non-empty access_token\", function () {", 25 | " const { access_token } = pm.response.json()", 26 | " pm.expect(access_token).to.be.a('string').that.is.not.empty;", 27 | "});", 28 | "", 29 | "// The type of `access_token` returned by the token request is expected to be", 30 | "// `Bearer`.", 31 | "pm.test(\"response body must represent `Bearer` token\", function() {", 32 | " const { token_type } = pm.response.json()", 33 | " pm.expect(token_type).to.equal(\"Bearer\");", 34 | "});", 35 | "", 36 | "// The returned data includes an `expires_in` field that indicates time until", 37 | "// token expiration. Validate that this value is a whole number greater than", 38 | "// zero, as anything less than or equal to zero means that the `access_token`", 39 | "// is already expired.", 40 | "pm.test(\"returned token must expire in the future\", function() {", 41 | " const { expires_in } = pm.response.json()", 42 | " pm.expect(expires_in).to.be.above(0);", 43 | "});" 44 | ], 45 | "type": "text/javascript" 46 | } 47 | } 48 | ], 49 | "request": { 50 | "method": "POST", 51 | "header": [], 52 | "body": { 53 | "mode": "urlencoded", 54 | "urlencoded": [ 55 | { 56 | "key": "audience", 57 | "value": "{{TOKEN_AUDIENCE}}", 58 | "type": "text" 59 | }, 60 | { 61 | "key": "client_id", 62 | "value": "{{CLIENT_ID}}", 63 | "type": "text" 64 | }, 65 | { 66 | "key": "client_secret", 67 | "value": "{{CLIENT_SECRET}}", 68 | "type": "text" 69 | }, 70 | { 71 | "key": "grant_type", 72 | "value": "client_credentials", 73 | "type": "text" 74 | }, 75 | { 76 | "key": "scope", 77 | "value": "{{CLIENT_SCOPE}}", 78 | "type": "text" 79 | } 80 | ] 81 | }, 82 | "url": { 83 | "raw": "{{TOKEN_ENDPOINT}}", 84 | "host": [ 85 | "{{TOKEN_ENDPOINT}}" 86 | ] 87 | } 88 | }, 89 | "response": [] 90 | } 91 | ], 92 | "event": [ 93 | { 94 | "listen": "prerequest", 95 | "script": { 96 | "type": "text/javascript", 97 | "exec": [ 98 | "pm.request.headers.add({key: 'User-Agent', value: 'W3C Traceability Interop Tests'});" 99 | ] 100 | } 101 | }, 102 | { 103 | "listen": "test", 104 | "script": { 105 | "type": "text/javascript", 106 | "exec": [ 107 | "" 108 | ] 109 | } 110 | } 111 | ], 112 | "variable": [ 113 | { 114 | "key": "foo", 115 | "value": "" 116 | }, 117 | { 118 | "key": "credential_issuer_id", 119 | "value": "" 120 | }, 121 | { 122 | "key": "access_token", 123 | "value": "" 124 | } 125 | ] 126 | } -------------------------------------------------------------------------------- /docs/tutorials/authentication/example.env: -------------------------------------------------------------------------------- 1 | CLIENT_ID="" 2 | CLIENT_SCOPE="" 3 | CLIENT_SECRET="" 4 | TOKEN_AUDIENCE="" 5 | TOKEN_ENDPOINT="" 6 | -------------------------------------------------------------------------------- /docs/tutorials/authentication/resources/create-workspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/authentication/resources/create-workspace.png -------------------------------------------------------------------------------- /docs/tutorials/authentication/resources/environment-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/authentication/resources/environment-variables.png -------------------------------------------------------------------------------- /docs/tutorials/authentication/resources/get-access-token-request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/authentication/resources/get-access-token-request.png -------------------------------------------------------------------------------- /docs/tutorials/authentication/resources/get-access-token-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/authentication/resources/get-access-token-response.png -------------------------------------------------------------------------------- /docs/tutorials/authentication/resources/get-access-token-tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/authentication/resources/get-access-token-tests.png -------------------------------------------------------------------------------- /docs/tutorials/credentials-status-update/resources/credentials-status-update-auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/credentials-status-update/resources/credentials-status-update-auth.png -------------------------------------------------------------------------------- /docs/tutorials/credentials-status-update/resources/credentials-status-update-body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/credentials-status-update/resources/credentials-status-update-body.png -------------------------------------------------------------------------------- /docs/tutorials/credentials-status-update/resources/credentials-status-update-headers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/credentials-status-update/resources/credentials-status-update-headers.png -------------------------------------------------------------------------------- /docs/tutorials/credentials-status-update/resources/credentials-status-update-prereq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/credentials-status-update/resources/credentials-status-update-prereq.png -------------------------------------------------------------------------------- /docs/tutorials/credentials-status-update/resources/credentials-status-update-tests-fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/credentials-status-update/resources/credentials-status-update-tests-fail.png -------------------------------------------------------------------------------- /docs/tutorials/credentials-status-update/resources/credentials-verification-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/credentials-status-update/resources/credentials-verification-response.png -------------------------------------------------------------------------------- /docs/tutorials/credentials-status-update/resources/credentials-verification-tests-fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/credentials-status-update/resources/credentials-verification-tests-fail.png -------------------------------------------------------------------------------- /docs/tutorials/credentials-status-update/resources/select-environment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/credentials-status-update/resources/select-environment.png -------------------------------------------------------------------------------- /docs/tutorials/did-web-discovery/did-web-discovery.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "7573cc79-8d9a-4dfd-8b2e-1c4079752653", 4 | "name": "DID Web Discovery Tutorial", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Get Access Token", 10 | "event": [ 11 | { 12 | "listen": "test", 13 | "script": { 14 | "exec": [ 15 | "// Token requests are expected to return a `200 Success` response code. Any", 16 | "// other response code should trigger a failure.", 17 | "pm.test(\"must return `200 Success` status\", function() {", 18 | " pm.response.to.have.status(200);", 19 | "})", 20 | "", 21 | "// The response should include an `access_token` value - this will be presented", 22 | "// to authenticated API endpoints in the `Authentication` header (see the last", 23 | "// testing code block for details on how this is persisted).", 24 | "pm.test(\"response body must include non-empty access_token\", function () {", 25 | " const { access_token } = pm.response.json()", 26 | " pm.expect(access_token).to.be.a('string').that.is.not.empty;", 27 | "});", 28 | "", 29 | "// The type of `access_token` returned by the token request is expected to be", 30 | "// `Bearer`.", 31 | "pm.test(\"response body must represent `Bearer` token\", function() {", 32 | " const { token_type } = pm.response.json()", 33 | " pm.expect(token_type).to.equal(\"Bearer\");", 34 | "});", 35 | "", 36 | "// The returned data includes an `expires_in` field that indicates time until", 37 | "// token expiration. Validate that this value is a whole number greater than", 38 | "// zero, as anything less than or equal to zero means that the `access_token`", 39 | "// is already expired.", 40 | "pm.test(\"returned token must expire in the future\", function() {", 41 | " const { expires_in } = pm.response.json()", 42 | " pm.expect(expires_in).to.be.above(0);", 43 | "});", 44 | "", 45 | "// The returned `access_token` value is persisted as a Postman collection", 46 | "// variable that can be accessed by other requests in the collection by calling", 47 | "// `pm.collectionVariables.get(\"access_token\")`.", 48 | "pm.test(\"`access_token` persisted to collectionVariables\", function() {", 49 | " const { access_token } = pm.response.json()", 50 | " pm.collectionVariables.set(\"access_token\", access_token);", 51 | "});" 52 | ], 53 | "type": "text/javascript" 54 | } 55 | } 56 | ], 57 | "request": { 58 | "method": "POST", 59 | "header": [], 60 | "body": { 61 | "mode": "urlencoded", 62 | "urlencoded": [ 63 | { 64 | "key": "audience", 65 | "value": "{{TOKEN_AUDIENCE}}", 66 | "type": "text" 67 | }, 68 | { 69 | "key": "client_id", 70 | "value": "{{CLIENT_ID}}", 71 | "type": "text" 72 | }, 73 | { 74 | "key": "client_secret", 75 | "value": "{{CLIENT_SECRET}}", 76 | "type": "text" 77 | }, 78 | { 79 | "key": "grant_type", 80 | "value": "client_credentials", 81 | "type": "text" 82 | }, 83 | { 84 | "key": "scope", 85 | "value": "{{CLIENT_SCOPE}}", 86 | "type": "text" 87 | } 88 | ] 89 | }, 90 | "url": { 91 | "raw": "{{TOKEN_ENDPOINT}}", 92 | "host": [ 93 | "{{TOKEN_ENDPOINT}}" 94 | ] 95 | } 96 | }, 97 | "response": [] 98 | }, 99 | { 100 | "name": "Get Organization DIDs", 101 | "event": [ 102 | { 103 | "listen": "test", 104 | "script": { 105 | "exec": [ 106 | "pm.test(\"Status code is 200\", function () {", 107 | " pm.response.to.have.status(200);", 108 | "});", 109 | "", 110 | "pm.test(\"must include valid JSON response body\", function() {", 111 | " pm.response.json(); // will throw on parse failure", 112 | "});", 113 | "", 114 | "// The response JSON must include a didDocument property that contains the", 115 | "// resolved DID document.", 116 | "pm.test(\"didDocument must be present in response body\", function() {", 117 | " const jsonData = pm.response.json();", 118 | " pm.expect(jsonData).to.have.property('didDocument');", 119 | "});", 120 | "", 121 | "// Service array is used to look up traceability API service endpoint", 122 | "pm.test(\"Response must include 'service' array\", function() {", 123 | " const { service } = pm.response.json().didDocument;", 124 | " pm.expect(service).to.be.an('array').that.is.not.empty;", 125 | "});", 126 | "", 127 | "// Service array must be correctly defined in DID document", 128 | "pm.test(\"'service' array must define Traceability API service endpoint\", function() {", 129 | " const { service } = pm.response.json().didDocument;", 130 | " const entry = service.find((s) => s.type == \"TraceabilityAPI\");", 131 | " pm.expect(entry).to.be.an('object').that.is.not.empty;", 132 | " pm.expect(entry.serviceEndpoint).to.be.a('string').that.is.not.empty;", 133 | "});", 134 | "", 135 | "// If a verificationMethod property is present, the controller property must", 136 | "// match the didDocument.id property.", 137 | "pm.test(\"verification method controller must match did subject\", function() {", 138 | " const { didDocument } = pm.response.json();", 139 | " const vm = didDocument.verificationMethod || [];", 140 | " vm.forEach((m) => pm.expect(m.controller).to.equal(didDocument.id));", 141 | "});" 142 | ], 143 | "type": "text/javascript" 144 | } 145 | } 146 | ], 147 | "protocolProfileBehavior": { 148 | "disabledSystemHeaders": {} 149 | }, 150 | "request": { 151 | "auth": { 152 | "type": "bearer", 153 | "bearer": [ 154 | { 155 | "key": "token", 156 | "value": "{{access_token}}", 157 | "type": "string" 158 | } 159 | ] 160 | }, 161 | "method": "GET", 162 | "header": [ 163 | { 164 | "key": "Accept", 165 | "value": "application/json", 166 | "type": "text" 167 | } 168 | ], 169 | "url": { 170 | "raw": "{{API_BASE_URL}}/identifiers/{{ORGANIZATION_DID_WEB}}", 171 | "host": [ 172 | "{{API_BASE_URL}}" 173 | ], 174 | "path": [ 175 | "identifiers", 176 | "{{ORGANIZATION_DID_WEB}}" 177 | ] 178 | } 179 | }, 180 | "response": [] 181 | } 182 | ], 183 | "event": [ 184 | { 185 | "listen": "prerequest", 186 | "script": { 187 | "type": "text/javascript", 188 | "exec": [ 189 | "pm.request.headers.add({key: 'User-Agent', value: 'W3C Traceability Interop Tests'});" 190 | ] 191 | } 192 | }, 193 | { 194 | "listen": "test", 195 | "script": { 196 | "type": "text/javascript", 197 | "exec": [ 198 | "" 199 | ] 200 | } 201 | } 202 | ], 203 | "variable": [ 204 | { 205 | "key": "access_token", 206 | "value": "" 207 | } 208 | ] 209 | } -------------------------------------------------------------------------------- /docs/tutorials/did-web-discovery/example.env: -------------------------------------------------------------------------------- 1 | CLIENT_ID="" 2 | CLIENT_SCOPE="" 3 | CLIENT_SECRET="" 4 | TOKEN_AUDIENCE="" 5 | TOKEN_ENDPOINT="" 6 | API_BASE_URL="" 7 | ORGANIZATION_DID_WEB="" -------------------------------------------------------------------------------- /docs/tutorials/did-web-discovery/resources/get-organization-dids-auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/did-web-discovery/resources/get-organization-dids-auth.png -------------------------------------------------------------------------------- /docs/tutorials/did-web-discovery/resources/get-organization-dids-headers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/did-web-discovery/resources/get-organization-dids-headers.png -------------------------------------------------------------------------------- /docs/tutorials/did-web-discovery/resources/get-organization-dids-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/did-web-discovery/resources/get-organization-dids-response.png -------------------------------------------------------------------------------- /docs/tutorials/did-web-discovery/resources/get-organization-dids-tests-fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/did-web-discovery/resources/get-organization-dids-tests-fail.png -------------------------------------------------------------------------------- /docs/tutorials/did-web-discovery/resources/persist-access-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/did-web-discovery/resources/persist-access-token.png -------------------------------------------------------------------------------- /docs/tutorials/did-web-discovery/resources/select-environment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/did-web-discovery/resources/select-environment.png -------------------------------------------------------------------------------- /docs/tutorials/interoperability_suite.postman_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "31a99bc6-b0ea-481a-9713-a7df499e7573", 3 | "name": "Traceability Interoperability", 4 | "values": [ 5 | { 6 | "key": "API_BASE_URL", 7 | "value": "s", 8 | "type": "default", 9 | "enabled": true 10 | }, 11 | { 12 | "key": "CLIENT_ID", 13 | "value": "", 14 | "type": "default", 15 | "enabled": true 16 | }, 17 | { 18 | "key": "CLIENT_SECRET", 19 | "value": "", 20 | "type": "secret", 21 | "enabled": true 22 | }, 23 | { 24 | "key": "ORGANIZATION_DID_WEB", 25 | "value": "", 26 | "type": "default", 27 | "enabled": true 28 | }, 29 | { 30 | "key": "TOKEN_AUDIENCE", 31 | "value": "", 32 | "type": "default", 33 | "enabled": true 34 | }, 35 | { 36 | "key": "TOKEN_ENDPOINT", 37 | "value": "", 38 | "type": "default", 39 | "enabled": true 40 | }, 41 | { 42 | "key": "ISSUER_API_BASE_URL", 43 | "value": "{{API_BASE_URL}}", 44 | "type": "default", 45 | "enabled": true 46 | }, 47 | { 48 | "key": "ISSUER_CLIENT_ID", 49 | "value": "{{CLIENT_ID}", 50 | "type": "default", 51 | "enabled": true 52 | }, 53 | { 54 | "key": "ISSUER_CLIENT_SECRET", 55 | "value": "{{CLIENT_SECRET}", 56 | "type": "secret", 57 | "enabled": true 58 | }, 59 | { 60 | "key": "ISSUER_ORGANIZATION_DID_WEB", 61 | "value": "{{ORGANIZATION_DID_WEB}}", 62 | "type": "default", 63 | "enabled": true 64 | }, 65 | { 66 | "key": "ISSUER_TOKEN_AUDIENCE", 67 | "value": "{{TOKEN_AUDIENCE}}", 68 | "type": "default", 69 | "enabled": true 70 | }, 71 | { 72 | "key": "ISSUER_TOKEN_ENDPOINT", 73 | "value": "{{TOKEN_ENDPOINT}}", 74 | "type": "default", 75 | "enabled": true 76 | }, 77 | { 78 | "key": "VERIFIER_API_BASE_URL", 79 | "value": "{{API_BASE_URL}}", 80 | "type": "default", 81 | "enabled": true 82 | }, 83 | { 84 | "key": "VERIFIER_CLIENT_ID", 85 | "value": "{{CLIENT_ID}}", 86 | "type": "default", 87 | "enabled": true 88 | }, 89 | { 90 | "key": "VERIFIER_CLIENT_SECRET", 91 | "value": "{{CLIENT_SECRET}", 92 | "type": "secret", 93 | "enabled": true 94 | }, 95 | { 96 | "key": "VERIFIER_ORGANIZATION_DID_WEB", 97 | "value": "{{ORGANIZATION_DID_WEB}}\n", 98 | "type": "default", 99 | "enabled": true 100 | }, 101 | { 102 | "key": "VERIFIER_TOKEN_AUDIENCE", 103 | "value": "{{TOKEN_AUDIENCE}}", 104 | "type": "default", 105 | "enabled": true 106 | }, 107 | { 108 | "key": "VERIFIER_TOKEN_ENDPOINT", 109 | "value": "{{TOKEN_ENDPOINT}}", 110 | "type": "default", 111 | "enabled": true 112 | } 113 | ], 114 | "_postman_variable_scope": "environment", 115 | "_postman_exported_at": "2022-11-09T18:57:34.572Z", 116 | "_postman_exported_using": "Postman/9.31.22" 117 | } -------------------------------------------------------------------------------- /docs/tutorials/presentations-exchange-oauth/README.md: -------------------------------------------------------------------------------- 1 | # Traceability Interop Postman Collection Setup 2 | 3 | This guide provides instructions on how to set up and run the Traceability Interop Postman Collection, [`presentations-exchange-oauth.json`](https://github.com/w3c-ccg/traceability-interop/blob/main/docs/tutorials/presentations-exchange-oauth/presentations-exchange-oauth.json). 4 | 5 | ## Prerequisites 6 | 7 | - [Postman](https://www.postman.com/) must be installed on your computer. 8 | 9 | ## Setting Up the Collection 10 | 11 | Open Postman and click on the "Import" button in the top left corner of the window. 12 | 13 | ![Import Trace-API Postman Collection](./resources/import-button.png) 14 | 15 | In the Import dialog, select the "Link" tab, enter the url `https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/presentations-exchange-oauth/presentations-exchange-oauth.json`, and click `Continue`. 16 | 17 | ![Import Trace-API Postman Collection by Link](./resources/import-from-link.png) 18 | 19 | You will be asked to confirm the import. 20 | 21 | ![Confirm Import Trace-API Postman Collection](./resources/confirm-import.png) 22 | 23 | After the collection has been imported, you should see a new collection in your Postman Workspace named "Traceability Interop". 24 | 25 | ![Complete Import Trace-API Postman Collection](./resources/import-complete.png) 26 | 27 | ## Setting Up the Environment 28 | 29 | 1. Click on the "Settings" icon in the top right corner of the Postman window and select "Manage Environments". 30 | 2. Click on the "Add" button to create a new environment. 31 | 3. Give the environment a name, such as "Traceability Interop Environment". 32 | 4. Add the following variables to the environment, replacing the placeholders with your own values: 33 | - `API_BASE_URL`: The base URL for the Traceability Interop API. 34 | - `CLIENT_ID`: The client ID obtained from your OAuth service provider. 35 | - `CLIENT_SCOPE`: The names of the scopes to request from your OAuth service provider. If your OAuth service provider requires that you name the specific scopes that should be included in the auth token, you should provide a value for this variable. 36 | - `CLIENT_SECRET`: The client secret obtained from your OAuth service provider. 🔥 Be especially careful with `CLIENT_SECRET`🔥, If it is stolen it will allow an attacker the ability to perform all api operations supported by your service provider. 37 | - `TOKEN_AUDIENCE`: This value is used to identify the service provider API that the token will be used to access. You may need to configure your identity provider and token endpoint to support this value. 38 | - `TOKEN_ENDPOINT`: This is the endpoint used to obtain an access token for Machine to Machine connection secured via CLIENT_ID and CLIENT_SECRET. 39 | 5. Save the environment by clicking on the "Add" button. 40 | 41 | ## Running the Collection 42 | 43 | 1. Select the "Traceability Interop" collection in the Postman Workspace. 44 | 2. In the top right corner of the Postman window, select the environment you just created from the dropdown list. 45 | 3. Click on the "Runner" button in the top right corner of the window to open the Collection Runner. 46 | 4. In the Collection Runner, select the "Traceability Interop" collection and the environment you created earlier. 47 | 5. Click on the "Start Test" button to run the collection. 48 | 6. Observe the results of the API requests in the Collection Runner. The responses from the API will be displayed in the "Tests" tab for each request. 49 | 7. If a request fails, you can examine the response to see what went wrong. 50 | 51 | By following these steps, you should now be able to successfully run the Traceability Interop Postman Collection and test the Traceability Interop API. 52 | -------------------------------------------------------------------------------- /docs/tutorials/presentations-exchange-oauth/resources/confirm-import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/presentations-exchange-oauth/resources/confirm-import.png -------------------------------------------------------------------------------- /docs/tutorials/presentations-exchange-oauth/resources/import-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/presentations-exchange-oauth/resources/import-button.png -------------------------------------------------------------------------------- /docs/tutorials/presentations-exchange-oauth/resources/import-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/presentations-exchange-oauth/resources/import-complete.png -------------------------------------------------------------------------------- /docs/tutorials/presentations-exchange-oauth/resources/import-from-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/presentations-exchange-oauth/resources/import-from-link.png -------------------------------------------------------------------------------- /docs/tutorials/report-generation/README.md: -------------------------------------------------------------------------------- 1 | Be careful generating reports from postman. 2 | 3 | Ensure that no senstive data is included in the report. 4 | 5 | ``` 6 | npm install -g newman newman-reporter-html 7 | ``` 8 | 9 | ```sh 10 | newman run ./report-tester.collection.json \ 11 | --reporters cli,html,json 12 | ``` 13 | -------------------------------------------------------------------------------- /docs/tutorials/report-generation/build-index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | const yargs = require('yargs/yargs'); 5 | const reportCleaner = require('./newman-json-sanitizer'); 6 | 7 | const readFilesSync = (dir) => { 8 | const files = []; 9 | fs.readdirSync(dir).forEach((filename) => { 10 | const { name, ext } = path.parse(filename); 11 | 12 | const filepath = path.resolve(dir, filename); 13 | const stat = fs.statSync(filepath); 14 | const isFile = stat.isFile(); 15 | if (isFile) { 16 | files.push({ 17 | filepath, 18 | name, 19 | ext, 20 | stat, 21 | content: fs.readFileSync(filepath).toString(), 22 | }); 23 | } 24 | }); 25 | 26 | const sortArgs = { 27 | numeric: true, 28 | sensitivity: 'base', 29 | }; 30 | 31 | files.sort((a, b) => a.name.localeCompare(b.name, undefined, sortArgs)); 32 | return files; 33 | }; 34 | 35 | const cleanAndMoveReports = (relativePath) => { 36 | readFilesSync(path.join(process.cwd(), relativePath)).map((f) => { 37 | const cleanerName = (f.name + f.ext).replace('newman-run-report-', ''); 38 | let { content } = f; 39 | if (f.ext === '.json') { 40 | content = JSON.stringify(reportCleaner(JSON.parse(content)), null, 2); 41 | } 42 | fs.writeFileSync(path.join(__dirname, `../../reports/${cleanerName}`), content); 43 | return cleanerName; 44 | }); 45 | }; 46 | 47 | const buildReportsIndex = (folder) => { 48 | const reports = readFilesSync(path.join(__dirname, '../../reports')) 49 | .filter((f) => f.name !== 'index' && ['.json', '.html'].includes(f.ext)) 50 | .map((f) => `https://w3id.org/traceability/interoperability/reports/${folder}/${f.name + f.ext}`); 51 | fs.writeFileSync(path.join(__dirname, '../../reports/index.json'), JSON.stringify({ items: reports }, null, 2)); 52 | }; 53 | 54 | (() => { 55 | const { argv } = yargs(process.argv.slice(2)) 56 | .option('index', { 57 | default: true, 58 | describe: 'Generate index.json, negate with --no-index.', 59 | type: 'boolean', 60 | }) 61 | .option('sanitize', { 62 | default: true, 63 | describe: 'Sanitize and collect test output, negate with --no-sanitize.', 64 | type: 'boolean', 65 | }) 66 | .option('folder', { 67 | describe: 'Subfolder to use when generating index.json links. Required unless --no-index is specified.', 68 | type: 'string', 69 | choices: ['interoperability', 'conformance'], 70 | alias: 'f', 71 | }) 72 | .check((args, _) => { 73 | if (args.index && !args.folder) { 74 | throw new Error('Missing required argument: folder'); 75 | } 76 | return true; 77 | }); 78 | 79 | if (argv.sanitize) { 80 | try { 81 | cleanAndMoveReports('./newman'); 82 | } catch (e) { 83 | console.log(e); 84 | console.log('No newman reports to clean'); 85 | } 86 | } 87 | 88 | if (argv.index) { 89 | buildReportsIndex(argv.folder); 90 | } 91 | 92 | // consider removing reports older than 1 month / 1 year... 93 | })(); 94 | -------------------------------------------------------------------------------- /docs/tutorials/report-generation/newman-json-sanitizer.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | jsonReport.run.executions will contain: 4 | 5 | - 🔥 OAuth ACCESS_TOKENs 🔥 6 | - 🔥 OAuth CLIENT_ID 🔥 7 | - 🔥 OAuth CLIENT_SECRET 🔥 8 | 9 | */ 10 | 11 | // 🔥 this is the most dangerous function 12 | const cleanExecutions = (executions) => { 13 | const safeExecutions = []; 14 | executions.forEach((execution) => { 15 | safeExecutions.push({ 16 | id: execution.id, 17 | cursor: execution.cursor, 18 | item: { 19 | id: execution.item.id, 20 | name: execution.item.name, 21 | }, 22 | // 🔥 we opt to not include any details of the request, and only status and timing from the response 23 | // request details can be inferred from collection data. 24 | // NOTE: The `response` may be missing if there was a request error, e.g., 25 | // ENOTFOUND in the case of missing base url parameters. 26 | response: { 27 | id: execution.response?.id, 28 | status: execution.response?.status, 29 | code: execution.response?.code, 30 | responseTime: execution.response?.responseTime, 31 | responseSize: execution.response?.responseSize, 32 | }, 33 | assertions: execution.assertions, 34 | }); 35 | }); 36 | return safeExecutions; 37 | }; 38 | 39 | module.exports = (jsonReport) => { 40 | const safeToPublishReport = { 41 | collection: { 42 | info: jsonReport.collection.info, 43 | item: jsonReport.collection.item, 44 | }, 45 | run: { 46 | stats: jsonReport.run.stats, 47 | timings: jsonReport.run.timings, 48 | executions: cleanExecutions(jsonReport.run.executions), 49 | }, 50 | }; 51 | return safeToPublishReport; 52 | }; 53 | -------------------------------------------------------------------------------- /docs/tutorials/report-generation/report-tester.collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "3dd7cd87-3fa5-4f21-b598-350eebd0d67d", 4 | "name": "Test Report Demo", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Discover API Configuration", 10 | "event": [ 11 | { 12 | "listen": "test", 13 | "script": { 14 | "exec": [ 15 | "const jsonResponse = pm.response.json();", 16 | "", 17 | "pm.test(\"The DID Document MUST contain 'assertionMethod' DID URL\", function () {", 18 | " pm.expect(jsonResponse.didDocument.assertionMethod).to.be.an(\"array\");", 19 | " pm.expect(jsonResponse.didDocument.assertionMethod[0]).to.be.a(\"string\");", 20 | " pm.environment.set(\"issuer_did_url\", jsonResponse.didDocument.assertionMethod[0]);", 21 | "});", 22 | "", 23 | "pm.test(\"The DID Document MUST contain 'authentication' DID URL\", function () {", 24 | " pm.expect(jsonResponse.didDocument.authentication).to.be.an(\"array\");", 25 | " pm.expect(jsonResponse.didDocument.authentication[0]).to.be.a(\"string\");", 26 | " pm.environment.set(\"holder_did_url\", jsonResponse.didDocument.authentication[0]);", 27 | "});", 28 | "", 29 | "pm.test(\"The DID Document MUST contain 'service'\", function () {", 30 | " pm.expect(jsonResponse.didDocument.service).to.be.an(\"array\");", 31 | " pm.environment.set(\"traceability_api_root\", jsonResponse.didDocument.service[0].serviceEndpoint);", 32 | "});" 33 | ], 34 | "type": "text/javascript" 35 | } 36 | } 37 | ], 38 | "request": { 39 | "method": "GET", 40 | "header": [ 41 | { 42 | "key": "Accept", 43 | "value": "application/json", 44 | "type": "text" 45 | } 46 | ], 47 | "url": { 48 | "raw": "https://api.did.actor/identifiers/did:web:api.did.actor:api", 49 | "protocol": "https", 50 | "host": [ 51 | "api", 52 | "did", 53 | "actor" 54 | ], 55 | "path": [ 56 | "identifiers", 57 | "did:web:api.did.actor:api" 58 | ] 59 | } 60 | }, 61 | "response": [] 62 | }, 63 | { 64 | "name": "Issue Credential", 65 | "event": [ 66 | { 67 | "listen": "prerequest", 68 | "script": { 69 | "exec": [ 70 | "" 71 | ], 72 | "type": "text/javascript" 73 | } 74 | }, 75 | { 76 | "listen": "test", 77 | "script": { 78 | "exec": [ 79 | "const {verifiableCredential} = pm.response.json();", 80 | "", 81 | "pm.test(\"The Verifiable Credential MUST have a 'proof'\", function () {", 82 | " pm.expect(verifiableCredential.proof).to.be.an(\"object\");", 83 | " pm.environment.set(\"verifiable_credential\", JSON.stringify(verifiableCredential));", 84 | "});", 85 | "" 86 | ], 87 | "type": "text/javascript" 88 | } 89 | } 90 | ], 91 | "request": { 92 | "method": "POST", 93 | "header": [], 94 | "body": { 95 | "mode": "raw", 96 | "raw": "{\n \"credential\": {\n \"@context\": [\n \"https://www.w3.org/2018/credentials/v1\"\n ],\n \"id\": \"urn:uuid:07aa969e-b40d-4c1b-ab46-ded252003ded\",\n \"type\": [\n \"VerifiableCredential\"\n ],\n \"issuer\": \"did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn\",\n \"issuanceDate\": \"{{$isoTimestamp}}\",\n \"credentialSubject\": {\n \"id\": \"did:key:z6MktiSzqF9kqwdU8VkdBKx56EYzXfpgnNPUAGznpicNiWfn\"\n }\n },\n \"options\": {\n \"type\": \"Ed25519Signature2018\"\n }\n}", 97 | "options": { 98 | "raw": { 99 | "language": "json" 100 | } 101 | } 102 | }, 103 | "url": { 104 | "raw": "{{traceability_api_root}}/credentials/issue", 105 | "host": [ 106 | "{{traceability_api_root}}" 107 | ], 108 | "path": [ 109 | "credentials", 110 | "issue" 111 | ] 112 | } 113 | }, 114 | "response": [] 115 | }, 116 | { 117 | "name": "Verify Credential", 118 | "event": [ 119 | { 120 | "listen": "test", 121 | "script": { 122 | "exec": [ 123 | "const verification = pm.response.json()", 124 | "", 125 | "pm.test(\"The Verifiable Credential MUST have been verified\", function () {", 126 | " pm.expect(verification.verified).to.eql(true);", 127 | "});", 128 | "" 129 | ], 130 | "type": "text/javascript" 131 | } 132 | } 133 | ], 134 | "request": { 135 | "method": "POST", 136 | "header": [], 137 | "body": { 138 | "mode": "raw", 139 | "raw": "{\n \"verifiableCredential\": {{verifiable_credential}}\n}", 140 | "options": { 141 | "raw": { 142 | "language": "json" 143 | } 144 | } 145 | }, 146 | "url": { 147 | "raw": "{{traceability_api_root}}/credentials/verify", 148 | "host": [ 149 | "{{traceability_api_root}}" 150 | ], 151 | "path": [ 152 | "credentials", 153 | "verify" 154 | ] 155 | } 156 | }, 157 | "response": [] 158 | } 159 | ], 160 | "event": [ 161 | { 162 | "listen": "prerequest", 163 | "script": { 164 | "type": "text/javascript", 165 | "exec": [ 166 | "pm.request.headers.add({key: 'User-Agent', value: 'W3C Traceability Interop Tests'});" 167 | ] 168 | } 169 | }, 170 | { 171 | "listen": "test", 172 | "script": { 173 | "type": "text/javascript", 174 | "exec": [ 175 | "" 176 | ] 177 | } 178 | } 179 | ] 180 | } -------------------------------------------------------------------------------- /docs/tutorials/resources/configure-environment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/resources/configure-environment.png -------------------------------------------------------------------------------- /docs/tutorials/resources/create-workspace-details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/resources/create-workspace-details.png -------------------------------------------------------------------------------- /docs/tutorials/resources/create-workspace-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/resources/create-workspace-start.png -------------------------------------------------------------------------------- /docs/tutorials/resources/import-collection-confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/resources/import-collection-confirm.png -------------------------------------------------------------------------------- /docs/tutorials/resources/import-collection-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/resources/import-collection-link.png -------------------------------------------------------------------------------- /docs/tutorials/resources/import-environment-confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/resources/import-environment-confirm.png -------------------------------------------------------------------------------- /docs/tutorials/resources/import-environment-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/resources/import-environment-link.png -------------------------------------------------------------------------------- /docs/tutorials/resources/import-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/resources/import-start.png -------------------------------------------------------------------------------- /docs/tutorials/resources/select-environment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/tutorials/resources/select-environment.png -------------------------------------------------------------------------------- /docs/tutorials/traceable-presentation-workflow/README.md: -------------------------------------------------------------------------------- 1 | # Traceability Interop Postman Collection Setup 2 | 3 | This guide provides instructions on how to set up and run the Traceability Interop Postman Collection, [`traceable-presentation-workflow.postman_collection.json`](https://github.com/w3c-ccg/traceability-interop/blob/main/docs/tutorials/traceable-presentation-workflow/traceable-presentation-workflow.postman_collection.json). 4 | 5 | ## Prerequisites 6 | 7 | - [Postman](https://www.postman.com/) must be installed on your computer. 8 | 9 | ## Setting Up the Collection 10 | 11 | Open Postman and click on the "Import" button in the top left corner of the window. 12 | 13 | In the Import dialog, select the "Link" tab, enter the url `https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/docs/tutorials/traceable-presentation-workflow/traceable-presentation-workflow.postman_collection.json`, and click `Continue`. 14 | 15 | You will be asked to confirm the import. 16 | 17 | After the collection has been imported, you should see a new collection in your Postman Workspace named "Workflow Tutorial". 18 | 19 | ## Setting Up the Environment 20 | 21 | 1. Click on the "Settings" icon in the top right corner of the Postman window and select "Manage Environments". 22 | 2. Click on the "Add" button to create a new environment. 23 | 3. Give the environment a name, such as "Traceability Interop Environment". 24 | 4. Add the following variables to the environment, replacing the placeholders with your own values. These should reflect the Machine to Machine OAuth Application configured on the Verifier's platform, used for the Holder to make the Traceable Presentation which includes Workflow details: 25 | - `API_BASE_URL`: Verifier platform's base URL. 26 | - `CLIENT_ID`: The client ID for the M2M application, to be used by the Holder. 27 | - `CLIENT_SCOPE`: The names of the scopes to request from your OAuth service provider. If your OAuth service provider requires that you name the specific scopes that should be included in the auth token, you should provide a value for this variable. 28 | - `CLIENT_SECRET`: The client secret provided from the Verifier to the Holder. 🔥 Be especially careful with `CLIENT_SECRET`🔥. 29 | - `TOKEN_ENDPOINT`: This is the endpoint used to obtain an access token for M2M connection secured via CLIENT_ID and CLIENT_SECRET. 30 | - `ORGANIZATION_DID_WEB`: Verifier's `did:web` used for service endpoint discovery, on which the presentation is made. 31 | 5. Save the environment by clicking on the "Add" button. 32 | 33 | ## Running the Collection 34 | 35 | 1. Select the "Workflow Tutorial" collection in the Postman Workspace. 36 | 2. In the top right corner of the Postman window, select the environment you just created from the dropdown list. 37 | 3. Click on the "Runner" button in the top right corner of the window to open the Collection Runner. 38 | 4. In the Collection Runner, select the "Workflow Tutorial" collection and the environment you created earlier. 39 | 5. Click on the "Start Test" button to run the collection. 40 | 6. Observe the results of the API requests in the Collection Runner. The responses from the API will be displayed in the "Tests" tab for each request. 41 | 7. If a request fails, you can examine the response to see what went wrong. 42 | 43 | By following these steps, you should now be able to successfully run the Traceability Interop Postman Collection and test the Traceability Interop API. 44 | -------------------------------------------------------------------------------- /docs/weekly-minutes/README.md: -------------------------------------------------------------------------------- 1 | # W3C CCG Weekly Minutes 2 | 3 | 4 | 5 | Published meeting minutes consist of three files which need to be prepared and added to the [`w3c-ccg/meetings` GitHub repository](https://github.com/w3c-ccg/meetings). This document outlines the procedures required to prepare these files and add them to the repository. 6 | 7 | - `irc.log` 8 | - `audio.ogg` 9 | - `group.txt` 10 | 11 | ## Download IRC Logs 12 | 13 | To download the IRC logs, visit the [Scribe tool](https://w3c-ccg.github.io/meetings/scribe-tool/). Select "Traceability" from the "Weekly Meeting" dropdown, enter the appropriate date in the date picker, and click "Retrieve Raw Logs". All of these UI elements are located at the bottom-right corner of the window. 14 | 15 | 16 | 17 | Select the "Raw IRC Log" tab at the bottom-left of the display, and copy/paste the contents of the text area into a file called `irc.log`. Clean this file up as needed, e.g., to remove `s/foo/bar/` comments and any other relevant items. 18 | 19 | 20 | 21 | ## Download Audio 22 | 23 | With the "Weekly Meeting" and date picker elements still properly filled out, click on the "Download Audio" button. 24 | 25 | 26 | 27 | You will be redirected to a new page, which will begin playing the audio from the selected meeting. Listen for a moment to ensure that the meeting was properly recorded, then click on the vertical ellipses and select "Download" to save the `.ogg` file. 28 | 29 | The downloaded file will have a name like `w3c-ccg-traceability-2022-08-09.ogg`; rename this file to `audio.ogg`. 30 | 31 | 32 | 33 | 34 | ## Commit `group.txt` file to GitHub 35 | 36 | _Note that you must have appropriate permissions to add files to the `w3c-ccg/meetings` GitHub repository. If you do not have this permission level, you will be unable to add files directly._ 37 | 38 | Visit the [W3C Credentials Community Group meeting transcripts](https://github.com/w3c-ccg/meetings) GitHub repository and select "Add file" -> "Create new file". 39 | 40 | 41 | 42 | In the `Name your file...` text box, add both a directory name and file name for the `group.txt` file. The directory name should include the appropriate date and meeting group suffix, e.g., `2022-08-09-traceability`. The contents of the file should simply be the word `Traceability`. 43 | 44 | 45 | 46 | Be sure to commit the new `group.txt` file by clicking the "Commit new file" button at the bottom of the page. 47 | 48 | 49 | 50 | ## Upload `irc.log` and `audio.ogg` to GitHub 51 | 52 | From within the newly created folder (`2022-08-09-traceability` in this example), select "Add file" -> "Upload Files" 53 | 54 | 55 | 56 | Drag or select the `irc.log` and `audio.ogg` files that you saved earlier and commit your changes. 57 | 58 | 59 | 60 | You should now have three files committed to the new folder, which will kick off a workflow job to handle the rest of the publishing steps. 61 | 62 | 63 | -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/add-group-file-name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/add-group-file-name.png -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/add-group-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/add-group-file.png -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/click-download-audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/click-download-audio.png -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/commit-group-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/commit-group-file.png -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/download-audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/download-audio.png -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/files-uploaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/files-uploaded.png -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/play-audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/play-audio.png -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/raw-irc-logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/raw-irc-logs.png -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/select-meeting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/select-meeting.png -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/upload-files-confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/upload-files-confirm.png -------------------------------------------------------------------------------- /docs/weekly-minutes/resources/upload-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/docs/weekly-minutes/resources/upload-files.png -------------------------------------------------------------------------------- /environment-setup/create-pat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/environment-setup/create-pat.png -------------------------------------------------------------------------------- /environment-setup/onboard-register.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # This script is a wrapper to call the onboard-register workflow in the 4 | # w3c-ccg/traceability-interop repository. Options correspond to inputs for the 5 | # workflow 6 | # 7 | # Examples: 8 | # 9 | # # Register contents of secrets.b64 under prefix VENDOR_PREFIX_. This will 10 | # # fail if the secrets being registered already exist. 11 | # 12 | # onboard-register.sh -p VENDOR_PREFIX_ secrets.b64 13 | # 14 | # # Register contents of secrets.b64 under prefix VENDOR_PREFIX_ and 15 | # # overwrite any existing values for those secrets. 16 | # 17 | # onboard-register.sh -p VENDOR_PREFIX_ -o secrets.b64 18 | 19 | set -o errexit 20 | set -o nounset 21 | set -o pipefail 22 | 23 | usage() { echo "Usage: $0 -p [-o] dotenv" 1>&2; exit 1; } 24 | 25 | OVERWRITE="false" 26 | while getopts "p:o" o; do 27 | case "${o}" in 28 | p) 29 | PREFIX=${OPTARG} 30 | ;; 31 | o) 32 | OVERWRITE="true" 33 | ;; 34 | *) 35 | usage 36 | ;; 37 | esac 38 | done 39 | shift $((OPTIND-1)) 40 | 41 | if [ -z "$1" ] || [ ! -f "$1" ]; then 42 | echo "You must specify a valid dotenv file" 43 | exit 1 44 | fi; 45 | 46 | GITHUB_REPOSITORY='w3c-ccg/traceability-interop' 47 | GITHUB_TOKEN=$(gh auth status -t 2>&1 | grep Token | awk '{print $3}') 48 | DOTENV=`cat $1` 49 | 50 | if [ -z "$GITHUB_TOKEN" ]; then 51 | echo "You must first authenticate with 'gh auth login'" 52 | exit 1 53 | fi 54 | 55 | gh workflow run onboard-register.yml --repo "$GITHUB_REPOSITORY" \ 56 | -f token="$GITHUB_TOKEN" \ 57 | -f prefix="$PREFIX" \ 58 | -f dotenv="$DOTENV" \ 59 | -f overwrite="$OVERWRITE" 60 | -------------------------------------------------------------------------------- /environment-setup/onboard-rotate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # This script is a wrapper to call the onboard-rotate workflow in the 4 | # w3c-ccg/traceability-interop repository. 5 | 6 | set -o errexit 7 | set -o nounset 8 | set -o pipefail 9 | 10 | GITHUB_REPOSITORY='w3c-ccg/traceability-interop' 11 | GITHUB_TOKEN=$(gh auth status -t 2>&1 | grep Token | awk '{print $3}') 12 | 13 | if [ -z "$GITHUB_TOKEN" ]; then 14 | echo "You must first authenticate with 'gh auth login'" 15 | exit 1 16 | fi 17 | 18 | gh workflow run onboard-rotate.yml --repo "$GITHUB_REPOSITORY" \ 19 | -f token="$GITHUB_TOKEN" 20 | -------------------------------------------------------------------------------- /environment-setup/pubkey.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mQINBGLN138BEADepbUj/nnG6LwSjLS4seHL8eDlsBIdajBa67c7Qfn8J8iuyTdb 4 | ona5DY3q9yYy9FxSM29XzsB7I9PDWwovVF+DJrfOwH/LxuRh2XG2ijXGGVv9gbnT 5 | ERu4l3kWSN5RmmJy+cApvF5LmHtQmDvkiJeU/e/UzdFWcm341RTCnJUm+3Ze2iS3 6 | p9tZeej5T3yFxEw3zls82dG6FcBnylFCiV5SY5ExK8AFB4SfOh4d3O0sV0tdrhT1 7 | Llf9BN+tjtmEf66Zh+EHUcqiFKqUyqsYnJErdpriRbOC/VcY6dNYvpPzbmVey6vL 8 | /PO4UY1eDrAehg4iLcCIjFYwgL1/zwZSR8lugB4Roojys8feYrRiRhTQ7/PpCCj0 9 | ipCgLY+Vv8L/BJdKDyyr9eva/w+ACV8Xa2kzgNMMUtB3QChXk3T5C1fiXZsgMWCR 10 | NNmnR02fqbpV/m5OWBnxAjToVa/WeD39+i1n4qOXGAIDao81wb9xktl2JPKNWSan 11 | HY7sPB4UNVG5sob4xu8B8qFnYYcHK138g+Rg0lAFqHZQ0mqsCIync3uOssKqzb55 12 | mwNDUYjZ3Z0ci4lQX9Qxvkz1ZHK/Nv0f3CdII39Zv/e6AIQc5O167q7RERqWGRdE 13 | JjTqENGZwbXEkOrUA8MZzwjD8nGAeqWtJps6k9Uj79zUFv9DuGdbu5Z9VQARAQAB 14 | tDZ3M2MtY2NnL3RyYWNlYWJpbGl0eS1pbnRlcm9wICh0ZXN0IHN1aXRlIHJlZ2lz 15 | dHJhdGlvbimJAk4EEwEKADgWIQShgULvShICcSJIMIIBoYiM7XujPwUCYs3XfwIb 16 | DQULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRABoYiM7XujP5ZXEACoxSHaH7s5 17 | kooVb3VjT3WoB4Vuzb3zEyp/iFwLHe3FaIQnYdNQxbec8WZVKWD4nxMhtGAr51hs 18 | sC8JRnIzDDiCl55dH/ivVgpUM2hLwNRxjyMGAJgzOfVYhaqPAexjFB5m0o5ZL+iP 19 | d1mkwlivaO2VtMNM0moN1uedq/C4CLL0y3BUFtKPuYALDdZ75rfmp6Jfi/eI+hLf 20 | xTJRzpvk9MBonqwz7Lo8ZSRcFm+KUE3Kvx6HlBcrhqL/SR0iq1OCpO6sVa6Yt3rD 21 | YGd7edu0cv1LAbcIfJZ/aj46ju3KZCn+80xizCRw2S+hrwVNMjRgx9SWsT3MKgOC 22 | AY23nSMO6vXPfrcgh0r4iQP2oikOGLrExLdyWsNzqqI7/nj4XhqiXaeIPt0aJSWl 23 | imCF1e6kJFaOi7NcfUsUujnd+YCkI/GetYTNEjTwDN10yWDPnnzdKeNr0yM0JogR 24 | Xb+TDuumdXbbozd6XtevYXJsfzCflFfu7Q0lTsGFTbGp0jaHyrMtTugUG6+R3caP 25 | bowZp20Jd7z6FpzM7fAyXAoJfIjWUWyM4qXmfN+nAmkMTsgQW1QsUbeaQZIkZCn4 26 | 6m2WOr6rj/1qzpyNdAO8DwAYPqaDmNTGYS6sEYY98oDA1r2HlbpDp83AtyWVa+20 27 | VcgrfAqFk9w4tPArCkHmllRI2DJl3sUARQ== 28 | =TWPN 29 | -----END PGP PUBLIC KEY BLOCK----- 30 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.0.0" 6 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "traceability-interop", 3 | "version": "0.0.1", 4 | "description": "An enterprise grade HTTP API for leveraging [W3C Decentralized Identifiers](https://www.w3.org/TR/did-core/) and [W3C Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) with [W3C CCG Traceability Vocabulary](https://w3c-ccg.github.io/traceability-vocab/) and the [VC API](https://w3c-ccg.github.io/vc-api/) when possible.", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "docs", 8 | "test": "tests" 9 | }, 10 | "scripts": { 11 | "shove": "git add -A; git commit -m 'chore: :rocket: testing ci'; git push origin main", 12 | "clean": "npm run report:clean", 13 | "report:generate": "newman run", 14 | "postreport:generate": "npm run report:prepare", 15 | "report:sanitize": "node ./docs/tutorials/report-generation/build-index.js --no-index", 16 | "report:index": "node ./docs/tutorials/report-generation/build-index.js --no-sanitize", 17 | "report:clean": "rm -rf ./docs/reports/*", 18 | "postinstall": "lerna bootstrap", 19 | "clean:lock": "npx lerna exec 'rm -rf package-lock.json node_modules'", 20 | "install:clean": "npx lerna clean -y && rm -rf node_modules && npm i", 21 | "install:ci": "npm ci --ignore-scripts && lerna link && lerna bootstrap", 22 | "lint": "lerna run lint --stream", 23 | "lint:docs": "eslint --ext .html docs --fix", 24 | "format": "lerna run format --stream", 25 | "format:docs": "prettier --write docs/**/*.html", 26 | "validate-spec": "npx swagger-cli validate docs/openapi/openapi.yml", 27 | "preserve": "npx swagger-cli bundle docs/openapi/openapi.yml -o docs/openapi/openapi.json --dereference", 28 | "serve": "npx serve ./docs", 29 | "testdata": "node ./tests/generate-testdata.js" 30 | }, 31 | "repository": { 32 | "type": "git", 33 | "url": "git+https://github.com/w3c-ccg/traceability-interop.git" 34 | }, 35 | "author": "", 36 | "license": "ISC", 37 | "bugs": { 38 | "url": "https://github.com/w3c-ccg/traceability-interop/issues" 39 | }, 40 | "homepage": "https://github.com/w3c-ccg/traceability-interop#readme", 41 | "devDependencies": { 42 | "@digitalbazaar/did-method-key": "^5.1.0", 43 | "@digitalbazaar/ed25519-signature-2018": "^4.0.0", 44 | "@digitalbazaar/ed25519-verification-key-2018": "^4.0.0", 45 | "@digitalbazaar/vc": "^6.0.1", 46 | "@html-eslint/eslint-plugin": "^0.19.0", 47 | "@html-eslint/parser": "^0.19.0", 48 | "credentials-context": "^2.0.0", 49 | "eslint": "^8.40.0", 50 | "eslint-config-airbnb-base": "^15.0.0", 51 | "eslint-plugin-import": "^2.27.5", 52 | "eslint-plugin-jest": "^27.2.1", 53 | "eslint-plugin-prettier": "^4.2.1", 54 | "jsonld": "^8.1.1", 55 | "jsonld-signatures": "^11.1.0", 56 | "klona": "^2.0.6", 57 | "prettier": "^2.8.8" 58 | }, 59 | "dependencies": { 60 | "@apidevtools/swagger-cli": "^4.0.4", 61 | "lerna": "^6.6.2", 62 | "newman": "^5.3.2", 63 | "newman-reporter-htmlextra": "^1.22.11", 64 | "yargs": "^17.7.2" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /reporting/README.md: -------------------------------------------------------------------------------- 1 | # Test Result Reporting 2 | 3 | ## Environment Setup 4 | 5 | To build and run the test reporting, first install and run either [miniconda](https://docs.conda.io/en/latest/miniconda.html) or full [conda](https://docs.conda.io/projects/conda/en/latest/user-guide/install/index.html) 6 | 7 | 8 | Once that is installed, you can create the reporting environment with the following command: 9 | 10 | ```bash 11 | $ conda env create -f environment.yml python=3.9 12 | ``` 13 | 14 | 15 | And then activate it: 16 | 17 | ```bash 18 | $ conda activate reporting 19 | ``` 20 | 21 | ## Run reporting 22 | 23 | The reporter can be run from your local machine with one of the following command: 24 | 25 | ```bash 26 | $ ./reporter.py --conformance 27 | $ ./reporter.py --interoperability 28 | ``` 29 | 30 | It will automatically fetch the latest test results, process them, and launch a server hosting the dashboard at [http://localhost:8050/](http://localhost:8050/) 31 | 32 | Test results are sourced from the JSON test results published in github pages in one of the following locations based on the provided command-line options (either `--conformance` or `--interoperability`) 33 | 34 | - [Interoperability test results](https://w3c-ccg.github.io/traceability-interop/reports/interoperability/index.json) 35 | - [Conformance test results](https://w3c-ccg.github.io/traceability-interop/reports/conformance/index.json) 36 | 37 | The reporter can also be run in CI mode, which will find test output in the `docs/reports` folder on the test runner instead of downloading published report output. If you want to test this mode locally, i.e., during development, you will need to run the postman test suite and post-process the results into the `docs/reports` folder yourself before running `reporter.py`. 38 | 39 | ```bash 40 | $ ./reporter.py --mode ci --conformance 41 | $ ./reporter.py --mode ci --interoperability 42 | ``` 43 | 44 | To get a full list of reporter options, you can execute the reporter with the help parameter: 45 | ```bash 46 | $ ./reporter.py -h 47 | usage: reporter.py [-h] [--mode [{all,data,html,dashboard,ci}]] (-c | -i) 48 | 49 | Interop test results reporting utility 50 | 51 | optional arguments: 52 | -h, --help show this help message and exit 53 | --mode [{all,data,html,dashboard,ci}] 54 | mode to run the reporter in 55 | 56 | Report Type: 57 | One of the following options MUST be specified. 58 | 59 | -c, --conformance generate a report based on the most recently published conformance testing output. 60 | -i, --interop generate a report based on the most recently publised interoperability testing output. 61 | ``` 62 | 63 | ## Module Description 64 | 65 | There are three main modules: 66 | 67 | - `./postman_reporter/report_data.py` — go get the data, link it up, create appropriate data frames, and store them as CSV for easy use 68 | - `./postman_reporter/report_static.py` — generate a static HTML report for use with gh-pages 69 | - `./postman_reporter/report_dashboard.py` — the latter half of this app that spins up a dash app on flask for actually working with the test results 70 | - `reporter.py` — the main binary that runs one or more modules as listed above, based on command line arguments 71 | 72 | ## Output 73 | 74 | After running the reporter you will see a dashboard in your browser 75 | ![image](https://user-images.githubusercontent.com/3495140/174913518-0612f10a-fe81-442c-816e-ab69fac285fa.png) 76 | 77 | In addition to the summary stats multiple drill through visualizations that cross compare results and identify problems in red are available 78 | ![image](https://user-images.githubusercontent.com/3495140/174913726-f94a8aff-7163-4b82-aaec-6aa0f6b3cc93.png) 79 | ![image](https://user-images.githubusercontent.com/3495140/174913749-0fa20211-c37e-4895-ad2d-7772dcaeb70a.png) 80 | 81 | There is also a searchable and filterable table of all unit test results 82 | ![image](https://user-images.githubusercontent.com/3495140/174913783-b5fd187d-908c-4c69-a67d-ad0f3dd94100.png) -------------------------------------------------------------------------------- /reporting/assets/conformance.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | W3C Supply Chain Traceability Conformance Test Results 8 | 10 | 91 | 92 | 94 | 95 | 96 | 97 | 98 |
99 |
100 |
101 |
102 |

Traceability Conformance Test Results

103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |

Summary

112 |
113 | {{ summary_text }} 114 |
115 |

Provider Summary

116 |
117 | {% for card in cards_single %} 118 | {{ card }} 119 | {% endfor %} 120 |
121 |
122 | {% for card in cards_multi %} 123 | {{ card }} 124 | {% endfor %} 125 |
126 |

Provider & Test Summary

127 |
128 | {{summary_table_single}} 129 |

130 |
131 | {{summary_table_multi}} 132 |


133 |
134 |
135 | {{ facet_tests }} 136 |
137 |

138 |
139 |
140 |
142 |
143 |

Results

144 |
145 |
{{ results_chart }}
146 |
{{ results_tree }}
147 |
148 |
149 |
150 |
152 |
153 |

Details

154 |
155 | {{ details }} 156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /reporting/assets/interoperability.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | W3C Supply Chain Traceability Interoperability Test Results 8 | 10 | 83 | 84 | 86 | 87 | 88 | 89 | 90 |
91 |
92 |
93 |
94 |

Traceability Interoperability Test Results

95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |

Summary

104 |
105 | {{ summary_text }} 106 |
107 |

Provider Summary

108 |
109 | {% for card in cards_single %} 110 | {{ card }} 111 | {% endfor %} 112 |
113 |
114 | {% for card in cards_multi %} 115 | {{ card }} 116 | {% endfor %} 117 |
118 |

Provider & Test Summary

119 |
120 | {{summary_table_single}} 121 |

122 |
123 | {{summary_table_multi}} 124 |


125 |
126 |
127 | {{ facet_tests }} 128 |
129 |

130 |
131 |
132 |
134 |
135 |

Results

136 |
137 |
{{ results_chart }}
138 |
{{ results_tree }}
139 |
140 |
141 |
142 |
144 |
145 |

Details

146 |
147 | {{ details }} 148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /reporting/environment.yml: -------------------------------------------------------------------------------- 1 | name: reporting 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - dash=2.3.1 6 | - dash-bootstrap-components 7 | - Jinja2==3.1.2 8 | - matplotlib=3.5.1 9 | - scipy=1.8.0 10 | - numpy=1.22.3 11 | - pandas=1.4.2 12 | - plotly=5.7.0 13 | - seaborn=0.11.2 14 | - tqdm=4.64.0 15 | - urllib3=1.26.9 16 | - pip=22.0.4 17 | - requests=2.27.1 18 | - pip: 19 | - pyarrow==7.0.0 20 | -------------------------------------------------------------------------------- /reporting/postman_reporter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/reporting/postman_reporter/__init__.py -------------------------------------------------------------------------------- /reporting/postman_reporter/report_config.py: -------------------------------------------------------------------------------- 1 | REPORTS_BASE_URL = "https://w3id.org/traceability/interoperability/reports" 2 | DATA_DIR = "./data/" 3 | CI_DIR = "../docs/reports" 4 | DF_PREFIX = "" 5 | DF_EXT = ".pkl" 6 | 7 | HEADER = [ 8 | "Testing Application", 9 | "Project Name", 10 | "Provider", 11 | "Test Type", 12 | "Test Step", 13 | "Assertion", 14 | "Result", 15 | "Error Message", 16 | "Passing", 17 | ] 18 | 19 | COLUMNS_DETAIL = [ 20 | "Provider", 21 | "Test Type", 22 | "Test Step", 23 | "Assertion", 24 | "Result", 25 | "Error Message", 26 | "Passing", 27 | ] 28 | 29 | COLUMNS_MAIN = [ 30 | "Provider", 31 | "Test Type", 32 | "Test Step", 33 | "Assertion", 34 | "Result", 35 | "Error Message", 36 | ] 37 | 38 | SIDEBAR_STYLE = { 39 | "position": "fixed", 40 | "top": 0, 41 | "left": 0, 42 | "bottom": 0, 43 | "width": "16rem", 44 | "padding": "2rem 1rem", 45 | # "background-color": "#f8f9fa", 46 | } 47 | CONTENT_STYLE = { 48 | "margin-left": "18rem", 49 | "margin-right": "2rem", 50 | "padding": "2rem 1rem", 51 | } 52 | 53 | COLOR_MAP = { 54 | "(?)": "darkgoldenrod", 55 | "Pass": "darkgreen", 56 | "Fail": "darkred", 57 | "Fail (Partial)": "darkgoldenrod", 58 | } 59 | 60 | DEFAULT_REPORT_PATH = ["Test Type", "Provider", "Test Step"] 61 | -------------------------------------------------------------------------------- /reporting/postman_reporter/report_dashboard.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # %% imports 3 | import warnings 4 | from datetime import datetime 5 | 6 | import dash_bootstrap_components as dbc 7 | import plotly.express as px 8 | import plotly.graph_objects as go 9 | import postman_reporter.report_charts as report_charts 10 | import postman_reporter.reporter_util as reporter_util 11 | from dash import Dash 12 | from dash import dash_table 13 | from dash import dcc 14 | from dash import html 15 | from postman_reporter.report_config import * 16 | 17 | warnings.filterwarnings("ignore") 18 | 19 | 20 | def getApp(): 21 | # %% setup 22 | df = reporter_util.get_dataframes() 23 | now = str(datetime.now()) 24 | 25 | # %% main components 26 | # sidebar = report_charts.getSidebar() 27 | 28 | # %% charts 29 | summaryText = report_charts.getSummaryText(df["df_summary_provider"], now) 30 | summaryProvider, summaryProviderMulti = report_charts.getCards( 31 | df["df_summary_provider"] 32 | ) 33 | facet_tests = report_charts.getFacet(df["df_inter"]) 34 | results_chart = report_charts.getSunburst(df["df_details"]) 35 | results_tree = report_charts.getTree( 36 | df["df_details"], path=["Provider", "Test Type", "Test Step"] 37 | ) 38 | 39 | # %% setup content 40 | overview = [dbc.Col([], xl="12")] 41 | summary = [ 42 | dbc.Col( 43 | [ 44 | html.H2("Summary"), 45 | html.Div( 46 | summaryText, style={"margin-bottom": "3em", "font-size": "1.2em"} 47 | ), 48 | html.H3("Provider Summary"), 49 | html.Div(summaryProvider, style={"margin-bottom": "2em"}), 50 | html.Div(summaryProviderMulti, style={"margin-bottom": "3em"}), 51 | html.H3("Provider & Test Summary"), 52 | html.Div( 53 | [ 54 | report_charts.getTable( 55 | df["df_crosstab_results"], "summary-test-crosstab" 56 | ) 57 | ], 58 | style={"width": "60%"}, 59 | ), 60 | html.Br(), 61 | html.Div( 62 | [ 63 | report_charts.getTable( 64 | df["df_crosstab_results_multi"], 65 | "summary-test-crosstab-multi", 66 | ) 67 | ], 68 | style={"width": "80%"}, 69 | ), 70 | html.Br(), 71 | html.Br(), 72 | html.Div( 73 | [ 74 | dcc.Graph( 75 | id="test-facets", 76 | figure=facet_tests, 77 | config={"displayModeBar": False}, 78 | ) 79 | ] 80 | ), 81 | # getTable(df_summary_test, 'summary-test'), 82 | html.Br(), 83 | ], 84 | xl="12", 85 | ) 86 | ] 87 | results = [ 88 | dbc.Col( 89 | [ 90 | html.H2("Results"), 91 | dbc.Row( 92 | [ 93 | dbc.Col( 94 | [ 95 | dcc.Graph( 96 | id="chart-tests", 97 | figure=results_chart, 98 | config={"displayModeBar": False}, 99 | ), 100 | ], 101 | xl="6", 102 | ), 103 | dbc.Col( 104 | [ 105 | dcc.Graph( 106 | id="chart-tests-tree", 107 | figure=results_tree, 108 | config={"displayModeBar": False}, 109 | ), 110 | ], 111 | xl="6", 112 | ), 113 | ] 114 | ), 115 | ], 116 | xl="12", 117 | ) 118 | ] 119 | details = [ 120 | dbc.Col( 121 | [ 122 | html.H2("Details"), 123 | html.Div( 124 | [ 125 | report_charts.getTable( 126 | df["df_main"], "results-table", filter=True 127 | ) 128 | ], 129 | style={"width": "100%", "max-width": "100%"}, 130 | ), 131 | ], 132 | xl="12", 133 | ) 134 | ] 135 | 136 | # %% layout 137 | content = html.Div( 138 | id="page-content", 139 | style=CONTENT_STYLE, 140 | children=[ 141 | dbc.Row( 142 | [ 143 | html.H1("Trace Interop Test Results"), 144 | ] 145 | ), 146 | dbc.Row( 147 | [ 148 | dbc.Col( 149 | [ 150 | dbc.Row( 151 | id="overview", 152 | children=overview, 153 | style={"margin-bottom": "3em"}, 154 | ), 155 | dbc.Row( 156 | id="summary", 157 | children=summary, 158 | style={"margin-bottom": "3em"}, 159 | ), 160 | dbc.Row( 161 | id="results", 162 | children=results, 163 | style={"margin-bottom": "3em"}, 164 | ), 165 | dbc.Row( 166 | id="details", 167 | children=details, 168 | style={"margin-bottom": "3em"}, 169 | ), 170 | ], 171 | xl="12", 172 | ) 173 | ] 174 | ), 175 | ], 176 | ) 177 | 178 | # %% setup dash 179 | app = Dash( 180 | title="Traceability Interop Test Results", 181 | external_stylesheets=[dbc.themes.DARKLY], 182 | ) 183 | app.index_string = reporter_util.get_html_base() 184 | app.layout = html.Div([dcc.Location(id="url"), content]) 185 | 186 | return app 187 | 188 | 189 | # %% main it up 190 | if __name__ == "__main__": 191 | app = getApp() 192 | # print(app.layout) 193 | app.run_server() 194 | -------------------------------------------------------------------------------- /reporting/postman_reporter/report_data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # %% imports 3 | import json 4 | import warnings 5 | from datetime import datetime 6 | 7 | import pandas as pd 8 | 9 | # disable warnings 10 | warnings.filterwarnings("ignore") 11 | import postman_reporter.reporter_util as reporter_util 12 | from postman_reporter.report_config import * 13 | from tqdm import tqdm 14 | 15 | # %% setup 16 | tqdm.pandas() 17 | 18 | 19 | # %% process initial json 20 | def getData(get_reports, get_json): 21 | 22 | report_sources = get_reports() 23 | 24 | def build_detail(args, execution, assertion): 25 | if "error" not in assertion: 26 | return [ 27 | "postman", 28 | args["projectname"], 29 | args["provider"], 30 | args["testtype"], 31 | execution["item"]["name"], 32 | assertion["assertion"], 33 | "Pass", 34 | "", 35 | 1, 36 | ] 37 | else: 38 | return [ 39 | "postman", 40 | args["projectname"], 41 | args["provider"], 42 | args["testtype"], 43 | execution["item"]["name"], 44 | assertion["assertion"], 45 | "Fail", 46 | assertion["error"]["message"], 47 | 0, 48 | ] 49 | 50 | reports = [] 51 | print("Processing identified reports:", len(report_sources)) 52 | report_sources_progress = tqdm(report_sources) 53 | for report_source in report_sources_progress: 54 | rs = report_source.replace(".json", "") 55 | provider = rs.rsplit("-", 1)[1] 56 | if "exchange" in report_source: 57 | provider = " - ".join(rs.split("-")[-2:]) 58 | report = get_json(report_source) 59 | name = report["collection"]["info"]["name"] 60 | report_sources_progress.set_description_str(provider + ": " + name) 61 | 62 | for execution in report["run"]["executions"]: 63 | args = {} 64 | args["provider"] = provider 65 | args["projectname"] = "Trace Interop" 66 | args["testtype"] = name 67 | if "assertions" not in execution: 68 | print("No assertions found in execution " + execution["item"]["name"]) 69 | continue 70 | 71 | for assertion in execution["assertions"]: 72 | detail = build_detail(args, execution, assertion) 73 | # print(detail) 74 | reports.append(detail) 75 | 76 | # %% persist raw data 77 | print("Persisting raw fetched data") 78 | df_main = pd.DataFrame(reports, columns=HEADER) 79 | # use pipe separation bc of commas and other nastys 80 | df_main.to_csv( 81 | DATA_DIR + "full_report.csv", index=False, quoting=1, quotechar="'", sep="|" 82 | ) 83 | with open(DATA_DIR + "full_report.json", "w") as out: 84 | out.write(json.dumps(reports, indent=2)) 85 | 86 | # %% clean it up 87 | print("Processing data") 88 | df_main = df_main.loc[df_main["Provider"] != "sanity"] 89 | df_main["Test Type"] = df_main["Test Type"].str.replace(" Tutorial", "") 90 | 91 | df_details = df_main[COLUMNS_DETAIL].copy() 92 | df_main = df_main[COLUMNS_MAIN].copy() 93 | 94 | df_failed = df_details.loc[df_details["Result"] == "Fail"].copy() 95 | 96 | # %% groupings 97 | df_inter = df_details[["Provider", "Test Type", "Test Step", "Passing"]] 98 | df_inter = df_inter.loc[~df_inter["Provider"].str.contains(" - ")].copy() 99 | 100 | df_inter_full = df_details[["Provider", "Test Type", "Test Step", "Passing"]].copy() 101 | df_inter_full["Size"] = 12 102 | df_inter_full["Shape"] = "Box" 103 | 104 | df_summary_test = ( 105 | df_details.groupby(["Test Type", "Provider"])["Passing"] 106 | .mean() 107 | .apply(lambda x: "{:.1%}".format(x)) 108 | .unstack("Test Type") 109 | .fillna("0%") 110 | .reset_index() 111 | ) 112 | df_summary_provider = ( 113 | df_details.groupby(["Provider"])["Passing"].mean().fillna(0).reset_index() 114 | ) 115 | 116 | df_summary_provider["Passing"] = df_summary_provider["Passing"].apply( 117 | lambda x: "{:.1%}".format(x) 118 | ) 119 | df_summary_provider = df_summary_provider.sort_values(["Provider"], ascending=True) 120 | 121 | df_singles = df_details.loc[~df_details["Provider"].str.contains(" - ")].copy() 122 | df_multi = df_details.loc[df_details["Provider"].str.contains(" - ")].copy() 123 | 124 | df_crosstab_results = ( 125 | df_singles.groupby(["Test Type", "Provider"])["Passing"] 126 | .mean() 127 | .round(3) 128 | .apply(lambda x: "{:.1%}".format(x)) 129 | .unstack() 130 | .fillna("n/a") 131 | .reset_index() 132 | ) 133 | 134 | df_crosstab_results_multi = ( 135 | df_multi.groupby(["Test Type", "Provider"])["Passing"] 136 | .mean() 137 | .round(3) 138 | .apply(lambda x: "{:.1%}".format(x)) 139 | .unstack() 140 | .fillna("n/a") 141 | .reset_index() 142 | ) 143 | 144 | # %% save results 145 | print("Saving data frames") 146 | reporter_util.save_dataframes( 147 | [ 148 | {"name": "df_main", "data": df_main}, 149 | {"name": "df_failed", "data": df_failed}, 150 | {"name": "df_details", "data": df_details}, 151 | {"name": "df_inter_full", "data": df_inter_full}, 152 | {"name": "df_inter", "data": df_inter}, 153 | {"name": "df_summary_test", "data": df_summary_test}, 154 | {"name": "df_summary_provider", "data": df_summary_provider}, 155 | {"name": "df_singles", "data": df_singles}, 156 | {"name": "df_multi", "data": df_multi}, 157 | {"name": "df_crosstab_results", "data": df_crosstab_results}, 158 | {"name": "df_crosstab_results_multi", "data": df_crosstab_results_multi}, 159 | ] 160 | ) 161 | 162 | 163 | if __name__ == "__main__": 164 | getData() 165 | exit(0) 166 | -------------------------------------------------------------------------------- /reporting/postman_reporter/report_static.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from datetime import datetime 3 | 4 | import plotly 5 | import postman_reporter.report_charts as report_charts 6 | import postman_reporter.reporter_util as reporter_util 7 | from jinja2 import Template 8 | from postman_reporter.report_config import * 9 | 10 | 11 | def generate_html(template): 12 | df = reporter_util.get_dataframes() 13 | crosstabDF = df["df_crosstab_results"] 14 | crosstabMultiDF = df["df_crosstab_results_multi"] 15 | detailsDF = df["df_details"] 16 | summaryDF = df["df_summary_provider"] 17 | 18 | facet_tests = report_charts.getFacet(df["df_inter"]) 19 | results_chart = report_charts.getSunburst(detailsDF) 20 | results_tree = report_charts.getTree( 21 | detailsDF, path=["Provider", "Test Type", "Test Step"] 22 | ) 23 | 24 | # Change percentage strings to floats for processing 25 | summaryDF["Passing"] = summaryDF["Passing"].str.rstrip("%").astype("float") / 100.0 26 | 27 | cards = renderSummaryCards(summaryDF) 28 | 29 | args = {"include_plotlyjs": False, "output_type": "div"} 30 | rendered = template.render( 31 | summary_text=renderSummaryText(summaryDF), 32 | cards_single=cards["single"], 33 | cards_multi=cards["multi"], 34 | summary_table_single=renderTable(crosstabDF), 35 | summary_table_multi=renderTable(crosstabMultiDF), 36 | facet_tests=plotly.offline.plot(facet_tests, **args), 37 | results_chart=plotly.offline.plot(results_chart, **args), 38 | results_tree=plotly.offline.plot(results_tree, **args), 39 | details=renderTable(detailsDF), 40 | ) 41 | 42 | with open(f"{CI_DIR}/index.html", "wt") as fh: 43 | fh.write(rendered) 44 | 45 | 46 | def renderTable(df): 47 | tpl = Template( 48 | """ 49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 61 | 62 | 63 | {% for cell in columns -%}{% endfor -%} 64 | 65 | 66 | 67 | {%for row in rows -%}{% for cell in row -%}{% endfor -%}{% endfor -%} 68 | 69 |
{{cell}}
{{cell}}
70 |
71 |
72 |
73 |
74 |
75 | """ 76 | ) 77 | 78 | return tpl.render( 79 | columns=df.columns, 80 | rows=df.values, 81 | ) 82 | 83 | 84 | def renderSummaryText(df): 85 | 86 | tpl = """ 87 |

88 | These are the test results for the 89 | Open API for Interoperable Traceability 90 | as of: {now} 91 |

92 |

93 | The highest current % of passed tests by a single provider is: {max:.1%}
94 | The lowest is: {min:.1%}
95 | Across all providers the average % of passed tests is: {avg:.1%} 96 |

97 | """ 98 | 99 | return tpl.format( 100 | now=datetime.now(), 101 | min=df["Passing"].min(), 102 | max=df["Passing"].max(), 103 | avg=df["Passing"].mean(), 104 | ) 105 | 106 | 107 | def renderSummaryCards(df): 108 | 109 | tpl = """ 110 |
111 |
112 |
{header}
113 |
114 |

{passed:.1%}

115 |

of tests taken, passed

116 |
117 |
118 |
119 | """ 120 | 121 | single = [] 122 | for _, r in df[~df["Provider"].str.contains(" - ")].iterrows(): 123 | single.append(tpl.format(header=r["Provider"], passed=r["Passing"])) 124 | 125 | multi = [] 126 | for _, r in df[df["Provider"].str.contains(" - ")].iterrows(): 127 | multi.append(tpl.format(header=r["Provider"], passed=r["Passing"])) 128 | 129 | return {"single": single, "multi": multi} 130 | -------------------------------------------------------------------------------- /reporting/postman_reporter/reporter_util.py: -------------------------------------------------------------------------------- 1 | # %% imports 2 | import glob 3 | import inspect 4 | import pickle 5 | from typing import List 6 | 7 | from postman_reporter.report_config import * 8 | from tqdm import tqdm 9 | 10 | 11 | # %% methods 12 | def get_var_name(var): 13 | local_vars = inspect.currentframe().f_back.f_locals.items() 14 | return [var_name for var_name, var_val in local_vars if var_val is var] 15 | 16 | def save_dataframes(dataframes: List): 17 | df_progress = tqdm(dataframes) 18 | for df in df_progress: 19 | df_name = df['name'] 20 | df_progress.set_description('Saving: %s' % df_name) 21 | with open(DATA_DIR+DF_PREFIX+df_name+DF_EXT, 'wb') as out_file: 22 | pickle.dump(df, out_file) 23 | 24 | def get_dataframes(data_dir=DATA_DIR, prefix=DF_PREFIX, postfix = DF_EXT): 25 | dataframes_names = glob.glob(data_dir+prefix+'*'+postfix) 26 | df_progress = tqdm(dataframes_names) 27 | dataframes = {} 28 | for df in df_progress: 29 | with open(df, 'rb') as in_file: 30 | d = pickle.load(in_file) 31 | dataframes[d['name']] = d['data'] 32 | return dataframes 33 | 34 | def get_html_base(): 35 | base = ''' 36 | 37 | 38 | 39 | {%metas%} 40 | {%title%} 41 | {%favicon%} 42 | 119 | {%css%} 120 | 121 | 122 | {%app_entry%} 123 |
124 | {%config%} 125 | {%scripts%} 126 | {%renderer%} 127 |
128 | 129 | 130 | ''' 131 | return base 132 | 133 | # %% 134 | -------------------------------------------------------------------------------- /reporting/reporter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import argparse 3 | import glob 4 | import json 5 | import os 6 | 7 | import requests 8 | from jinja2 import Environment, FileSystemLoader 9 | from postman_reporter import (report_config, report_dashboard, report_data, 10 | report_static) 11 | 12 | 13 | def runData(args): 14 | def _json_from_url(url): 15 | return json.loads(requests.get(url).text) 16 | 17 | def _reports_from_url(url): 18 | def func(): 19 | data = json.loads(requests.get(url).text) 20 | items = data["items"] 21 | report_sources = [] 22 | for item in items: 23 | if ".json" in item: 24 | report_sources.append(item) 25 | return report_sources 26 | 27 | return func 28 | 29 | url = os.path.join(report_config.REPORTS_BASE_URL, args.type, "index.json") 30 | 31 | report_data.getData( 32 | get_reports=_reports_from_url(url), 33 | get_json=_json_from_url, 34 | ) 35 | 36 | return 0 37 | 38 | 39 | def runDash(): 40 | app = report_dashboard.getApp() 41 | print(app) 42 | app.run_server() 43 | return 0 44 | 45 | 46 | def runHtml(args): 47 | # Create the jinja2 environment. 48 | current_directory = os.path.dirname(os.path.abspath(__file__)) 49 | env = Environment(loader=FileSystemLoader(current_directory)) 50 | path = os.path.join("assets", f"{args.type}.j2") 51 | template = env.get_template(path) 52 | report_static.generate_html(template) 53 | return 0 54 | 55 | 56 | def runCi(args): 57 | """ 58 | runCi prepares report data from local docs/reports folder and uses that 59 | data to generate a static interactive HTML report. 60 | """ 61 | 62 | def _reports_from_file(path): 63 | def func(): 64 | files = glob.glob(f"{path}/*.json") 65 | return [i for i in files if not i.endswith("index.json")] 66 | 67 | return func 68 | 69 | def _json_from_file(path): 70 | with open(path, "r") as fh: 71 | return json.load(fh) 72 | 73 | report_data.getData( 74 | get_reports=_reports_from_file(report_config.CI_DIR), 75 | get_json=_json_from_file, 76 | ) 77 | 78 | runHtml(args) 79 | 80 | 81 | def main(args): 82 | # first check for data dir 83 | if not os.path.exists("./data"): 84 | os.mkdir( 85 | "./data" 86 | ) # explicitly let it throw an exception if permissions are not there 87 | 88 | # then for html 89 | if not os.path.exists("./html"): 90 | os.mkdir( 91 | "./html" 92 | ) # explicitly let it throw an exception if permissions are not there 93 | 94 | # now check args 95 | if args.mode == "all" or args.mode == "data": 96 | runData(args) 97 | 98 | if args.mode == "all" or args.mode == "html": 99 | runHtml(args) 100 | 101 | if args.mode == "all" or args.mode == "dashboard": 102 | runDash() 103 | 104 | if args.mode == "ci": 105 | runCi(args) 106 | 107 | 108 | if __name__ == "__main__": 109 | parser = argparse.ArgumentParser( 110 | description="Interop test results reporting utility" 111 | ) 112 | parser.add_argument( 113 | "--mode", 114 | type=str, 115 | default="all", 116 | const="all", 117 | nargs="?", 118 | choices=["all", "data", "html", "dashboard", "ci"], 119 | help="mode to run the reporter in", 120 | ) 121 | 122 | # Mutually-exclusive group must be nested in an argument group in order to 123 | # support providing a title and description in help output. 124 | group = parser.add_argument_group( 125 | "Report Type", 126 | "One of the following options MUST be specified.", 127 | ) 128 | group = group.add_mutually_exclusive_group(required=True) 129 | 130 | group.add_argument( 131 | "-c", 132 | "--conformance", 133 | dest="type", 134 | action="store_const", 135 | const="conformance", 136 | help="generate a report based on the most recently published conformance testing output", 137 | ) 138 | 139 | group.add_argument( 140 | "-i", 141 | "--interoperability", 142 | dest="type", 143 | action="store_const", 144 | const="interoperability", 145 | help="generate a report based on the most recently published interoperability testing output", 146 | ) 147 | 148 | args = parser.parse_args() 149 | main(args) 150 | -------------------------------------------------------------------------------- /reporting/requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2==3.1.2 2 | dash-bootstrap-components 3 | dash==2.3.1 4 | numpy==1.22.3 5 | pandas==1.4.2 6 | plotly==5.7.0 7 | requests==2.27.1 8 | tqdm==4.64.0 9 | urllib3==1.26.9 -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Traceability API Conformance Testing 2 | 3 | The Postman suite in this directory is intended to test API implmentations for 4 | conformance against the published OpenAPI spec. 5 | 6 | Testing is performed by making requests to an endpoint and validating response 7 | bodies against configured schemas. Schemas are maintained in the Postman collection 8 | by the `update_conformance_schemas.sh` script, which is run by a workflow triggered 9 | by changes to files in the `docs/openapi` folder. 10 | 11 | Request bodies for the "Bad Request" series of negative tests for the `/credentials/verify` endpoint are generated using the `update_conformance_vcs.js` script. This will dynamically generate invalid credentials with valid signatures, and inject them directly into the Postman suite. Output is written to standard out, and should be redirected to a temporary file before overwriting the existing Postman suite, for example: 12 | 13 | ```bash 14 | ./update_conformance_vcs.js > tmp.json 15 | mv tmp.json conformance_suite.postman_collection.json 16 | ``` 17 | 18 | ## Importing The Test Suite 19 | 20 | ### Create a workspace 21 | 22 | Postman test suites are imported into workspaces. You may either choose an existing workspace before importing, or create a new one by opening the "Workspaces" dropdown menu at the top left of the Postman window and clicking on the "Create Workspace" button. 23 | 24 | 25 | 26 | In the following examples, the "Traceability Conformance" workspace will be created and used. 27 | 28 | 29 | 30 | ### Import the collection 31 | 32 | Once you have created and/or selected your workspace, use the "Import" button to import the conformance collection. 33 | 34 | 35 | 36 | When the import modal window opens, select the "Link" option, paste the [link to the Postman collection](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/tests/conformance_suite.postman_collection.json) in the text input, and click "Continue" 37 | 38 | 39 | 40 | After you click "Continue", Postman will download and process the remote url and present a confirmation screen. Click the "Import" button to continue. 41 | 42 | 43 | 44 | ### Import the environment 45 | 46 | Use the "Import" button again to import the conformance environment. 47 | 48 | 49 | 50 | When the import modal window opens, select the "Link" option, paste the [link to the Postman environment](https://raw.githubusercontent.com/w3c-ccg/traceability-interop/main/tests/conformance_suite.postman_environment.json) in the text input, and click "Continue" 51 | 52 | 53 | 54 | After you click "Continue", Postman will download and process the remote url and present a confirmation screen. Click the "Import" button to continue. 55 | 56 | 57 | 58 | ### Configure the environment 59 | 60 | Once the environment finishes importing, you will need to add in the values specific to your implementation. Click on the "Environments" tab, then highlight the "Traceability Conformance" environment and update the values under the "INITIAL VALUE" column. Click "Reset All" to copy the values to the "CURRENT VALUE" column and then click "Save". 61 | 62 | 63 | 64 | You are now ready to begin executing conformance tests against your implementation! 65 | 66 | ## Testing Notes 67 | 68 | ### Optional Elements 69 | When a schema calls for optional elements, the base happy path test will exclude all optional items. 70 | 71 | There will be one additional happy path test for each optional item, which will be run separately. 72 | 73 | There will be one negative test for each optional item in which an invalid value will be used. 74 | 75 | ### Alternate Elements 76 | When a schema calls for one of several possible values for an item, the base happy path test will include testing for the first alternate. Additional happy path tests will be created for each of the remaining alternates. 77 | 78 | Negative tests will be created separately for each alternate item. 79 | 80 | ### Negative Type Checking 81 | Negative testing for type checking is exhaustive over javascript types, e.g., if a schema element is defined to be an array, the conformance suite will include one test for each of the other JS types (`boolean`, `integer`, `null`, `object`, and `string`) to ensure that the request fails. 82 | 83 | ### Negative Value Checking 84 | When a schema element is constrained to a specific set of values, negative testing will only check that the request fails for a single bad value. In this case, exhaustive testing is not feasible. 85 | -------------------------------------------------------------------------------- /tests/conformance_suite.postman_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "d7a28a8c-69bc-4df1-86f3-47376a3b7c68", 3 | "name": "Traceability Conformance", 4 | "values": [ 5 | { 6 | "key": "API_BASE_URL", 7 | "value": "", 8 | "type": "default", 9 | "enabled": true 10 | }, 11 | { 12 | "key": "CLIENT_ID", 13 | "value": "", 14 | "type": "default", 15 | "enabled": true 16 | }, 17 | { 18 | "key": "CLIENT_SECRET", 19 | "value": "", 20 | "type": "secret", 21 | "enabled": true 22 | }, 23 | { 24 | "key": "CLIENT_SCOPE", 25 | "value": "", 26 | "type": "default", 27 | "enabled": true 28 | }, 29 | { 30 | "key": "ORGANIZATION_DID_WEB", 31 | "value": "", 32 | "type": "default", 33 | "enabled": true 34 | }, 35 | { 36 | "key": "TOKEN_AUDIENCE", 37 | "value": "", 38 | "type": "default", 39 | "enabled": true 40 | }, 41 | { 42 | "key": "TOKEN_ENDPOINT", 43 | "value": "", 44 | "type": "default", 45 | "enabled": true 46 | } 47 | ], 48 | "_postman_variable_scope": "environment", 49 | "_postman_exported_at": "2023-05-23T13:56:55.252Z", 50 | "_postman_exported_using": "Postman/9.31.29" 51 | } -------------------------------------------------------------------------------- /tests/resources/configure-environment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/tests/resources/configure-environment.png -------------------------------------------------------------------------------- /tests/resources/create-workspace-details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/tests/resources/create-workspace-details.png -------------------------------------------------------------------------------- /tests/resources/create-workspace-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/tests/resources/create-workspace-start.png -------------------------------------------------------------------------------- /tests/resources/import-collection-confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/tests/resources/import-collection-confirm.png -------------------------------------------------------------------------------- /tests/resources/import-collection-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/tests/resources/import-collection-link.png -------------------------------------------------------------------------------- /tests/resources/import-environment-confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/tests/resources/import-environment-confirm.png -------------------------------------------------------------------------------- /tests/resources/import-environment-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/tests/resources/import-environment-link.png -------------------------------------------------------------------------------- /tests/resources/import-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/tests/resources/import-start.png -------------------------------------------------------------------------------- /tests/resources/select-environment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c-ccg/traceability-interop/103073c6f132e5865805101842bdb7594a1b67ce/tests/resources/select-environment.png -------------------------------------------------------------------------------- /tests/update_conformance_schemas.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # update_schemas.sh 4 | # 5 | # 6 | 7 | set -euo pipefail 8 | 9 | # Dereferenced JSON schema allows extraction of specific parts using jq 10 | SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )"; 11 | YAML_SRC=`dirname "$SCRIPT_DIR"`/docs/openapi/openapi.yml 12 | JSON_SCHEMA=$(npx swagger-cli bundle "$YAML_SRC" --dereference) 13 | 14 | # update_postman injects schema into postman collection 15 | # $1 - Postman schema file 16 | # $2 - Postman collection variable name for schema 17 | # $3 - Stringified JSON schema to inject into Postman collection 18 | function update_postman() { 19 | FILE=$1; KEY=$2; SCHEMA=$3 20 | # Using a temp file (vs. in-place update) prevents truncation on failure 21 | TMP=$(mktemp) 22 | jq --arg k "$KEY" --arg s "$SCHEMA" --tab \ 23 | '(.variable[] | select(.key==($k))).value=($s)' \ 24 | "$FILE" > "$TMP" && mv "$TMP" "$FILE" 25 | } 26 | 27 | # get_schema extracts stringified JSON schema from openapi schema file 28 | # $1 - JQ selector string for the schema to extract 29 | function get_schema() { 30 | SCHEMA=$(echo $JSON_SCHEMA | jq -c $1) 31 | # Examples should be excluded from schema 32 | SCHEMA=$(echo $SCHEMA | jq -c 'del(..|.example?)') 33 | # JSON schema does not support "format" keyword for "integer" type 34 | SCHEMA=$(echo $SCHEMA | sed -e 's/,"format":"int32"//') 35 | echo $SCHEMA 36 | } 37 | 38 | # API Configuration [200] 39 | update_postman \ 40 | "conformance_suite.postman_collection.json" \ 41 | "responseSchema200ApiConfiguration" \ 42 | "$(get_schema '.paths["/did.json"].get.responses["200"].content["application/json"].schema')" 43 | 44 | # Identifiers [200] 45 | update_postman \ 46 | "conformance_suite.postman_collection.json" \ 47 | "responseSchema200Identifiers" \ 48 | "$(get_schema '.paths["/identifiers/{did}"].get.responses["200"].content["application/json"].schema')" 49 | 50 | # Identifiers [400] 51 | update_postman \ 52 | "conformance_suite.postman_collection.json" \ 53 | "responseSchema400Identifiers" \ 54 | "$(get_schema '.paths["/identifiers/{did}"].get.responses["400"].content["application/json"].schema')" 55 | 56 | # Identifiers [404] 57 | update_postman \ 58 | "conformance_suite.postman_collection.json" \ 59 | "responseSchema404" \ 60 | "$(get_schema '.paths["/identifiers/{did}"].get.responses["404"].content["application/json"].schema')" 61 | 62 | # Credentials - Issue [201] 63 | update_postman \ 64 | "conformance_suite.postman_collection.json" \ 65 | "responseSchema201CredentialsIssue" \ 66 | "$(get_schema '.paths["/credentials/issue"].post.responses["201"].content["application/json"].schema')" 67 | 68 | # Credentials - Issue [400] 69 | update_postman \ 70 | "conformance_suite.postman_collection.json" \ 71 | "responseSchema400" \ 72 | "$(get_schema '.paths["/credentials/issue"].post.responses["400"].content["application/json"].schema')" 73 | 74 | # Credentials - Issue [401] 75 | update_postman \ 76 | "conformance_suite.postman_collection.json" \ 77 | "responseSchema401" \ 78 | "$(get_schema '.paths["/credentials/issue"].post.responses["401"].content["application/json"].schema')" 79 | 80 | # Credentials - Issue [403] 81 | update_postman \ 82 | "conformance_suite.postman_collection.json" \ 83 | "responseSchema403" \ 84 | "$(get_schema '.paths["/credentials/issue"].post.responses["403"].content["application/json"].schema')" 85 | 86 | # Credentials - Issue [422] 87 | update_postman \ 88 | "conformance_suite.postman_collection.json" \ 89 | "responseSchema422CredentialsIssue" \ 90 | "$(get_schema '.paths["/credentials/issue"].post.responses["422"].content["application/json"].schema')" 91 | 92 | # Credentials - Issue [500] 93 | update_postman \ 94 | "conformance_suite.postman_collection.json" \ 95 | "responseSchema500" \ 96 | "$(get_schema '.paths["/credentials/issue"].post.responses["500"].content["application/json"].schema')" 97 | 98 | # Credentials - Verify [200] 99 | update_postman \ 100 | "conformance_suite.postman_collection.json" \ 101 | "responseSchema200CredentialsVerify" \ 102 | "$(get_schema '.paths["/credentials/verify"].post.responses["200"].content["application/json"].schema')" 103 | -------------------------------------------------------------------------------- /tests/valid-credential.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": ["https://www.w3.org/2018/credentials/v1", "https://w3id.org/traceability/v1"], 3 | "credentialSubject": { 4 | "id": "did:example:123" 5 | }, 6 | "id": "urn:uuid:57016a7f-0e4c-4be9-beec-cf39c4dd459a", 7 | "issuanceDate": "2006-01-02T15:04:05Z", 8 | "issuer": "did:key:z6MkgVHZNqLBqoQAoGxRiSJP5gLgVEDCJJzT5ZsGEabKtfyn", 9 | "type": ["VerifiableCredential"] 10 | } 11 | --------------------------------------------------------------------------------