diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..8794551 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: CloudQuery Central Issues Tracker + url: https://github.com/cloudquery/cloudquery/issues + about: Please file any issue with regards to any of CloudQuery repo in the main repository with the right labels. + - name: CloudQuery Community + url: https://community.cloudquery.io + about: Join our community to get help from us as well as other users and experts. diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7ce2828..0ddedfa 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,12 +10,14 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: "3.x" + python-version: "3.13" - name: Install dependencies - run: pip install -r requirements.txt + run: | + pip install --upgrade pip + pip install -r requirements.txt - name: Check formatting run: make fmt-check diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index aa35df8..ac0b8b8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,28 +2,28 @@ name: publish on: push: tags: - - 'v*.*.*' + - "v*.*.*" jobs: - pypi-publish: - name: upload release to PyPI - runs-on: ubuntu-latest - permissions: - # IMPORTANT: this permission is mandatory for trusted publishing - id-token: write - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install build - - name: Build package - run: python -m build - - name: Publish package distributions to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 \ No newline at end of file + pypi-publish: + name: upload release to PyPI + runs-on: ubuntu-latest + permissions: + # IMPORTANT: this permission is mandatory for trusted publishing + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install dependencies + run: | + pip install --upgrade pip + pip install -r requirements.txt + pip install build + - name: Build package + run: python -m build + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/release_pr.yml b/.github/workflows/release_pr.yml index 7e4b9c9..ddb8a6b 100644 --- a/.github/workflows/release_pr.yml +++ b/.github/workflows/release_pr.yml @@ -8,14 +8,7 @@ jobs: release-please: runs-on: ubuntu-latest steps: - - uses: google-github-actions/release-please-action@v3 + - uses: googleapis/release-please-action@v4 id: release with: - release-type: python - package-name: plugin-sdk-python token: ${{ secrets.GH_CQ_BOT }} - pull-request-title-pattern: "chore${scope}: Release${component} v${version}" - # Should breaking changes before 1.0.0 produce minor bumps? - bump-minor-pre-major: true - # Should feat changes before 1.0.0 produce patch bumps instead of minor bumps? - bump-patch-for-minor-pre-major: true diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index eec37c9..fcec4cd 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -12,12 +12,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + - # Required for the package command tests to work + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: "3.x" + python-version: "3.13" - name: Install dependencies - run: pip install -r requirements.txt + run: | + pip install --upgrade pip + pip install -r requirements.txt - name: Run tests run: make test diff --git a/.gitignore b/.gitignore index 2dc53ca..e605249 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,5 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ + +.DS_Store \ No newline at end of file diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..4694839 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.1.45" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index cd8366a..df4795a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,438 @@ # Changelog +## [0.1.45](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.44...v0.1.45) (2025-07-01) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.44 ([#303](https://github.com/cloudquery/plugin-sdk-python/issues/303)) ([aefd9e1](https://github.com/cloudquery/plugin-sdk-python/commit/aefd9e1f1e4189301ad92bb171612613b6a9dedc)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.45 ([#306](https://github.com/cloudquery/plugin-sdk-python/issues/306)) ([e31f6a0](https://github.com/cloudquery/plugin-sdk-python/commit/e31f6a0b70931d91f7575e145c7bf9eae51a1efa)) +* **deps:** Update dependency exceptiongroup to v1.3.0 ([#304](https://github.com/cloudquery/plugin-sdk-python/issues/304)) ([73e911f](https://github.com/cloudquery/plugin-sdk-python/commit/73e911f766cda03a342b5f2bf1a51b5f8b0e0d57)) +* **deps:** Update dependency grpcio to v1.73.0 ([#305](https://github.com/cloudquery/plugin-sdk-python/issues/305)) ([9c9ee6c](https://github.com/cloudquery/plugin-sdk-python/commit/9c9ee6cd9b47428d2ce7ef7cb890907661bc039d)) +* **deps:** Update dependency protobuf to v5.29.5 [SECURITY] ([#301](https://github.com/cloudquery/plugin-sdk-python/issues/301)) ([dbc81e3](https://github.com/cloudquery/plugin-sdk-python/commit/dbc81e3e20a5e5a359dc219bd8a885162f4dce2a)) + +## [0.1.44](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.43...v0.1.44) (2025-05-09) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.43 ([#300](https://github.com/cloudquery/plugin-sdk-python/issues/300)) ([addbf20](https://github.com/cloudquery/plugin-sdk-python/commit/addbf2043330e6e6d78a94b00e8605cf1de21039)) +* **deps:** Update dependency grpcio to v1.71.0 ([#299](https://github.com/cloudquery/plugin-sdk-python/issues/299)) ([ffce9eb](https://github.com/cloudquery/plugin-sdk-python/commit/ffce9eba5c125db08a7e8ec46d33520e2991a27d)) +* **deps:** Update dependency numpy to v2.2.5 ([#295](https://github.com/cloudquery/plugin-sdk-python/issues/295)) ([21a5637](https://github.com/cloudquery/plugin-sdk-python/commit/21a563717a7a1e652443225dc54574a4f24691a0)) +* **deps:** Update dependency pytest to v8.3.5 ([#296](https://github.com/cloudquery/plugin-sdk-python/issues/296)) ([5cc3e5e](https://github.com/cloudquery/plugin-sdk-python/commit/5cc3e5e30e2786cbfee58b1488c75caa335a092e)) + +## [0.1.43](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.42...v0.1.43) (2025-04-01) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.42 ([#294](https://github.com/cloudquery/plugin-sdk-python/issues/294)) ([5220174](https://github.com/cloudquery/plugin-sdk-python/commit/5220174beb1459de4bf5458b7c94b12d55371529)) +* **deps:** Update dependency numpy to v2.2.4 ([#291](https://github.com/cloudquery/plugin-sdk-python/issues/291)) ([45a9f5b](https://github.com/cloudquery/plugin-sdk-python/commit/45a9f5b18a9531973e7e6c7f8909109e9b3a719a)) +* **deps:** Update dependency protobuf to v5.29.4 ([#292](https://github.com/cloudquery/plugin-sdk-python/issues/292)) ([8480317](https://github.com/cloudquery/plugin-sdk-python/commit/84803177b9d3a164d9e47c30659ff8a1a0a4b36d)) + +## [0.1.42](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.41...v0.1.42) (2025-03-06) + + +### Bug Fixes + +* **deps:** Update dependency black to v25 ([#287](https://github.com/cloudquery/plugin-sdk-python/issues/287)) ([dca6df3](https://github.com/cloudquery/plugin-sdk-python/commit/dca6df39f8fd94174add2f0799852f0c246f8c70)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.41 ([#289](https://github.com/cloudquery/plugin-sdk-python/issues/289)) ([17d6e4a](https://github.com/cloudquery/plugin-sdk-python/commit/17d6e4a0a4034d995bd5135cea094dfd2d8893c1)) +* **deps:** Update dependency Jinja2 to v3.1.6 [SECURITY] ([#290](https://github.com/cloudquery/plugin-sdk-python/issues/290)) ([995fb93](https://github.com/cloudquery/plugin-sdk-python/commit/995fb93e34895afc0128fbb2ff82eeaa9e7d5aa8)) +* **deps:** Update dependency numpy to v2.2.3 ([#284](https://github.com/cloudquery/plugin-sdk-python/issues/284)) ([d7d344a](https://github.com/cloudquery/plugin-sdk-python/commit/d7d344a7bacef1f24cad800d4bf45c92b2c4bc9b)) +* **deps:** Update dependency pyarrow to v19.0.1 ([#285](https://github.com/cloudquery/plugin-sdk-python/issues/285)) ([29dda77](https://github.com/cloudquery/plugin-sdk-python/commit/29dda775f6479b13929a93fe7646502a0871df57)) +* **deps:** Update dependency pytz to v2025 ([#288](https://github.com/cloudquery/plugin-sdk-python/issues/288)) ([9134d63](https://github.com/cloudquery/plugin-sdk-python/commit/9134d6384ba9e6db8c24110c7e28832cd427cb60)) + +## [0.1.41](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.40...v0.1.41) (2025-02-03) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.40 ([#283](https://github.com/cloudquery/plugin-sdk-python/issues/283)) ([2ad2aff](https://github.com/cloudquery/plugin-sdk-python/commit/2ad2aff1682b32fc8c0ccc665e4ebce36ed1991a)) +* **deps:** Update dependency grpcio to v1.70.0 ([#278](https://github.com/cloudquery/plugin-sdk-python/issues/278)) ([323c94f](https://github.com/cloudquery/plugin-sdk-python/commit/323c94f3923d4d353f06acac38fa85f5af845f47)) +* **deps:** Update dependency grpcio-tools to v1.70.0 ([#279](https://github.com/cloudquery/plugin-sdk-python/issues/279)) ([6e3598f](https://github.com/cloudquery/plugin-sdk-python/commit/6e3598fb097c1c2be012b2ac1b5f02c34a71a386)) +* **deps:** Update dependency numpy to v2.2.2 ([#275](https://github.com/cloudquery/plugin-sdk-python/issues/275)) ([1588459](https://github.com/cloudquery/plugin-sdk-python/commit/1588459bc9381319f9acb469a5ee888647cb1aec)) +* **deps:** Update dependency protobuf to v5.29.3 ([#276](https://github.com/cloudquery/plugin-sdk-python/issues/276)) ([2ff79da](https://github.com/cloudquery/plugin-sdk-python/commit/2ff79da1a55a724650329f349e9aa5c8389e53eb)) +* **deps:** Update dependency pyarrow to v19 ([#280](https://github.com/cloudquery/plugin-sdk-python/issues/280)) ([c5f06dc](https://github.com/cloudquery/plugin-sdk-python/commit/c5f06dc58be6816b1e600925245fcc06eb30d030)) +* **deps:** Update dependency structlog to v25 ([#281](https://github.com/cloudquery/plugin-sdk-python/issues/281)) ([e78c775](https://github.com/cloudquery/plugin-sdk-python/commit/e78c775422a94d0fd8e3c36d38f230197b6fce19)) +* **deps:** Update dependency tzdata to v2025 ([#282](https://github.com/cloudquery/plugin-sdk-python/issues/282)) ([016a074](https://github.com/cloudquery/plugin-sdk-python/commit/016a0740dc063abadb4d4200375c209071eacd3e)) + +## [0.1.40](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.39...v0.1.40) (2025-01-07) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.39 ([#273](https://github.com/cloudquery/plugin-sdk-python/issues/273)) ([d7b7590](https://github.com/cloudquery/plugin-sdk-python/commit/d7b7590168a8109d26ec9170e8ac66586bb827db)) +* **deps:** Update dependency numpy to v2.2.1 ([#270](https://github.com/cloudquery/plugin-sdk-python/issues/270)) ([0077c26](https://github.com/cloudquery/plugin-sdk-python/commit/0077c2683235f06ceadb76d487c7d784033eed3e)) +* **deps:** Update dependency protobuf to v5.29.2 ([#269](https://github.com/cloudquery/plugin-sdk-python/issues/269)) ([6a1e021](https://github.com/cloudquery/plugin-sdk-python/commit/6a1e021d4a7c0cbbef7eec67416b43d101f33bf1)) +* **deps:** Update dependency six to v1.17.0 ([#272](https://github.com/cloudquery/plugin-sdk-python/issues/272)) ([f3733b7](https://github.com/cloudquery/plugin-sdk-python/commit/f3733b76604527b15aae68bee1e38e83961fad50)) + +## [0.1.39](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.38...v0.1.39) (2024-12-24) + + +### Bug Fixes + +* **deps:** Update dependency Jinja2 to v3.1.5 ([#267](https://github.com/cloudquery/plugin-sdk-python/issues/267)) ([fd445a1](https://github.com/cloudquery/plugin-sdk-python/commit/fd445a1301ee59f24373535a63f16184febfe141)) + +## [0.1.38](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.37...v0.1.38) (2024-12-02) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.38 ([#260](https://github.com/cloudquery/plugin-sdk-python/issues/260)) ([10d7f2c](https://github.com/cloudquery/plugin-sdk-python/commit/10d7f2ccd0871f23a3942cea87c90a3a53037666)) +* **deps:** Update dependency grpcio to v1.68.0 ([#255](https://github.com/cloudquery/plugin-sdk-python/issues/255)) ([83ce2a3](https://github.com/cloudquery/plugin-sdk-python/commit/83ce2a399bb19570a663d622a90dafe0a112d736)) +* **deps:** Update dependency grpcio to v1.68.1 ([#261](https://github.com/cloudquery/plugin-sdk-python/issues/261)) ([eb06ffb](https://github.com/cloudquery/plugin-sdk-python/commit/eb06ffb192964670d0bda0b7dfa24b1ef0ddb255)) +* **deps:** Update dependency grpcio-tools to v1.68.0 ([#257](https://github.com/cloudquery/plugin-sdk-python/issues/257)) ([b931c99](https://github.com/cloudquery/plugin-sdk-python/commit/b931c9914b42808a672b976b72fe6cd0937c8a40)) +* **deps:** Update dependency grpcio-tools to v1.68.1 ([#262](https://github.com/cloudquery/plugin-sdk-python/issues/262)) ([4e8584c](https://github.com/cloudquery/plugin-sdk-python/commit/4e8584c7556a50d0bd53740e4fa2a84d79ce08a0)) +* **deps:** Update dependency numpy to v2.1.3 ([#254](https://github.com/cloudquery/plugin-sdk-python/issues/254)) ([c0a87e6](https://github.com/cloudquery/plugin-sdk-python/commit/c0a87e627bc12802122304a104516ea89146a338)) +* **deps:** Update dependency packaging to v24.2 ([#258](https://github.com/cloudquery/plugin-sdk-python/issues/258)) ([d323a09](https://github.com/cloudquery/plugin-sdk-python/commit/d323a09194320d348ef384c23ec502b151499007)) +* **deps:** Update dependency protobuf to v5.29.0 ([#264](https://github.com/cloudquery/plugin-sdk-python/issues/264)) ([dbe63d7](https://github.com/cloudquery/plugin-sdk-python/commit/dbe63d74966ea5669ce2cdb21048a3cddf2ee44e)) +* **deps:** Update dependency pyarrow to v18.1.0 ([#265](https://github.com/cloudquery/plugin-sdk-python/issues/265)) ([cbcfd95](https://github.com/cloudquery/plugin-sdk-python/commit/cbcfd9599f4179de861330eea519087f47d65839)) +* **deps:** Update dependency pytest to v8.3.4 ([#263](https://github.com/cloudquery/plugin-sdk-python/issues/263)) ([b6882f0](https://github.com/cloudquery/plugin-sdk-python/commit/b6882f0eee74a47d267a83689122e67934074da5)) +* **deps:** Update dependency tomli to v2.1.0 ([#259](https://github.com/cloudquery/plugin-sdk-python/issues/259)) ([6651357](https://github.com/cloudquery/plugin-sdk-python/commit/6651357263d89ac7ac50bd054563c77eb196187e)) +* **deps:** Update dependency tomli to v2.2.1 ([#266](https://github.com/cloudquery/plugin-sdk-python/issues/266)) ([89c49c0](https://github.com/cloudquery/plugin-sdk-python/commit/89c49c04cc4c413fa5668466318b2c9ac0a68fb5)) + +## [0.1.37](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.36...v0.1.37) (2024-11-04) + + +### Bug Fixes + +* **deps:** Update dependency black to v24.10.0 ([#248](https://github.com/cloudquery/plugin-sdk-python/issues/248)) ([a5b63c6](https://github.com/cloudquery/plugin-sdk-python/commit/a5b63c614a795ef7f456bd3d22c1208e0ec75009)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.37 ([#253](https://github.com/cloudquery/plugin-sdk-python/issues/253)) ([040dc2b](https://github.com/cloudquery/plugin-sdk-python/commit/040dc2b92c17fdc0db671e08e61315393f2b5f0d)) +* **deps:** Update dependency grpcio to v1.67.1 ([#249](https://github.com/cloudquery/plugin-sdk-python/issues/249)) ([05dd532](https://github.com/cloudquery/plugin-sdk-python/commit/05dd5322bed7c35bf3006bb161f92ed0014b0316)) +* **deps:** Update dependency grpcio-tools to v1.67.1 ([#250](https://github.com/cloudquery/plugin-sdk-python/issues/250)) ([a9a0fe5](https://github.com/cloudquery/plugin-sdk-python/commit/a9a0fe560a314ab05466acc3b06de43becc77046)) +* **deps:** Update dependency MarkupSafe to v3 ([#252](https://github.com/cloudquery/plugin-sdk-python/issues/252)) ([d3dbd3f](https://github.com/cloudquery/plugin-sdk-python/commit/d3dbd3f67a3a5fab8ea069cc12cdf231d4df8e3e)) +* **deps:** Update dependency numpy to v2.1.2 ([#244](https://github.com/cloudquery/plugin-sdk-python/issues/244)) ([fc45d99](https://github.com/cloudquery/plugin-sdk-python/commit/fc45d99d0d4c1d667c45515cee774887e3bae2f6)) +* **deps:** Update dependency protobuf to v5.28.3 ([#245](https://github.com/cloudquery/plugin-sdk-python/issues/245)) ([192260e](https://github.com/cloudquery/plugin-sdk-python/commit/192260e68fc11946b19c2269c91f2dad5df63c53)) +* **deps:** Update dependency tomli to v2.0.2 ([#247](https://github.com/cloudquery/plugin-sdk-python/issues/247)) ([09fbc66](https://github.com/cloudquery/plugin-sdk-python/commit/09fbc6638b273cab07b156a7df306428d894fc4a)) +* **deps:** Update python Docker tag to v3.13 ([#251](https://github.com/cloudquery/plugin-sdk-python/issues/251)) ([0ad38dd](https://github.com/cloudquery/plugin-sdk-python/commit/0ad38dddbd74d7de73421bed2d1e91b89b43833a)) + +## [0.1.36](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.35...v0.1.36) (2024-10-01) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.35 ([#232](https://github.com/cloudquery/plugin-sdk-python/issues/232)) ([d01b653](https://github.com/cloudquery/plugin-sdk-python/commit/d01b653f17c3a608264b5196797448e3a3c65b41)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.36 ([#243](https://github.com/cloudquery/plugin-sdk-python/issues/243)) ([65d1dcb](https://github.com/cloudquery/plugin-sdk-python/commit/65d1dcbe1d06b38bfbfb4b6982bec5f043ad6409)) +* **deps:** Update dependency grpcio to v1.66.2 ([#235](https://github.com/cloudquery/plugin-sdk-python/issues/235)) ([94253fe](https://github.com/cloudquery/plugin-sdk-python/commit/94253fe110fe42f34249b520eb13f2b993eff41e)) +* **deps:** Update dependency grpcio-tools to v1.66.2 ([#236](https://github.com/cloudquery/plugin-sdk-python/issues/236)) ([549016e](https://github.com/cloudquery/plugin-sdk-python/commit/549016ee0541ad77e1090da2afc747db9431eb6b)) +* **deps:** Update dependency numpy to v2.1.1 ([#237](https://github.com/cloudquery/plugin-sdk-python/issues/237)) ([b9182a5](https://github.com/cloudquery/plugin-sdk-python/commit/b9182a5813ab5ab995d92d20e815cdc3cf0665dc)) +* **deps:** Update dependency pandas to v2.2.3 ([#238](https://github.com/cloudquery/plugin-sdk-python/issues/238)) ([3f24bb2](https://github.com/cloudquery/plugin-sdk-python/commit/3f24bb2d8c5d458d361dd9d7c7f378c046f78276)) +* **deps:** Update dependency protobuf to v5.28.2 ([#239](https://github.com/cloudquery/plugin-sdk-python/issues/239)) ([8727b23](https://github.com/cloudquery/plugin-sdk-python/commit/8727b230a92860441102a4f88b96dc198638aab7)) +* **deps:** Update dependency pytest to v8.3.3 ([#240](https://github.com/cloudquery/plugin-sdk-python/issues/240)) ([f5c3397](https://github.com/cloudquery/plugin-sdk-python/commit/f5c3397524fc76e155428e49770ac195f6bb0ac6)) +* **deps:** Update dependency pytz to v2024.2 ([#241](https://github.com/cloudquery/plugin-sdk-python/issues/241)) ([e3b2f37](https://github.com/cloudquery/plugin-sdk-python/commit/e3b2f3789837c74cdbb8a4f2618e6dc4c283a445)) +* **deps:** Update dependency tzdata to v2024.2 ([#242](https://github.com/cloudquery/plugin-sdk-python/issues/242)) ([655e5ba](https://github.com/cloudquery/plugin-sdk-python/commit/655e5ba1eb2db52e7f061393fb11222edba51df8)) + +## [0.1.35](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.34...v0.1.35) (2024-09-04) + + +### Bug Fixes + +* **deps:** Update dependency black to v24.8.0 ([#225](https://github.com/cloudquery/plugin-sdk-python/issues/225)) ([e380771](https://github.com/cloudquery/plugin-sdk-python/commit/e380771a3c57cea3596ec1ddfd570363c2e8aa3f)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.34 ([#231](https://github.com/cloudquery/plugin-sdk-python/issues/231)) ([ee5f3a9](https://github.com/cloudquery/plugin-sdk-python/commit/ee5f3a900b5f10458d1cbe4c9a9a833ea4f6f295)) +* **deps:** Update dependency grpcio to v1.66.1 ([#226](https://github.com/cloudquery/plugin-sdk-python/issues/226)) ([5f6bcac](https://github.com/cloudquery/plugin-sdk-python/commit/5f6bcace644ce2e7858223d2bb5bf674da28f406)) +* **deps:** Update dependency grpcio-tools to v1.66.1 ([#228](https://github.com/cloudquery/plugin-sdk-python/issues/228)) ([20a219c](https://github.com/cloudquery/plugin-sdk-python/commit/20a219c1dab2c6f03226ff98af871265d7824a0c)) +* **deps:** Update dependency numpy to v2.1.0 ([#229](https://github.com/cloudquery/plugin-sdk-python/issues/229)) ([23f58ed](https://github.com/cloudquery/plugin-sdk-python/commit/23f58ed81d408ede248760144aebcff3143513ae)) +* **deps:** Update dependency protobuf to v5.28.0 ([#230](https://github.com/cloudquery/plugin-sdk-python/issues/230)) ([dbefcd8](https://github.com/cloudquery/plugin-sdk-python/commit/dbefcd8119644bcbc4f637a008121115a7ade3fa)) + +## [0.1.34](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.33...v0.1.34) (2024-08-21) + + +### Features + +* Enable destination plugins to do reads (for backend) ([#223](https://github.com/cloudquery/plugin-sdk-python/issues/223)) ([829f552](https://github.com/cloudquery/plugin-sdk-python/commit/829f55217839c29128bf839ee556b1679ed21b59)) + +## [0.1.33](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.32...v0.1.33) (2024-08-14) + + +### Bug Fixes + +* dont error when field has no metadata. ([#221](https://github.com/cloudquery/plugin-sdk-python/issues/221)) ([aac99a9](https://github.com/cloudquery/plugin-sdk-python/commit/aac99a967d0e1a089794f5c53aee4cdd4f6d80cd)) + +## [0.1.32](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.31...v0.1.32) (2024-08-01) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.33 ([#220](https://github.com/cloudquery/plugin-sdk-python/issues/220)) ([33ad65f](https://github.com/cloudquery/plugin-sdk-python/commit/33ad65f9796138a1603dadb38820dc5ab4f25c90)) +* **deps:** Update dependency exceptiongroup to v1.2.2 ([#211](https://github.com/cloudquery/plugin-sdk-python/issues/211)) ([ea7f54b](https://github.com/cloudquery/plugin-sdk-python/commit/ea7f54b7f9576e078254e51d56d7cbd2c0e30b70)) +* **deps:** Update dependency grpcio to v1.65.2 ([#215](https://github.com/cloudquery/plugin-sdk-python/issues/215)) ([0936749](https://github.com/cloudquery/plugin-sdk-python/commit/0936749c906626e7b6c26675e60301002b00673c)) +* **deps:** Update dependency grpcio-tools to v1.65.2 ([#216](https://github.com/cloudquery/plugin-sdk-python/issues/216)) ([c4887ee](https://github.com/cloudquery/plugin-sdk-python/commit/c4887ee4d3ce9b44cbf05cbf4e0fb3717c47b898)) +* **deps:** Update dependency numpy to v2.0.1 ([#212](https://github.com/cloudquery/plugin-sdk-python/issues/212)) ([f460c5b](https://github.com/cloudquery/plugin-sdk-python/commit/f460c5b104a7ae0295c9d31daccdb91e9698936d)) +* **deps:** Update dependency protobuf to v5.27.3 ([#214](https://github.com/cloudquery/plugin-sdk-python/issues/214)) ([1f50a0c](https://github.com/cloudquery/plugin-sdk-python/commit/1f50a0c6c592b3d4bb8d9bfde075bfee23347def)) +* **deps:** Update dependency pyarrow to v17 ([#219](https://github.com/cloudquery/plugin-sdk-python/issues/219)) ([766bba9](https://github.com/cloudquery/plugin-sdk-python/commit/766bba98245f8aebd79c5e093fe9697a8acbbbf3)) +* **deps:** Update dependency pytest to v8.3.2 ([#217](https://github.com/cloudquery/plugin-sdk-python/issues/217)) ([5c1d110](https://github.com/cloudquery/plugin-sdk-python/commit/5c1d11079645ba1e83304ab506c20c9a502cccfd)) +* **deps:** Update dependency structlog to v24.4.0 ([#218](https://github.com/cloudquery/plugin-sdk-python/issues/218)) ([f320480](https://github.com/cloudquery/plugin-sdk-python/commit/f320480797fc1d4edac7df592616e29cd50a2aba)) + +## [0.1.31](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.30...v0.1.31) (2024-07-30) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.31 ([#208](https://github.com/cloudquery/plugin-sdk-python/issues/208)) ([f4f2f6d](https://github.com/cloudquery/plugin-sdk-python/commit/f4f2f6ddde89642cdd83033ed1acc98c2d83e113)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.32 ([#210](https://github.com/cloudquery/plugin-sdk-python/issues/210)) ([4ee3f53](https://github.com/cloudquery/plugin-sdk-python/commit/4ee3f53c0b62f67a5141c31674d65fc6ea6fa34d)) + +## [0.1.30](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.29...v0.1.30) (2024-07-17) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.30 ([#206](https://github.com/cloudquery/plugin-sdk-python/issues/206)) ([c7448c2](https://github.com/cloudquery/plugin-sdk-python/commit/c7448c2a163592978690edbd493554ae9faa8bf3)) + +## [0.1.29](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.28...v0.1.29) (2024-07-11) + + +### Bug Fixes + +* Relax python-dateutil version requirement ([#203](https://github.com/cloudquery/plugin-sdk-python/issues/203)) ([dec6cfe](https://github.com/cloudquery/plugin-sdk-python/commit/dec6cfe497d5c8912c8db8cc0365e4fe346e4ae2)) + +## [0.1.28](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.27...v0.1.28) (2024-07-08) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.29 ([#199](https://github.com/cloudquery/plugin-sdk-python/issues/199)) ([ed47946](https://github.com/cloudquery/plugin-sdk-python/commit/ed47946b6b658a6ca3a196b26cd22471cad4c00b)) +* **deps:** Update dependency pyarrow to v16 ([#200](https://github.com/cloudquery/plugin-sdk-python/issues/200)) ([8c625c3](https://github.com/cloudquery/plugin-sdk-python/commit/8c625c36bbd04a4b828e49b9416547eb0a1c4783)) +* **deps:** Update dependency structlog to v24 ([#201](https://github.com/cloudquery/plugin-sdk-python/issues/201)) ([d964cf1](https://github.com/cloudquery/plugin-sdk-python/commit/d964cf131aad22e155e037cc7411ac399c986ba3)) + +## [0.1.27](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.26...v0.1.27) (2024-07-08) + + +### Features + +* State Client ([#198](https://github.com/cloudquery/plugin-sdk-python/issues/198)) ([3193879](https://github.com/cloudquery/plugin-sdk-python/commit/3193879e57fe84d5cdbffeddf25bb98cea5bef10)) + + +### Bug Fixes + +* **deps:** Update dependency grpcio to v1.64.1 ([#189](https://github.com/cloudquery/plugin-sdk-python/issues/189)) ([9cafffd](https://github.com/cloudquery/plugin-sdk-python/commit/9cafffdf22aa261a52f55d2148f008ce8d20ec5f)) +* **deps:** Update dependency grpcio-tools to v1.64.1 ([#190](https://github.com/cloudquery/plugin-sdk-python/issues/190)) ([f7eea8d](https://github.com/cloudquery/plugin-sdk-python/commit/f7eea8d30a72e8f4226f75f4d0caef9be8bb36ae)) +* **deps:** Update dependency packaging to v24.1 ([#194](https://github.com/cloudquery/plugin-sdk-python/issues/194)) ([73d18ca](https://github.com/cloudquery/plugin-sdk-python/commit/73d18ca673a1d86de622181fb21c10ed142c9b58)) +* **deps:** Update dependency protobuf to v5.27.2 ([#192](https://github.com/cloudquery/plugin-sdk-python/issues/192)) ([b2e8673](https://github.com/cloudquery/plugin-sdk-python/commit/b2e8673bf4e212ca0983ef0fb1e0849859a466b5)) +* **deps:** Update dependency pytest to v8.2.2 ([#193](https://github.com/cloudquery/plugin-sdk-python/issues/193)) ([e3b240e](https://github.com/cloudquery/plugin-sdk-python/commit/e3b240e594a046b65217b24cb34d53dcb2f9ac3d)) +* **deps:** Update dependency python-dateutil to v2.9.0.post0 ([#195](https://github.com/cloudquery/plugin-sdk-python/issues/195)) ([a2835bb](https://github.com/cloudquery/plugin-sdk-python/commit/a2835bb8b0e3d810b21060d1f46a7873d1a3ea09)) +* **deps:** Update dependency tzdata to v2024 ([#197](https://github.com/cloudquery/plugin-sdk-python/issues/197)) ([a6c7a10](https://github.com/cloudquery/plugin-sdk-python/commit/a6c7a1045e5e703e4ae5200a1c1b06f68f1a11c9)) + +## [0.1.26](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.25...v0.1.26) (2024-06-03) + + +### Bug Fixes + +* Revert "fix(deps): Update dependency python-dateutil to v2.9.0.post0 ([#182](https://github.com/cloudquery/plugin-sdk-python/issues/182))" ([#186](https://github.com/cloudquery/plugin-sdk-python/issues/186)) ([2d45018](https://github.com/cloudquery/plugin-sdk-python/commit/2d450180c7843535a85ec6864893a23ac347876a)) + +## [0.1.25](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.24...v0.1.25) (2024-06-02) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.28 ([#185](https://github.com/cloudquery/plugin-sdk-python/issues/185)) ([34f26cf](https://github.com/cloudquery/plugin-sdk-python/commit/34f26cf0ee763f5225a60c9383e3256bf61f889e)) +* **deps:** Update dependency grpcio to v1.64.0 ([#179](https://github.com/cloudquery/plugin-sdk-python/issues/179)) ([82b93ab](https://github.com/cloudquery/plugin-sdk-python/commit/82b93ab0291fe71ddd6b8facaebd444b05c4f9c1)) +* **deps:** Update dependency grpcio-tools to v1.64.0 ([#180](https://github.com/cloudquery/plugin-sdk-python/issues/180)) ([0bca972](https://github.com/cloudquery/plugin-sdk-python/commit/0bca972cfdd9e395981b9228651e7740f27a6dad)) +* **deps:** Update dependency Jinja2 to v3.1.4 ([#176](https://github.com/cloudquery/plugin-sdk-python/issues/176)) ([7717899](https://github.com/cloudquery/plugin-sdk-python/commit/7717899758987c90411350364b08628ccb1f3d70)) +* **deps:** Update dependency protobuf to v5.27.0 ([#181](https://github.com/cloudquery/plugin-sdk-python/issues/181)) ([14fc183](https://github.com/cloudquery/plugin-sdk-python/commit/14fc18338f7cfeb3cb825c837167384e4e059623)) +* **deps:** Update dependency pytest to v8.2.1 ([#177](https://github.com/cloudquery/plugin-sdk-python/issues/177)) ([7a9d626](https://github.com/cloudquery/plugin-sdk-python/commit/7a9d6264dd6cba538701d026930b057699f9aa49)) +* **deps:** Update dependency python-dateutil to v2.9.0.post0 ([#182](https://github.com/cloudquery/plugin-sdk-python/issues/182)) ([b03fe3d](https://github.com/cloudquery/plugin-sdk-python/commit/b03fe3d832be634bae7756cc4ca61dd90249b3e4)) + +## [0.1.24](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.23...v0.1.24) (2024-05-24) + + +### Bug Fixes + +* Set spec to `b"{}"` on empty input ([#174](https://github.com/cloudquery/plugin-sdk-python/issues/174)) ([3f46922](https://github.com/cloudquery/plugin-sdk-python/commit/3f469227003f0df101df36724bfc6019411b9bf0)) + +## [0.1.23](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.22...v0.1.23) (2024-05-17) + + +### Features + +* Log resource when exception occurs to aid debugging ([#172](https://github.com/cloudquery/plugin-sdk-python/issues/172)) ([6403290](https://github.com/cloudquery/plugin-sdk-python/commit/6403290dd15da5190e85930f2738a7d27da4c3db)) + +## [0.1.22](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.21...v0.1.22) (2024-05-07) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.27 ([#169](https://github.com/cloudquery/plugin-sdk-python/issues/169)) ([cf4668d](https://github.com/cloudquery/plugin-sdk-python/commit/cf4668dc7916ba8497f06faaeb2cdfbebc849db1)) +* Revert "fix(deps): Update dependency python-dateutil to v2.9.0.post0 ([#167](https://github.com/cloudquery/plugin-sdk-python/issues/167))" ([#171](https://github.com/cloudquery/plugin-sdk-python/issues/171)) ([6d471e8](https://github.com/cloudquery/plugin-sdk-python/commit/6d471e8952322e2e323feab5cd78861caf5c31eb)) + +## [0.1.21](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.20...v0.1.21) (2024-05-06) + + +### Bug Fixes + +* **deps:** Update dependency black to v24.4.2 ([#162](https://github.com/cloudquery/plugin-sdk-python/issues/162)) ([0b5b482](https://github.com/cloudquery/plugin-sdk-python/commit/0b5b482c22c8a7bf352597a25606c21538dfa0c2)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.26 ([#168](https://github.com/cloudquery/plugin-sdk-python/issues/168)) ([07c0435](https://github.com/cloudquery/plugin-sdk-python/commit/07c043597d3c2afb018dc1a08b16ba8e90be0ff8)) +* **deps:** Update dependency exceptiongroup to v1.2.1 ([#159](https://github.com/cloudquery/plugin-sdk-python/issues/159)) ([21c5de7](https://github.com/cloudquery/plugin-sdk-python/commit/21c5de734617f13b747ef8e4ae8bfb94c5b0fe25)) +* **deps:** Update dependency grpcio to v1.63.0 ([#163](https://github.com/cloudquery/plugin-sdk-python/issues/163)) ([531c4b8](https://github.com/cloudquery/plugin-sdk-python/commit/531c4b884a9d34da872518dcbb036418240fab03)) +* **deps:** Update dependency grpcio-tools to v1.63.0 ([#164](https://github.com/cloudquery/plugin-sdk-python/issues/164)) ([4ceb7ae](https://github.com/cloudquery/plugin-sdk-python/commit/4ceb7ae30861d538ef835a62861ed8ad91a97da7)) +* **deps:** Update dependency pandas to v2.2.2 ([#160](https://github.com/cloudquery/plugin-sdk-python/issues/160)) ([0cc0fc0](https://github.com/cloudquery/plugin-sdk-python/commit/0cc0fc02a0c9616deb113ad3ac2e39d80b94d13a)) +* **deps:** Update dependency pluggy to v1.5.0 ([#165](https://github.com/cloudquery/plugin-sdk-python/issues/165)) ([c9c3b93](https://github.com/cloudquery/plugin-sdk-python/commit/c9c3b933583fb3992970cc46ffa42c2cb89dcedd)) +* **deps:** Update dependency pytest to v8.2.0 ([#166](https://github.com/cloudquery/plugin-sdk-python/issues/166)) ([85c29c0](https://github.com/cloudquery/plugin-sdk-python/commit/85c29c060e49d4f6fc1de2b3a0391a82a5a52970)) +* **deps:** Update dependency python-dateutil to v2.9.0.post0 ([#167](https://github.com/cloudquery/plugin-sdk-python/issues/167)) ([fbf5de1](https://github.com/cloudquery/plugin-sdk-python/commit/fbf5de12b0a86774053ac5529a569428d282ed65)) + +## [0.1.20](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.19...v0.1.20) (2024-04-01) + + +### Bug Fixes + +* Revert https://github.com/cloudquery/plugin-sdk-python/pull/151 ([#157](https://github.com/cloudquery/plugin-sdk-python/issues/157)) ([e7d4547](https://github.com/cloudquery/plugin-sdk-python/commit/e7d45473f5821644e9d3557490860635883f8e73)) + +## [0.1.19](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.18...v0.1.19) (2024-04-01) + + +### Bug Fixes + +* **deps:** Update dependency packaging to v24 ([#152](https://github.com/cloudquery/plugin-sdk-python/issues/152)) ([484cd03](https://github.com/cloudquery/plugin-sdk-python/commit/484cd03b1d22e0b2321c49ea3cfd09e0b196d9bd)) +* **deps:** Update dependency pytz to v2024 ([#154](https://github.com/cloudquery/plugin-sdk-python/issues/154)) ([17081b1](https://github.com/cloudquery/plugin-sdk-python/commit/17081b157a9af4c5c9d3f3297ea08007a9f3300b)) + +## [0.1.18](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.17...v0.1.18) (2024-04-01) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.25 ([#155](https://github.com/cloudquery/plugin-sdk-python/issues/155)) ([7d9a39c](https://github.com/cloudquery/plugin-sdk-python/commit/7d9a39c2937c70a641a6321dddf5a3a6cf5e09b8)) +* **deps:** Update dependency grpcio to v1.62.1 ([#146](https://github.com/cloudquery/plugin-sdk-python/issues/146)) ([d0a14ac](https://github.com/cloudquery/plugin-sdk-python/commit/d0a14ac3131c04eb1ca37dabe7bf912fcd348237)) +* **deps:** Update dependency grpcio-tools to v1.62.1 ([#147](https://github.com/cloudquery/plugin-sdk-python/issues/147)) ([15e58eb](https://github.com/cloudquery/plugin-sdk-python/commit/15e58eb0f1d565e93c1d932b65424b3ff51c1f85)) +* **deps:** Update dependency pyarrow to v15.0.2 ([#149](https://github.com/cloudquery/plugin-sdk-python/issues/149)) ([c37fd64](https://github.com/cloudquery/plugin-sdk-python/commit/c37fd6457dd9afe28939f57c53a28f4958a14df5)) +* **deps:** Update dependency pytest to v8.1.1 ([#150](https://github.com/cloudquery/plugin-sdk-python/issues/150)) ([986eced](https://github.com/cloudquery/plugin-sdk-python/commit/986ecedf4382e85e8befe7fc4007f6c78c6117f5)) +* **deps:** Update dependency python-dateutil to v2.9.0.post0 ([#151](https://github.com/cloudquery/plugin-sdk-python/issues/151)) ([b4c83e9](https://github.com/cloudquery/plugin-sdk-python/commit/b4c83e9ba4140a82b8805266c22d3d6d83ac83a3)) + +## [0.1.17](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.16...v0.1.17) (2024-03-20) + + +### Bug Fixes + +* **deps:** Update dependency black to v24.3.0 ([#144](https://github.com/cloudquery/plugin-sdk-python/issues/144)) ([6dd4cd1](https://github.com/cloudquery/plugin-sdk-python/commit/6dd4cd1ee912a4721b804ec9d327413678c3d23a)) + +## [0.1.16](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.15...v0.1.16) (2024-03-11) + + +### Bug Fixes + +* Exception Handling ([#141](https://github.com/cloudquery/plugin-sdk-python/issues/141)) ([afba5db](https://github.com/cloudquery/plugin-sdk-python/commit/afba5db9a8c536983c99e3b43da4a818cdbd8ff2)) + +## [0.1.15](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.14...v0.1.15) (2024-03-01) + + +### Bug Fixes + +* **deps:** Update dependency black to v24.2.0 ([#136](https://github.com/cloudquery/plugin-sdk-python/issues/136)) ([a8cd1ae](https://github.com/cloudquery/plugin-sdk-python/commit/a8cd1ae1bac7949542adae8d80bef45541725ec3)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.24 ([#140](https://github.com/cloudquery/plugin-sdk-python/issues/140)) ([cbd8d2e](https://github.com/cloudquery/plugin-sdk-python/commit/cbd8d2ef4567f1c8b426c5aee922b88a142a5f8f)) +* **deps:** Update dependency grpcio to v1.62.0 ([#137](https://github.com/cloudquery/plugin-sdk-python/issues/137)) ([0004bcf](https://github.com/cloudquery/plugin-sdk-python/commit/0004bcf94e40db4587b330125609db9f7849cf75)) +* **deps:** Update dependency grpcio-tools to v1.62.0 ([#138](https://github.com/cloudquery/plugin-sdk-python/issues/138)) ([0f9a10d](https://github.com/cloudquery/plugin-sdk-python/commit/0f9a10dab6bcb939c698c374f68af931606d618d)) +* **deps:** Update dependency MarkupSafe to v2.1.5 ([#131](https://github.com/cloudquery/plugin-sdk-python/issues/131)) ([289a7bd](https://github.com/cloudquery/plugin-sdk-python/commit/289a7bddf664af292ae048c72e42e595b9f45c59)) +* **deps:** Update dependency numpy to v1.26.4 ([#132](https://github.com/cloudquery/plugin-sdk-python/issues/132)) ([ce27605](https://github.com/cloudquery/plugin-sdk-python/commit/ce276058895488c1b3a81fef89115479c95ea530)) +* **deps:** Update dependency pandas to v2.2.1 ([#134](https://github.com/cloudquery/plugin-sdk-python/issues/134)) ([b973bf8](https://github.com/cloudquery/plugin-sdk-python/commit/b973bf8ba16ca8a9bab6c5eea4b56aa2158d6c76)) +* **deps:** Update dependency protobuf to v4.25.3 ([#135](https://github.com/cloudquery/plugin-sdk-python/issues/135)) ([8e6c12e](https://github.com/cloudquery/plugin-sdk-python/commit/8e6c12eafa9464a18cf85b27ceed6bbda5f6ca64)) + +## [0.1.14](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.13...v0.1.14) (2024-02-12) + + +### Features + +* Implement `GetSpecSchema` plugin gRPC call ([#130](https://github.com/cloudquery/plugin-sdk-python/issues/130)) ([35c981c](https://github.com/cloudquery/plugin-sdk-python/commit/35c981c6c57247d210b4e8513cbb45caf6659177)) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.23 ([#125](https://github.com/cloudquery/plugin-sdk-python/issues/125)) ([5d1a1d7](https://github.com/cloudquery/plugin-sdk-python/commit/5d1a1d7d98ec88607695a6454085317f32df6abd)) +* **deps:** Update dependency pyarrow to v15 ([#126](https://github.com/cloudquery/plugin-sdk-python/issues/126)) ([541e9ca](https://github.com/cloudquery/plugin-sdk-python/commit/541e9ca6748d327a9a4f5117ee49695e0f8bb464)) + +## [0.1.13](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.12...v0.1.13) (2024-02-01) + + +### Bug Fixes + +* **deps:** Update dependency black to v24 ([#123](https://github.com/cloudquery/plugin-sdk-python/issues/123)) ([e1b23d1](https://github.com/cloudquery/plugin-sdk-python/commit/e1b23d1eded0b79c96c1efafb6a375b298bbb804)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.22 ([#124](https://github.com/cloudquery/plugin-sdk-python/issues/124)) ([4f9be1b](https://github.com/cloudquery/plugin-sdk-python/commit/4f9be1b312994aa5616a22393f04d1b3a2270442)) +* **deps:** Update dependency Jinja2 to v3.1.3 ([#115](https://github.com/cloudquery/plugin-sdk-python/issues/115)) ([76cfae9](https://github.com/cloudquery/plugin-sdk-python/commit/76cfae9aafbacbe42b0dec9b6d9fd5cc043e9165)) +* **deps:** Update dependency MarkupSafe to v2.1.4 ([#116](https://github.com/cloudquery/plugin-sdk-python/issues/116)) ([2d6eb79](https://github.com/cloudquery/plugin-sdk-python/commit/2d6eb795f401f81b1706fa9c9cddc834acf1dbc3)) +* **deps:** Update dependency numpy to v1.26.3 ([#118](https://github.com/cloudquery/plugin-sdk-python/issues/118)) ([8bc822f](https://github.com/cloudquery/plugin-sdk-python/commit/8bc822f9b16f27126d1ec32d2f15b9b17bb78afb)) +* **deps:** Update dependency pandas to v2.2.0 ([#120](https://github.com/cloudquery/plugin-sdk-python/issues/120)) ([b6c82e9](https://github.com/cloudquery/plugin-sdk-python/commit/b6c82e98ad6a30578b39eba113dcfb1de77b3319)) +* **deps:** Update dependency pluggy to v1.4.0 ([#121](https://github.com/cloudquery/plugin-sdk-python/issues/121)) ([50d44e6](https://github.com/cloudquery/plugin-sdk-python/commit/50d44e690d37e5f29789d4d410c3d54493e0b9db)) +* **deps:** Update dependency protobuf to v4.25.2 ([#119](https://github.com/cloudquery/plugin-sdk-python/issues/119)) ([a907a5d](https://github.com/cloudquery/plugin-sdk-python/commit/a907a5d55ba57a9159e928509aa671eb7c7b4b2c)) +* **deps:** Update dependency pytz to v2023.4 ([#122](https://github.com/cloudquery/plugin-sdk-python/issues/122)) ([8eed670](https://github.com/cloudquery/plugin-sdk-python/commit/8eed6702fdba75b3d1522924f0c6cb41a1d4cf48)) + +## [0.1.12](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.11...v0.1.12) (2024-01-03) + + +### Bug Fixes + +* Rename docker registry host ([#113](https://github.com/cloudquery/plugin-sdk-python/issues/113)) ([b783921](https://github.com/cloudquery/plugin-sdk-python/commit/b783921b1439252797b55dcc2f16a26cb65d7f7b)) + +## [0.1.11](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.10...v0.1.11) (2024-01-02) + + +### Bug Fixes + +* **deps:** Update dependency black to v23.12.1 ([#103](https://github.com/cloudquery/plugin-sdk-python/issues/103)) ([abfd215](https://github.com/cloudquery/plugin-sdk-python/commit/abfd215f2be7fb63154d0cd7114ae9fbb19eaa73)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.21 ([#111](https://github.com/cloudquery/plugin-sdk-python/issues/111)) ([94eadd1](https://github.com/cloudquery/plugin-sdk-python/commit/94eadd1041d036518812db4202022104a4200169)) +* **deps:** Update dependency pyarrow to v14.0.2 ([#104](https://github.com/cloudquery/plugin-sdk-python/issues/104)) ([3e16d15](https://github.com/cloudquery/plugin-sdk-python/commit/3e16d15f9b959b83ae12640d207bfa98f3c3a815)) +* **deps:** Update dependency pytest to v7.4.4 ([#106](https://github.com/cloudquery/plugin-sdk-python/issues/106)) ([230e6fd](https://github.com/cloudquery/plugin-sdk-python/commit/230e6fd72c122e2579886aa09f148535d486105d)) +* **deps:** Update dependency structlog to v23.3.0 ([#107](https://github.com/cloudquery/plugin-sdk-python/issues/107)) ([b210f6b](https://github.com/cloudquery/plugin-sdk-python/commit/b210f6b7d0a3c34746fbca236dbd449e26dccfb6)) +* **deps:** Update dependency tzdata to v2023.4 ([#108](https://github.com/cloudquery/plugin-sdk-python/issues/108)) ([870fd60](https://github.com/cloudquery/plugin-sdk-python/commit/870fd606a51fdc5ceb7a9b516a448e95bf5a56b2)) +* **deps:** Update python Docker tag to v3.12 ([#109](https://github.com/cloudquery/plugin-sdk-python/issues/109)) ([79f40a1](https://github.com/cloudquery/plugin-sdk-python/commit/79f40a1bc00df9c20e3635897361b990dbc9b0bb)) + +## [0.1.10](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.9...v0.1.10) (2023-12-29) + + +### Bug Fixes + +* Add --license placeholder for future use ([#101](https://github.com/cloudquery/plugin-sdk-python/issues/101)) ([22d4c60](https://github.com/cloudquery/plugin-sdk-python/commit/22d4c605a97470d90ccc36f1789968b4ed7e2ec4)) + +## [0.1.9](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.8...v0.1.9) (2023-12-18) + + +### Features + +* Add package command ([#84](https://github.com/cloudquery/plugin-sdk-python/issues/84)) ([69bd71d](https://github.com/cloudquery/plugin-sdk-python/commit/69bd71df90d038ace8bcb1352312604a22f61712)) + + +### Bug Fixes + +* **deps:** Update dependency black to v23.11.0 ([#90](https://github.com/cloudquery/plugin-sdk-python/issues/90)) ([a9bb395](https://github.com/cloudquery/plugin-sdk-python/commit/a9bb395a786211589d66acfc63363180b09daba3)) +* **deps:** Update dependency black to v23.12.0 ([#95](https://github.com/cloudquery/plugin-sdk-python/issues/95)) ([1fa9e2c](https://github.com/cloudquery/plugin-sdk-python/commit/1fa9e2c8eeb1bc00479b771a7c7df64d919e225e)) +* **deps:** Update dependency exceptiongroup to v1.2.0 ([#91](https://github.com/cloudquery/plugin-sdk-python/issues/91)) ([2a59001](https://github.com/cloudquery/plugin-sdk-python/commit/2a59001197e78433373ea41e8bbf32246f64ad74)) +* **deps:** Update dependency grpcio to v1.59.3 ([#85](https://github.com/cloudquery/plugin-sdk-python/issues/85)) ([4512f59](https://github.com/cloudquery/plugin-sdk-python/commit/4512f597da73ea2ef1810745db6da9f82cc4c799)) +* **deps:** Update dependency grpcio to v1.60.0 ([#96](https://github.com/cloudquery/plugin-sdk-python/issues/96)) ([4592007](https://github.com/cloudquery/plugin-sdk-python/commit/459200783b553b6826609ecee139f8316231a163)) +* **deps:** Update dependency grpcio-tools to v1.59.3 ([#86](https://github.com/cloudquery/plugin-sdk-python/issues/86)) ([e8eeb7c](https://github.com/cloudquery/plugin-sdk-python/commit/e8eeb7cf1acba4136a33bb6ea2dd434f9780cf13)) +* **deps:** Update dependency grpcio-tools to v1.60.0 ([#97](https://github.com/cloudquery/plugin-sdk-python/issues/97)) ([2ffd88b](https://github.com/cloudquery/plugin-sdk-python/commit/2ffd88bff03cb9f91af076f09fd139679de9f890)) +* **deps:** Update dependency numpy to v1.26.2 ([#88](https://github.com/cloudquery/plugin-sdk-python/issues/88)) ([5f8f469](https://github.com/cloudquery/plugin-sdk-python/commit/5f8f4694306dea4bb305a341fcbac8803b0975d1)) +* **deps:** Update dependency pandas to v2.1.3 ([#89](https://github.com/cloudquery/plugin-sdk-python/issues/89)) ([1a7eaf6](https://github.com/cloudquery/plugin-sdk-python/commit/1a7eaf60f7073e7fdd5213368752d433d6c4c252)) +* **deps:** Update dependency pandas to v2.1.4 ([#94](https://github.com/cloudquery/plugin-sdk-python/issues/94)) ([f90efc3](https://github.com/cloudquery/plugin-sdk-python/commit/f90efc35441755fb44314d62523fd9b0e049e29f)) +* **deps:** Update dependency protobuf to v4.25.1 ([#92](https://github.com/cloudquery/plugin-sdk-python/issues/92)) ([cb9105e](https://github.com/cloudquery/plugin-sdk-python/commit/cb9105e1a36373a7c68c06ef0e43063afe537369)) +* **deps:** Update dependency structlog to v23.2.0 ([#93](https://github.com/cloudquery/plugin-sdk-python/issues/93)) ([350f2d6](https://github.com/cloudquery/plugin-sdk-python/commit/350f2d698fea76c3a0d1860339fcaa561f038cda)) + +## [0.1.8](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.7...v0.1.8) (2023-11-10) + + +### Bug Fixes + +* **deps:** Update `pyarrow` to `v14.0.1` [SECURITY] ([#83](https://github.com/cloudquery/plugin-sdk-python/issues/83)) ([36f52e3](https://github.com/cloudquery/plugin-sdk-python/commit/36f52e376f3bdbd3f201e94d6ebe067a975d6fd5)) +* **deps:** Update dependency black to v23.10.1 ([#80](https://github.com/cloudquery/plugin-sdk-python/issues/80)) ([e096751](https://github.com/cloudquery/plugin-sdk-python/commit/e096751e4053a8a3183ab8d3b6cc38a93c196308)) +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.20 ([#82](https://github.com/cloudquery/plugin-sdk-python/issues/82)) ([ea4b0b0](https://github.com/cloudquery/plugin-sdk-python/commit/ea4b0b00ec6af6b13f241121584b3d40568277e9)) +* **deps:** Update dependency grpcio to v1.59.2 ([#73](https://github.com/cloudquery/plugin-sdk-python/issues/73)) ([d0775b0](https://github.com/cloudquery/plugin-sdk-python/commit/d0775b0e7fc82b4b31909e0cef35731de771eb16)) +* **deps:** Update dependency grpcio-tools to v1.59.2 ([#74](https://github.com/cloudquery/plugin-sdk-python/issues/74)) ([8626aa2](https://github.com/cloudquery/plugin-sdk-python/commit/8626aa2c2c6d828aab33182cbdcc0ddd37cb0370)) +* **deps:** Update dependency numpy to v1.26.1 ([#76](https://github.com/cloudquery/plugin-sdk-python/issues/76)) ([3a3a2be](https://github.com/cloudquery/plugin-sdk-python/commit/3a3a2be441aa07762979399a3b463275092b697b)) +* **deps:** Update dependency packaging to v23.2 ([#81](https://github.com/cloudquery/plugin-sdk-python/issues/81)) ([956db38](https://github.com/cloudquery/plugin-sdk-python/commit/956db38fab9cd1f43f7d27dbb887f8d58d1ee384)) +* **deps:** Update dependency pandas to v2.1.2 ([#77](https://github.com/cloudquery/plugin-sdk-python/issues/77)) ([1e3d7da](https://github.com/cloudquery/plugin-sdk-python/commit/1e3d7da7128c3f798065df12ed0370cb78d3aa8e)) +* **deps:** Update dependency protobuf to v4.24.4 ([#78](https://github.com/cloudquery/plugin-sdk-python/issues/78)) ([8c68799](https://github.com/cloudquery/plugin-sdk-python/commit/8c68799125c0bb6ebc3ddb14194b6286970d802c)) +* **deps:** Update dependency pytest to v7.4.3 ([#79](https://github.com/cloudquery/plugin-sdk-python/issues/79)) ([2bda782](https://github.com/cloudquery/plugin-sdk-python/commit/2bda7828fedcb7ce9e6da8811c99b649f2f38f7c)) + +## [0.1.7](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.6...v0.1.7) (2023-10-23) + + +### Bug Fixes + +* **deps:** Update dependency pyarrow to v13 ([#53](https://github.com/cloudquery/plugin-sdk-python/issues/53)) ([55bed05](https://github.com/cloudquery/plugin-sdk-python/commit/55bed05df36c75dc9972b445e4bb21ef0321329c)) +* Properly handle `uuid.UUID` values in scalars ([#70](https://github.com/cloudquery/plugin-sdk-python/issues/70)), closes (https://github.com/cloudquery/cloudquery/issues/14825) ([1fc0cf7](https://github.com/cloudquery/plugin-sdk-python/commit/1fc0cf7535138d5b8c4e00b39d6863ce0f6bf51a)) + +## [0.1.6](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.5...v0.1.6) (2023-10-12) + + +### Bug Fixes + +* **deps:** Update dependency cloudquery-plugin-pb to v0.0.18 ([#68](https://github.com/cloudquery/plugin-sdk-python/issues/68)) ([7d4d8fb](https://github.com/cloudquery/plugin-sdk-python/commit/7d4d8fb6e802de67e53870a4b38547e0fc9821b9)) + ## [0.1.5](https://github.com/cloudquery/plugin-sdk-python/compare/v0.1.4...v0.1.5) (2023-10-09) diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..f9c1c9b --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,6 @@ +* @cloudquery/backend + +requirements.txt +setup.py +.release-please-manifest.json +CHANGELOG.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4257ad4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.13-slim + +WORKDIR /app + +# Copy the code and install dependencies +COPY requirements.txt . +COPY setup.cfg . +COPY setup.py . +COPY cloudquery cloudquery +COPY main.py . +RUN pip3 install --no-cache-dir -r requirements.txt + +EXPOSE 7777 + +ENTRYPOINT ["python3", "main.py"] + +CMD ["serve", "--address", "[::]:7777", "--log-format", "json", "--log-level", "info"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..82b4de9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,353 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. diff --git a/cloudquery/sdk/internal/memdb/memdb.py b/cloudquery/sdk/internal/memdb/memdb.py index 4f422f5..dd7bd5d 100644 --- a/cloudquery/sdk/internal/memdb/memdb.py +++ b/cloudquery/sdk/internal/memdb/memdb.py @@ -1,18 +1,92 @@ +import json + from cloudquery.sdk import plugin from cloudquery.sdk import message from cloudquery.sdk import schema from typing import List, Generator, Dict import pyarrow as pa +from cloudquery.sdk.schema.table import Table +from cloudquery.sdk.types import JSONType +from dataclasses import dataclass, field NAME = "memdb" VERSION = "development" +@dataclass +class Spec: + abc: str = field(default="abc") + + class MemDB(plugin.Plugin): def __init__(self) -> None: - super().__init__(NAME, VERSION) + super().__init__( + NAME, VERSION, opts=plugin.plugin.Options(team="cloudquery", kind="source") + ) self._db: Dict[str, pa.RecordBatch] = {} - self._tables: Dict[str, schema.Table] = {} + self._tables: Dict[str, schema.Table] = { + "table_1": schema.Table( + name="table_1", + columns=[ + schema.Column( + name="name", + type=pa.string(), + primary_key=True, + not_null=True, + unique=True, + ), + schema.Column( + name="id", + type=pa.string(), + primary_key=True, + not_null=True, + unique=True, + incremental_key=True, + ), + ], + title="Table 1", + description="Test Table 1", + is_incremental=True, + relations=[ + schema.Table( + name="table_1_relation_1", + columns=[ + schema.Column( + name="name", + type=pa.string(), + primary_key=True, + not_null=True, + unique=True, + ), + schema.Column(name="data", type=JSONType()), + ], + title="Table 1 Relation 1", + description="Test Table 1 Relation 1", + ) + ], + ), + "table_2": schema.Table( + name="table_2", + columns=[ + schema.Column( + name="name", + type=pa.string(), + primary_key=True, + not_null=True, + unique=True, + ), + schema.Column(name="id", type=pa.string()), + ], + title="Table 2", + description="Test Table 2", + ), + } + + def init(self, spec, no_connection: bool = False): + if no_connection: + return + self._spec_json = json.loads(spec) + self._spec = Spec(**self._spec_json) def get_tables(self, options: plugin.TableOptions = None) -> List[plugin.Table]: tables = list(self._tables.values()) @@ -36,5 +110,9 @@ def write(self, writer: Generator[message.WriteMessage, None, None]) -> None: else: raise NotImplementedError(f"Unknown message type {type(msg)}") + def read(self, table: Table) -> Generator[message.ReadMessage, None, None]: + for table, record in self._db.items(): + yield message.ReadMessage(record) + def close(self) -> None: self._db = {} diff --git a/cloudquery/sdk/internal/servers/plugin_v3/plugin.py b/cloudquery/sdk/internal/servers/plugin_v3/plugin.py index 8bc561f..dee7ff0 100644 --- a/cloudquery/sdk/internal/servers/plugin_v3/plugin.py +++ b/cloudquery/sdk/internal/servers/plugin_v3/plugin.py @@ -28,8 +28,13 @@ def GetName(self, request, context): def GetVersion(self, request, context): return plugin_pb2.GetVersion.Response(version=self._plugin.version()) + def GetSpecSchema(self, request, context): + return plugin_pb2.GetSpecSchema.Response(json_schema=self._plugin.json_schema()) + def Init(self, request: plugin_pb2.Init.Request, context): - self._plugin.init(request.spec, no_connection=request.no_connection) + self._plugin.init( + sanitize_spec(request.spec), no_connection=request.no_connection + ) return plugin_pb2.Init.Response() def GetTables(self, request: plugin_pb2.GetTables.Request, context): @@ -58,7 +63,7 @@ def Sync(self, request, context): skip_dependent_tables=request.skip_dependent_tables, skip_tables=request.skip_tables, tables=request.tables, - backend_options=None, + backend_options=request.backend, ) for msg in self._plugin.sync(options): @@ -76,8 +81,14 @@ def Sync(self, request, context): # unknown sync message type raise NotImplementedError() - def Read(self, request, context): - raise NotImplementedError() + def Read( + self, request: plugin_pb2.Read.Request, context + ) -> Generator[plugin_pb2.Read.Response, None, None]: + schema = arrow.new_schema_from_bytes(request.table) + table = Table.from_arrow_schema(schema) + for msg in self._plugin.read(table): + buf = arrow.record_to_bytes(msg.record) + yield plugin_pb2.Read.Response(record=buf) def Write( self, request_iterator: Generator[plugin_pb2.Write.Request, None, None], context @@ -88,7 +99,9 @@ def msg_iterator() -> Generator[WriteMessage, None, None]: if field == "migrate_table": sc = arrow.new_schema_from_bytes(msg.migrate_table.table) table = Table.from_arrow_schema(sc) - yield WriteMigrateTableMessage(table=table) + yield WriteMigrateTableMessage( + table=table, migrate_force=msg.migrate_table.migrate_force + ) elif field == "insert": yield WriteInsertMessage( record=arrow.new_record_from_bytes(msg.insert.record) @@ -110,3 +123,7 @@ def msg_iterator() -> Generator[WriteMessage, None, None]: def Close(self, request, context): self._plugin.close() return plugin_pb2.Close.Response() + + +def sanitize_spec(spec=None): + return b"{}" if spec is None or spec == b"" or spec == b"null" else spec diff --git a/cloudquery/sdk/message/__init__.py b/cloudquery/sdk/message/__init__.py index 5ddb77a..bbcb84f 100644 --- a/cloudquery/sdk/message/__init__.py +++ b/cloudquery/sdk/message/__init__.py @@ -5,3 +5,4 @@ WriteMigrateTableMessage, WriteDeleteStale, ) +from .read import ReadMessage diff --git a/cloudquery/sdk/message/read.py b/cloudquery/sdk/message/read.py new file mode 100644 index 0000000..227aa1e --- /dev/null +++ b/cloudquery/sdk/message/read.py @@ -0,0 +1,6 @@ +import pyarrow as pa + + +class ReadMessage: + def __init__(self, record: pa.RecordBatch): + self.record = record diff --git a/cloudquery/sdk/message/write.py b/cloudquery/sdk/message/write.py index 69ef9cc..f1beeb7 100644 --- a/cloudquery/sdk/message/write.py +++ b/cloudquery/sdk/message/write.py @@ -12,8 +12,9 @@ def __init__(self, record: pa.RecordBatch): class WriteMigrateTableMessage(WriteMessage): - def __init__(self, table: Table): + def __init__(self, table: Table, migrate_force: bool): self.table = table + self.migrate_force = migrate_force class WriteDeleteStale(WriteMessage): diff --git a/cloudquery/sdk/plugin/plugin.py b/cloudquery/sdk/plugin/plugin.py index 2462bc1..d03dbef 100644 --- a/cloudquery/sdk/plugin/plugin.py +++ b/cloudquery/sdk/plugin/plugin.py @@ -29,10 +29,33 @@ class SyncOptions: backend_options: BackendOptions = None +@dataclass +class BuildTarget: + os: str = None + arch: str = None + + +@dataclass +class Options: + dockerfile: str = None + build_targets: List[BuildTarget] = None + team: str = None + kind: str = None + json_schema: str = None + + class Plugin: - def __init__(self, name: str, version: str) -> None: + def __init__(self, name: str, version: str, opts: Options = None) -> None: self._name = name self._version = version + self._opts = Options() if opts is None else opts + if self._opts.dockerfile is None: + self._opts.dockerfile = "Dockerfile" + if self._opts.build_targets is None: + self._opts.build_targets = [ + BuildTarget("linux", "amd64"), + BuildTarget("linux", "arm64"), + ] def init(self, spec: bytes, no_connection: bool = False) -> None: pass @@ -46,6 +69,21 @@ def name(self) -> str: def version(self) -> str: return self._version + def team(self) -> str: + return self._opts.team + + def kind(self) -> str: + return self._opts.kind + + def json_schema(self) -> str: + return self._opts.json_schema + + def dockerfile(self) -> str: + return self._opts.dockerfile + + def build_targets(self) -> List[BuildTarget]: + return self._opts.build_targets + def get_tables(self, options: TableOptions) -> List[Table]: raise NotImplementedError() @@ -55,5 +93,8 @@ def sync(self, options: SyncOptions) -> Generator[message.SyncMessage, None, Non def write(self, writer: Generator[message.WriteMessage, None, None]) -> None: raise NotImplementedError() + def read(self, table: Table) -> Generator[message.ReadMessage, None, None]: + raise NotImplementedError() + def close(self) -> None: raise NotImplementedError() diff --git a/cloudquery/sdk/scalar/uuid.py b/cloudquery/sdk/scalar/uuid.py index 4e49f9f..052633f 100644 --- a/cloudquery/sdk/scalar/uuid.py +++ b/cloudquery/sdk/scalar/uuid.py @@ -4,6 +4,8 @@ class UUID(Scalar): def __init__(self, valid: bool = False, value: uuid.UUID = None): + if isinstance(value, uuid.UUID): + value = value.bytes super().__init__(valid, value) def __eq__(self, scalar: Scalar) -> bool: @@ -27,11 +29,13 @@ def set(self, value: any): self._value = value.value return - if isinstance(value, uuid.UUID): + if isinstance(value, bytes): self._value = value + elif isinstance(value, uuid.UUID): + self._value = value.bytes elif isinstance(value, str): try: - self._value = uuid.UUID(value) + self._value = uuid.UUID(value).bytes except ValueError as e: raise ScalarInvalidTypeError("Invalid type for UUID scalar") from e else: diff --git a/cloudquery/sdk/scheduler/scheduler.py b/cloudquery/sdk/scheduler/scheduler.py index cfd37b2..c7e7b0c 100644 --- a/cloudquery/sdk/scheduler/scheduler.py +++ b/cloudquery/sdk/scheduler/scheduler.py @@ -10,6 +10,7 @@ SyncMigrateTableMessage, ) from cloudquery.sdk.schema import Resource +from cloudquery.sdk.stateclient.stateclient import StateClient from .table_resolver import TableResolver, Client QUEUE_PER_WORKER = 100 @@ -35,6 +36,7 @@ class Scheduler: def __init__( self, concurrency: int, queue_size: int = 0, max_depth: int = 3, logger=None ): + self._post_sync_hook = lambda: None self._queue = queue.Queue() self._max_depth = max_depth if logger is None: @@ -108,13 +110,19 @@ def resolve_table( resource = self.resolve_resource( resolver, client, parent_item, item ) - except Exception: + except Exception as e: self._logger.error( "failed to resolve resource", client_id=client.id(), table=resolver.table.name, depth=depth, - exc_info=True, + exc_info=e, + ) + self._logger.debug( + "details about resource that failed to resolve", + client_id=client.id(), + table=resolver.table.name, + resource=item, ) continue res.put(SyncInsertMessage(resource.to_arrow_record())) @@ -145,14 +153,14 @@ def resolve_table( resources=total_resources, depth=depth, ) - except Exception: + except Exception as e: self._logger.error( "table resolver finished with error", client_id=client.id(), table=resolver.table.name, resources=total_resources, depth=depth, - exc_info=True, + exc_info=e, ) finally: res.put(TableResolverFinished()) @@ -195,6 +203,8 @@ def sync( break continue yield message + + self._post_sync_hook() thread.shutdown(wait=True) def _send_migrate_table_messages( @@ -204,3 +214,18 @@ def _send_migrate_table_messages( yield SyncMigrateTableMessage(table=resolver.table.to_arrow_schema()) if resolver.child_resolvers: yield from self._send_migrate_table_messages(resolver.child_resolvers) + + def set_post_sync_hook(self, fn): + """ + Use this to set a function that will be called after the sync is finished, + a la `defer fn()` in Go (but for a single function, rather than a stack). + + This is necessary because plugins use this pattern on their sync method: + + ``` + return self._scheduler.sync(...) + ``` + + So if a plugin has a `state_client`, there's nowhere to call the flush method. + """ + self._post_sync_hook = fn diff --git a/cloudquery/sdk/scheduler/table_resolver.py b/cloudquery/sdk/scheduler/table_resolver.py index be13d7c..13c20c6 100644 --- a/cloudquery/sdk/scheduler/table_resolver.py +++ b/cloudquery/sdk/scheduler/table_resolver.py @@ -14,6 +14,7 @@ def __init__(self, table: Table, child_resolvers: Optional[List] = None) -> None child_resolvers = [] self._table = table self._child_resolvers = child_resolvers + self.state_client = None @property def table(self) -> Table: @@ -23,6 +24,9 @@ def table(self) -> Table: def child_resolvers(self): return self._child_resolvers + def set_state_client(self, state_client): + self.state_client = state_client + def multiplex(self, client: Client) -> List[Client]: return [client] diff --git a/cloudquery/sdk/schema/__init__.py b/cloudquery/sdk/schema/__init__.py index 649a1fd..63b4065 100644 --- a/cloudquery/sdk/schema/__init__.py +++ b/cloudquery/sdk/schema/__init__.py @@ -1,5 +1,16 @@ from .column import Column -from .table import Table, tables_to_arrow_schemas, filter_dfs +from .table import ( + Table, + tables_to_arrow_schemas, + filter_dfs, + TableColumnChangeType, + TableColumnChange, + TableColumnChangeType, + get_table_changes, + get_table_column, + flatten_tables_recursive, + flatten_tables, +) from .resource import Resource # from .table_resolver import TableReso diff --git a/cloudquery/sdk/schema/column.py b/cloudquery/sdk/schema/column.py index 4473d57..3d165a7 100644 --- a/cloudquery/sdk/schema/column.py +++ b/cloudquery/sdk/schema/column.py @@ -45,21 +45,23 @@ def __eq__(self, __value: object) -> bool: def to_arrow_field(self): metadata = { - arrow.METADATA_PRIMARY_KEY: arrow.METADATA_TRUE - if self.primary_key - else arrow.METADATA_FALSE, - arrow.METADATA_UNIQUE: arrow.METADATA_TRUE - if self.unique - else arrow.METADATA_FALSE, - arrow.METADATA_INCREMENTAL: arrow.METADATA_TRUE - if self.incremental_key - else arrow.METADATA_FALSE, + arrow.METADATA_PRIMARY_KEY: ( + arrow.METADATA_TRUE if self.primary_key else arrow.METADATA_FALSE + ), + arrow.METADATA_UNIQUE: ( + arrow.METADATA_TRUE if self.unique else arrow.METADATA_FALSE + ), + arrow.METADATA_INCREMENTAL: ( + arrow.METADATA_TRUE if self.incremental_key else arrow.METADATA_FALSE + ), } - return pa.field(self.name, self.type, metadata=metadata) + return pa.field( + self.name, self.type, metadata=metadata, nullable=not self.not_null + ) @staticmethod def from_arrow_field(field: pa.Field) -> Column: - metadata = field.metadata + metadata = field.metadata or {} primary_key = metadata.get(arrow.METADATA_PRIMARY_KEY) == arrow.METADATA_TRUE unique = metadata.get(arrow.METADATA_UNIQUE) == arrow.METADATA_TRUE incremental_key = ( diff --git a/cloudquery/sdk/schema/table.py b/cloudquery/sdk/schema/table.py index 0c740c8..a55ce76 100644 --- a/cloudquery/sdk/schema/table.py +++ b/cloudquery/sdk/schema/table.py @@ -1,8 +1,9 @@ from __future__ import annotations import copy +from enum import IntEnum import fnmatch -from typing import List +from typing import List, Optional import pyarrow as pa @@ -10,6 +11,10 @@ from .column import Column +CQ_SYNC_TIME_COLUMN = "cq_sync_time" +CQ_SOURCE_NAME_COLUMN = "cq_source_name" + + class Client: pass @@ -88,9 +93,9 @@ def to_arrow_schema(self): arrow.METADATA_TABLE_DESCRIPTION: self.description, arrow.METADATA_TABLE_TITLE: self.title, arrow.METADATA_TABLE_DEPENDS_ON: self.parent.name if self.parent else "", - arrow.METADATA_INCREMENTAL: arrow.METADATA_TRUE - if self.is_incremental - else arrow.METADATA_FALSE, + arrow.METADATA_INCREMENTAL: ( + arrow.METADATA_TRUE if self.is_incremental else arrow.METADATA_FALSE + ), } for column in self.columns: fields.append(column.to_arrow_field()) @@ -151,6 +156,8 @@ def filter_dfs_func(tt: List[Table], include, exclude, skip_dependent_tables: bo filtered_tables = [] for t in tt: filtered_table = copy.deepcopy(t) + for r in filtered_table.relations: + r.parent = filtered_table filtered_table = _filter_dfs_impl( filtered_table, False, include, exclude, skip_dependent_tables ) @@ -190,9 +197,137 @@ def filter_dfs_child(r, matched, include, exclude, skip_dependent_tables): return None -def flatten_tables(tables: List[Table]) -> List[Table]: - flattened: List[Table] = [] +class TableColumnChangeType: + ADD = 1 + REMOVE = 2 + REMOVE_UNIQUE_CONSTRAINT = 3 + + +class TableColumnChange: + def __init__( + self, + type: TableColumnChangeType, + column_name: str, + current: Optional[Column], + previous: Optional[Column], + ): + self.type = type + self.column_name = column_name + self.current = current + self.previous = previous + + +class TableColumnChangeType(IntEnum): + UNKNOWN = 0 + ADD = 1 + UPDATE = 2 + REMOVE = 3 + REMOVE_UNIQUE_CONSTRAINT = 4 + MOVE_TO_CQ_ONLY = 5 + + +def get_table_changes(new: Table, old: Table) -> List[TableColumnChange]: + changes = [] + + # Special case: Moving from individual PKs to singular PK on _cq_id + new_pks = new.primary_keys + if ( + len(new_pks) == 1 + and new_pks[0] == "CqIDColumn" + and get_table_column(old, "CqIDColumn") is None + and len(old.primary_keys) > 0 + ): + changes.append( + TableColumnChange( + type=TableColumnChangeType.MOVE_TO_CQ_ONLY, + ) + ) + + for c in new.columns: + other_column = get_table_column(old, c.name) + # A column was added to the table definition + if other_column is None: + changes.append( + TableColumnChange( + type=TableColumnChangeType.ADD, + column_name=c.name, + current=c, + previous=None, + ) + ) + continue + + # Column type or options (e.g. PK, Not Null) changed in the new table definition + if ( + c.type != other_column.type + or c.not_null != other_column.not_null + or c.primary_key != other_column.primary_key + ): + changes.append( + TableColumnChange( + type=TableColumnChangeType.UPDATE, + column_name=c.name, + current=c, + previous=other_column, + ) + ) + + # Unique constraint was removed + if not c.unique and other_column.unique: + changes.append( + TableColumnChange( + type=TableColumnChangeType.REMOVE_UNIQUE_CONSTRAINT, + column_name=c.name, + current=c, + previous=other_column, + ) + ) + + # A column was removed from the table definition + for c in old.columns: + if get_table_column(new, c.name) is None: + changes.append( + TableColumnChange( + type=TableColumnChangeType.REMOVE, + column_name=c.name, + current=None, + previous=c, + ) + ) + + return changes + + +def get_table_column(table: Table, column_name: str) -> Optional[Column]: + for c in table.columns: + if c.name == column_name: + return c + return None + + +def flatten_tables_recursive(original_tables: List[Table]) -> List[Table]: + tables = [] + for table in original_tables: + table_copy = Table( + name=table.name, + columns=table.columns, + relations=table.relations, + title=table.title, + description=table.description, + is_incremental=table.is_incremental, + parent=table.parent, + ) + tables.append(table_copy) + tables.extend(flatten_tables_recursive(table.relations)) + return tables + + +def flatten_tables(original_tables: List[Table]) -> List[Table]: + tables = flatten_tables_recursive(original_tables) + seen = set() + deduped = [] for table in tables: - flattened.append(table) - flattened.extend(flatten_tables(table.relations)) - return flattened + if table.name not in seen: + deduped.append(table) + seen.add(table.name) + return deduped diff --git a/cloudquery/sdk/serve/plugin.py b/cloudquery/sdk/serve/plugin.py index 23f56cc..7ea4e88 100644 --- a/cloudquery/sdk/serve/plugin.py +++ b/cloudquery/sdk/serve/plugin.py @@ -1,7 +1,13 @@ import argparse +import hashlib +import json import logging import os +import shutil +import subprocess +import tarfile from concurrent import futures +from pathlib import Path import grpc import structlog @@ -9,10 +15,13 @@ from cloudquery.discovery_v1 import discovery_pb2_grpc from cloudquery.plugin_v3 import plugin_pb2_grpc from structlog import wrap_logger +from cloudquery.sdk import plugin + from cloudquery.sdk.internal.servers.discovery_v1.discovery import DiscoveryServicer from cloudquery.sdk.internal.servers.plugin_v3 import PluginServicer from cloudquery.sdk.plugin.plugin import Plugin +from cloudquery.sdk.schema import table _IS_WINDOWS = sys.platform == "win32" @@ -74,6 +83,14 @@ def get_logger(args): return log +def calc_sha256_checksum(filename: str): + with open(filename, "rb") as f: + file_hash = hashlib.sha256() + while chunk := f.read(32768): + file_hash.update(chunk) + return file_hash.hexdigest() + + class PluginCommand: def __init__(self, plugin: Plugin): self._plugin = plugin @@ -82,6 +99,20 @@ def run(self, args): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest="command", required=True) + self._register_serve_command(subparsers) + self._register_package_command(subparsers) + + parsed_args = parser.parse_args(args) + + if parsed_args.command == "serve": + self._serve(parsed_args) + elif parsed_args.command == "package": + self._package(parsed_args) + else: + parser.print_help() + sys.exit(1) + + def _register_serve_command(self, subparsers): serve_parser = subparsers.add_parser("serve", help="Start plugin server") serve_parser.add_argument( "--log-format", @@ -97,7 +128,6 @@ def run(self, args): choices=["trace", "debug", "info", "warn", "error"], help="log level", ) - # ignored for now serve_parser.add_argument( "--no-sentry", @@ -118,7 +148,13 @@ def run(self, args): default="", help="Open Telemetry HTTP collector endpoint (for development only) (placeholder for future use)", ) - + # ignored for now + serve_parser.add_argument( + "--license", + type=str, + default="", + help="set offline license file (placeholder for future use)", + ) serve_parser.add_argument( "--address", type=str, @@ -133,13 +169,224 @@ def run(self, args): help="network to serve on. can be tcp or unix", ) - parsed_args = parser.parse_args(args) + def _register_package_command(self, subparsers): + package_parser = subparsers.add_parser( + "package", help="Package the plugin as a Docker image" + ) + package_parser.add_argument( + "version", help="version to tag the Docker image with" + ) + package_parser.add_argument("plugin-directory") + package_parser.add_argument( + "--log-format", + type=str, + default="text", + choices=["text", "json"], + help="logging format", + ) + package_parser.add_argument( + "--log-level", + type=str, + default="info", + choices=["trace", "debug", "info", "warn", "error"], + help="log level", + ) + package_parser.add_argument( + "-D", + "--dist-dir", + type=str, + help="dist directory to output the built plugin. (default: /dist)", + ) + package_parser.add_argument( + "--docs-dir", + type=str, + help="docs directory containing markdown files to copy to the dist directory. (default: /docs)", + ) + package_parser.add_argument( + "-m", + "--message", + type=str, + required=True, + help="message that summarizes what is new or changed in this version. Use @ to read from file. Supports markdown.", + ) - if parsed_args.command == "serve": - self._serve(parsed_args) - else: - parser.print_help() - sys.exit(1) + def _package(self, args): + logger = get_logger(args) + self._plugin.set_logger(logger) + + def _is_empty(val): + return val == None or len(val) == 0 + + if _is_empty(self._plugin.name()): + raise Exception("plugin name is required") + if _is_empty(self._plugin.team()): + raise Exception("plugin team is required") + if _is_empty(self._plugin.kind()): + raise Exception("plugin kind is required") + if _is_empty(self._plugin.dockerfile()): + raise Exception("plugin dockerfile is required") + if _is_empty(self._plugin.build_targets()): + raise Exception("at least one build target is required") + + plugin_directory, version, message = ( + getattr(args, "plugin-directory"), + getattr(args, "version"), + getattr(args, "message"), + ) + dist_dir = ( + "%s/dist" % plugin_directory if args.dist_dir == None else args.dist_dir + ) + docs_dir = ( + "%s/docs" % plugin_directory if args.docs_dir == None else args.docs_dir + ) + Path(dist_dir).mkdir(0o755, exist_ok=True, parents=True) + + self._copy_docs(logger, docs_dir, dist_dir) + self._write_tables_json(logger, dist_dir) + supported_targets = self._build_dockerfile( + logger, plugin_directory, dist_dir, version + ) + self._write_package_json(logger, dist_dir, message, version, supported_targets) + logger.info("Done packaging plugin to '%s'" % dist_dir) + + def _write_package_json(self, logger, dist_dir, message, version, supportedTargets): + package_json_path = "%s/package.json" % dist_dir + logger.info("Writing package.json to '%s'" % package_json_path) + content = { + "schema_version": 1, + "name": self._plugin.name(), + "team": self._plugin.team(), + "kind": self._plugin.kind(), + "version": version, + "message": message, + "protocols": [3], + "supported_targets": supportedTargets, + "package_type": "docker", + } + with open("%s/package.json" % dist_dir, "w") as f: + f.write(json.dumps(content, indent=2)) + + def _copy_docs(self, logger, docs_dir, dist_dir): + # check is docs_dir exists + if not os.path.isdir(docs_dir): + raise Exception("docs directory '%s' does not exist" % docs_dir) + + output_docs_dir = "%s/docs" % dist_dir + logger.info("Copying docs from '%s' to '%s'" % (docs_dir, output_docs_dir)) + shutil.copytree(docs_dir, output_docs_dir, dirs_exist_ok=True) + + def _write_tables_json(self, logger, dist_dir): + if self._plugin.kind() != "source": + return + + tables_json_output_path = "%s/tables.json" % dist_dir + logger.info("Writing tables to '%s'" % tables_json_output_path) + self._plugin.init(spec=b"", no_connection=True) + tables = self._plugin.get_tables( + options=plugin.plugin.TableOptions( + tables=["*"], skip_tables=[], skip_dependent_tables=False + ) + ) + flattened_tables = table.flatten_tables(tables) + + def column_to_json(column: table.Column): + return { + "name": column.name, + "type": str(column.type), + "description": column.description, + "incremental_key": column.incremental_key, + "primary_key": column.primary_key, + "not_null": column.not_null, + "unique": column.unique, + } + + def table_to_json(table: table.Table): + return { + "name": table.name, + "title": table.title, + "description": table.description, + "is_incremental": table.is_incremental, + "parent": table.parent.name if table.parent else "", + "relations": list(map(lambda r: r.name, table.relations)), + "columns": list(map(column_to_json, table.columns)), + } + + tables_json = list(map(table_to_json, flattened_tables)) + with open(tables_json_output_path, "w") as f: + f.write(json.dumps(tables_json)) + logger.info( + "Wrote %d tables to '%s'" % (len(tables_json), tables_json_output_path) + ) + + def _build_dockerfile(self, logger, plugin_dir, dist_dir, version): + dockerfile_path = "%s/%s" % (plugin_dir, self._plugin.dockerfile()) + if not os.path.isfile(dockerfile_path): + raise Exception("Dockerfile '%s' does not exist" % dockerfile_path) + + def run_docker_cmd(cmd, plugin_dir): + result = subprocess.run(cmd, capture_output=True, cwd=plugin_dir) + if result.returncode != 0: + err = ( + "" + if result.stderr is None + else result.stderr.decode("utf-8").strip() + ) + raise ChildProcessError("Unable to run Docker command: %s" % err) + + def build_target(target: plugin.plugin.BuildTarget): + image_repository = "docker.cloudquery.io/%s/%s-%s" % ( + self._plugin.team(), + self._plugin.kind(), + self._plugin.name(), + ) + image_tag = "%s:%s-%s-%s" % ( + image_repository, + version, + target.os, + target.arch, + ) + image_tar = "plugin-%s-%s-%s-%s.tar" % ( + self._plugin.name(), + version, + target.os, + target.arch, + ) + image_path = "%s/%s" % (dist_dir, image_tar) + logger.info("Building docker image %s" % image_tag) + docker_build_arguments = [ + "docker", + "buildx", + "build", + "-t", + image_tag, + "--platform", + "%s/%s" % (target.os, target.arch), + "-f", + dockerfile_path, + ".", + "--progress", + "plain", + "--load", + ] + logger.debug( + "Running command 'docker %s'" % " ".join(docker_build_arguments) + ) + run_docker_cmd(docker_build_arguments, plugin_dir) + logger.debug("Saving docker image '%s' to '%s'" % (image_tag, image_path)) + docker_save_arguments = ["docker", "save", "-o", image_path, image_tag] + logger.debug("Running command 'docker %s'", " ".join(docker_save_arguments)) + run_docker_cmd(docker_save_arguments, plugin_dir) + return { + "os": target.os, + "arch": target.arch, + "path": image_tar, + "checksum": calc_sha256_checksum(image_path), + "docker_image_tag": image_tag, + } + + logger.info("Building %d targets" % len(self._plugin.build_targets())) + supported_targets = list(map(build_target, self._plugin.build_targets())) + return supported_targets def _serve(self, args): logger = get_logger(args) diff --git a/cloudquery/sdk/stateclient/__init__.py b/cloudquery/sdk/stateclient/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cloudquery/sdk/stateclient/stateclient.py b/cloudquery/sdk/stateclient/stateclient.py new file mode 100644 index 0000000..0b0dded --- /dev/null +++ b/cloudquery/sdk/stateclient/stateclient.py @@ -0,0 +1,207 @@ +from __future__ import annotations + +import grpc +from abc import ABC, abstractmethod +from typing import Generator, Optional, Tuple + +import pyarrow as pa +from cloudquery.sdk import schema +from cloudquery.sdk.plugin.plugin import BackendOptions +from cloudquery.plugin_v3 import plugin_pb2, plugin_pb2_grpc, arrow +from functools import wraps + +keyColumn = "key" +valueColumn = "value" + + +class StateClientBuilder: + """ + Provides a `build` method that creates a `ConnectedStateClient` if you pass backend_options, + or `NoOpStateClient` otherwise. + + Args: + backend_options (BackendOptions): which has `connection` & `table_name` strings. + + Returns: + `ConnectedStateClient` or `NoOpStateClient` + + """ + + @staticmethod + def build(*, backend_options: BackendOptions) -> StateClient: + if not backend_options or not backend_options.table_name: + return NoOpStateClient(backend_options=backend_options) + + return ConnectedStateClient(backend_options=backend_options) + + +class StateClient(ABC): + """ + Abstract class that defines the interface for a state client. + + It implements all methods except those that require a connection, + so it is a succinct overview of what a state client does. + """ + + def __init__(self, *, backend_options: BackendOptions): + self.mem = {} + self.changes = {} + self.connection = getattr(backend_options, "connection", None) + self.table = Table(getattr(backend_options, "table_name", None)) + + self.migrate_state_table() + self.read_all_state() + + def get_key(self, key: str) -> Optional[str]: + return self.mem.get(key) + + def set_key(self, key: str, value: str) -> None: + self.mem[key] = value + self.changes[key] = True + + def flush(self): + if not self.changes: + return + + self.write_all_state(self._changed_keys()) + + def _changed_keys(self): + for key, _ in self.changes.items(): + yield (key, self.mem[key]) + + @abstractmethod + def migrate_state_table(self): + pass + + @abstractmethod + def read_all_state(self): + pass + + @abstractmethod + def write_all_state(self, changed_keys: Generator[Tuple[str, str], None, None]): + pass + + +class NoOpStateClient(StateClient): + """ + A state client implementation that does nothing. Used when there is no backend connection. + """ + + def get_key(self, key: str) -> Optional[str]: + pass + + def set_key(self, key: str, value: str) -> None: + pass + + def migrate_state_table(self): + pass + + def read_all_state(self): + pass + + def write_all_state(self, changed_keys: Generator[Tuple[str, str], None, None]): + pass + + +def connected(func): + """ + Decorator that provides a `backend_plugin` with a gRPC connection to the decorated function. + """ + + @wraps(func) + def wrapper(self, *args, **kwargs): + with grpc.insecure_channel(self.connection) as channel: + backend_plugin = plugin_pb2_grpc.PluginStub(channel) + return func(self, backend_plugin, *args, **kwargs) + + return wrapper + + +class ConnectedStateClient(StateClient): + """ + A state client implementation that connects to a backend plugin via gRPC to read/write state. + """ + + @connected + def migrate_state_table(self, backend_plugin: plugin_pb2_grpc.PluginStub): + backend_plugin.Write(self._migrate_table_request()) + + @connected + def read_all_state(self, backend_plugin: plugin_pb2_grpc.PluginStub): + response = backend_plugin.Read( + plugin_pb2.Read.Request(table=self.table.bytes()) + ) + + for record in read_response_to_records(response): + self.mem[record[keyColumn]] = record[valueColumn] + + @connected + def write_all_state( + self, + backend_plugin: plugin_pb2_grpc.PluginStub, + changed_keys: Generator[Tuple[str, str], None, None], + ): + backend_plugin.Write(self._write_request(k, v) for k, v in changed_keys) + + def _write_request(self, key: str, value: str) -> plugin_pb2.Write.Request: + record = pa.RecordBatch.from_arrays( + [ + pa.array([key]), + pa.array([value]), + ], + schema=self.table.arrow_schema(), + ) + return plugin_pb2.Write.Request( + insert=plugin_pb2.Write.MessageInsert(record=arrow.record_to_bytes(record)) + ) + + def _migrate_table_request(self): + yield plugin_pb2.Write.Request( + migrate_table=plugin_pb2.Write.MessageMigrateTable(table=self.table.bytes()) + ) + + +def read_response_to_records(response) -> Generator[dict[str, str], None, None]: + for record in response: + record_batch = arrow.new_record_from_bytes(record.record) + for record in recordbatch_to_list_of_maps(record_batch): + yield record + + +def recordbatch_to_list_of_maps( + record_batch: pa.RecordBatch, +) -> Generator[dict[str, str], None, None]: + table = pa.Table.from_batches([record_batch]) + for row in table.to_pandas().to_dict(orient="records"): + yield row + + +class Table: + """ + Represents a state table with two columns: key and value. + Provides convenience methods for whatever the gRPC requests need. + """ + + def __init__(self, name): + self.name = name + self._arrow_schema = None + self._bytes = None + + if self.name: + self._arrow_schema = self._state_table_schema().to_arrow_schema() + self._bytes = arrow.schema_to_bytes(self._arrow_schema) + + def arrow_schema(self): + return self._arrow_schema + + def bytes(self): + return self._bytes + + def _state_table_schema(self): + return schema.Table( + name=self.name, + columns=[ + schema.Column(name=keyColumn, type=pa.string(), primary_key=True), + schema.Column(name=valueColumn, type=pa.string()), + ], + ) diff --git a/cloudquery/sdk/types/json.py b/cloudquery/sdk/types/json.py index 28bf3bb..dfcd852 100644 --- a/cloudquery/sdk/types/json.py +++ b/cloudquery/sdk/types/json.py @@ -13,8 +13,14 @@ def __arrow_ext_serialize__(self): # metadata to be deserialized return b"json-serialized" + def __str__(self): + return "json" + @classmethod def __arrow_ext_deserialize__(self, storage_type, serialized): # return an instance of this subclass given the serialized # metadata. return JSONType() + + +pa.register_extension_type(JSONType()) diff --git a/cloudquery/sdk/types/uuid.py b/cloudquery/sdk/types/uuid.py index a9ef571..549774c 100644 --- a/cloudquery/sdk/types/uuid.py +++ b/cloudquery/sdk/types/uuid.py @@ -15,8 +15,14 @@ def __arrow_ext_serialize__(self): # metadata to be deserialized return b"uuid-serialized" + def __str__(self): + return "uuid" + @classmethod def __arrow_ext_deserialize__(self, storage_type, serialized): # return an instance of this subclass given the serialized # metadata. return UUIDType() + + +pa.register_extension_type(UUIDType()) diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 0000000..027ec10 --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,3 @@ +# MemDB Plugin + +Test docs for the MemDB plugin. diff --git a/main.py b/main.py new file mode 100644 index 0000000..06e81ce --- /dev/null +++ b/main.py @@ -0,0 +1,13 @@ +import sys +from cloudquery.sdk import serve + +from cloudquery.sdk.internal.memdb import MemDB + + +def main(): + p = MemDB() + serve.PluginCommand(p).run(sys.argv[1:]) + + +if __name__ == "__main__": + main() diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..5e60778 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "python", + "pull-request-title-pattern": "chore${scope}: Release${component} v${version}", + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": true, + "include-component-in-tag": false, + "packages": { + ".": {} + } +} diff --git a/setup.py b/setup.py index 48da519..c2e2f11 100644 --- a/setup.py +++ b/setup.py @@ -10,32 +10,30 @@ description = "CloudQuery Plugin SDK for Python" dependencies = [ - "cloudquery-plugin-pb==0.0.17", - "exceptiongroup==1.1.3", - "black==23.9.1", - "grpcio==1.59.0", - "grpcio-tools==1.59.0", - "iniconfig==2.0.0", - "Jinja2==3.1.2", - "MarkupSafe==2.1.3", - "numpy==1.26.0", - "packaging==23.1", - "pandas==2.1.1", - "pluggy==1.3.0", - "protobuf==4.24.3", - "pyarrow==12.0.1", - "pytest==7.4.2", - "python-dateutil==2.8.2", - "pytz==2023.3.post1", - "six==1.16.0", - "structlog==23.1.0", - "tomli==2.0.1", - "tzdata==2023.3", + "cloudquery-plugin-pb==0.0.46", + "exceptiongroup==1.3.0", + "black==25.1.0", + "grpcio==1.74.0", + "grpcio-tools==1.74.0", + "iniconfig==2.1.0", + "Jinja2==3.1.6", + "MarkupSafe==3.0.2", + "numpy==2.3.2", + "packaging==24.2", + "pandas==2.2.3", + "pluggy==1.5.0", + "protobuf>=6.31.1", + "pyarrow==19.0.1", + "pytest==8.3.5", + "python-dateutil>=2.8.1", + "pytz==2025.1", + "six==1.17.0", + "structlog==25.1.0", + "tomli==2.2.1", + "tzdata==2025.1", ] url = "https://github.com/cloudquery/plugin-sdk-python" -package_root = os.path.abspath(os.path.dirname(__file__)) - long_description = """ CloudQuery Plugin SDK for Python ================================================ @@ -53,7 +51,7 @@ ] setuptools.setup( name=name, - version="0.1.5", + version="0.1.45", description=description, long_description=long_description, author="CloudQuery LTD", diff --git a/tests/internal/memdb/memdb.py b/tests/internal/memdb/memdb.py index 546d109..5b75f38 100644 --- a/tests/internal/memdb/memdb.py +++ b/tests/internal/memdb/memdb.py @@ -1,10 +1,11 @@ from cloudquery.sdk.internal import memdb +from cloudquery.sdk.internal.servers.plugin_v3 import plugin from cloudquery.sdk.plugin import SyncOptions def test_memdb(): p = memdb.MemDB() - p.init(None) + p.init(plugin.sanitize_spec(b"null")) msgs = [] for msg in p.sync(SyncOptions(tables=["*"])): msgs.append(msg) diff --git a/tests/serve/plugin.py b/tests/serve/plugin.py index 26c212c..f07d819 100644 --- a/tests/serve/plugin.py +++ b/tests/serve/plugin.py @@ -1,4 +1,7 @@ +import json +import os import random +from uuid import UUID import grpc import time import pyarrow as pa @@ -7,12 +10,16 @@ from cloudquery.sdk import serve from cloudquery.plugin_v3 import plugin_pb2_grpc, plugin_pb2, arrow from cloudquery.sdk.internal.memdb import MemDB +from cloudquery.sdk.types.json import JSONType +from cloudquery.sdk.types.uuid import UUIDType test_table = Table( "test", [ Column("id", pa.int64()), Column("name", pa.string()), + Column("json", JSONType()), + Column("uuid", UUIDType()), ], ) @@ -45,6 +52,15 @@ def writer_iterator(): [ pa.array([1, 2, 3]), pa.array(["a", "b", "c"]), + pa.array([None, b"{}", b'{"a":null}']), + pa.array( + [ + None, + UUID("550e8400-e29b-41d4-a716-446655440000").bytes, + UUID("123e4567-e89b-12d3-a456-426614174000").bytes, + ], + type=pa.binary(16), + ), ], schema=test_table.to_arrow_schema(), ) @@ -58,7 +74,7 @@ def writer_iterator(): response = stub.GetTables(plugin_pb2.GetTables.Request(tables=["*"])) schemas = arrow.new_schemas_from_bytes(response.tables) - assert len(schemas) == 1 + assert len(schemas) == 4 response = stub.Sync(plugin_pb2.Sync.Request(tables=["*"])) total_records = 0 @@ -70,3 +86,205 @@ def writer_iterator(): finally: cmd.stop() pool.shutdown() + + +def test_plugin_read(): + p = MemDB() + sample_record_1 = pa.RecordBatch.from_arrays( + [ + pa.array([1, 2, 3]), + pa.array(["a", "b", "c"]), + pa.array([None, b"{}", b'{"a":null}']), + pa.array( + [ + None, + UUID("550e8400-e29b-41d4-a716-446655440000").bytes, + UUID("123e4567-e89b-12d3-a456-426614174000").bytes, + ], + type=pa.binary(16), + ), + ], + schema=test_table.to_arrow_schema(), + ) + sample_record_2 = pa.RecordBatch.from_arrays( + [ + pa.array([2, 3, 4]), + pa.array(["b", "c", "d"]), + pa.array([b'""', b'{"a":true}', b'{"b":1}']), + pa.array( + [ + UUID("9bba4c2a-1a37-4fbe-b489-6b40303a8a25").bytes, + None, + UUID("3fa85f64-5717-4562-b3fc-2c963f66afa6").bytes, + ], + type=pa.binary(16), + ), + ], + schema=test_table.to_arrow_schema(), + ) + p._db["test_1"] = sample_record_1 + p._db["test_2"] = sample_record_2 + + cmd = serve.PluginCommand(p) + port = random.randint(5000, 50000) + pool = futures.ThreadPoolExecutor(max_workers=1) + pool.submit(cmd.run, ["serve", "--address", f"[::]:{port}"]) + time.sleep(1) + try: + with grpc.insecure_channel(f"localhost:{port}") as channel: + stub = plugin_pb2_grpc.PluginStub(channel) + response = stub.GetName(plugin_pb2.GetName.Request()) + assert response.name == "memdb" + + response = stub.GetVersion(plugin_pb2.GetVersion.Request()) + assert response.version == "development" + + response = stub.Init(plugin_pb2.Init.Request(spec=b"")) + assert response is not None + + request = plugin_pb2.Read.Request( + table=arrow.schema_to_bytes(test_table.to_arrow_schema()) + ) + reader_iterator = stub.Read(request) + + output_records = [] + for msg in reader_iterator: + output_records.append(arrow.new_record_from_bytes(msg.record)) + + assert len(output_records) == 2 + assert output_records[0].equals(sample_record_1) + assert output_records[1].equals(sample_record_2) + finally: + cmd.stop() + pool.shutdown() + + +def test_plugin_package(): + p = MemDB() + cmd = serve.PluginCommand(p) + cmd.run(["package", "-m", "test", "v1.0.0", "."]) + assert os.path.isfile("dist/tables.json") + assert os.path.isfile("dist/package.json") + assert os.path.isfile("dist/docs/overview.md") + assert os.path.isfile("dist/plugin-memdb-v1.0.0-linux-amd64.tar") + assert os.path.isfile("dist/plugin-memdb-v1.0.0-linux-arm64.tar") + + with open("dist/tables.json", "r") as f: + tables = json.loads(f.read()) + assert tables == [ + { + "name": "table_1", + "title": "Table 1", + "description": "Test Table 1", + "is_incremental": True, + "parent": "", + "relations": ["table_1_relation_1"], + "columns": [ + { + "name": "name", + "type": "string", + "description": "", + "incremental_key": False, + "primary_key": True, + "not_null": True, + "unique": True, + }, + { + "name": "id", + "type": "string", + "description": "", + "incremental_key": True, + "primary_key": True, + "not_null": True, + "unique": True, + }, + ], + }, + { + "name": "table_1_relation_1", + "title": "Table 1 Relation 1", + "description": "Test Table 1 Relation 1", + "is_incremental": False, + "parent": "table_1", + "relations": [], + "columns": [ + { + "name": "name", + "type": "string", + "description": "", + "incremental_key": False, + "primary_key": True, + "not_null": True, + "unique": True, + }, + { + "name": "data", + "type": "json", + "description": "", + "incremental_key": False, + "primary_key": False, + "not_null": False, + "unique": False, + }, + ], + }, + { + "name": "table_2", + "title": "Table 2", + "description": "Test Table 2", + "is_incremental": False, + "parent": "", + "relations": [], + "columns": [ + { + "name": "name", + "type": "string", + "description": "", + "incremental_key": False, + "primary_key": True, + "not_null": True, + "unique": True, + }, + { + "name": "id", + "type": "string", + "description": "", + "incremental_key": False, + "primary_key": False, + "not_null": False, + "unique": False, + }, + ], + }, + ] + with open("dist/package.json", "r") as f: + package = json.loads(f.read()) + assert package["schema_version"] == 1 + assert package["name"] == "memdb" + assert package["version"] == "v1.0.0" + assert package["team"] == "cloudquery" + assert package["kind"] == "source" + assert package["message"] == "test" + assert package["protocols"] == [3] + assert len(package["supported_targets"]) == 2 + assert package["package_type"] == "docker" + assert package["supported_targets"][0]["os"] == "linux" + assert package["supported_targets"][0]["arch"] == "amd64" + assert ( + package["supported_targets"][0]["path"] + == "plugin-memdb-v1.0.0-linux-amd64.tar" + ) + assert ( + package["supported_targets"][0]["docker_image_tag"] + == "docker.cloudquery.io/cloudquery/source-memdb:v1.0.0-linux-amd64" + ) + assert package["supported_targets"][1]["os"] == "linux" + assert package["supported_targets"][1]["arch"] == "arm64" + assert ( + package["supported_targets"][1]["path"] + == "plugin-memdb-v1.0.0-linux-arm64.tar" + ) + assert ( + package["supported_targets"][1]["docker_image_tag"] + == "docker.cloudquery.io/cloudquery/source-memdb:v1.0.0-linux-arm64" + )