From c27b4d2a177e883bbdb78c0395d8f37baaf11a02 Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Wed, 9 Mar 2022 14:28:24 -0800 Subject: [PATCH 01/20] Generate source-pivotal-tracker --- .../source-pivotal-tracker/.dockerignore | 6 + .../source-pivotal-tracker/Dockerfile | 38 ++++ .../source-pivotal-tracker/README.md | 132 +++++++++++ .../acceptance-test-config.yml | 30 +++ .../acceptance-test-docker.sh | 16 ++ .../source-pivotal-tracker/build.gradle | 9 + .../integration_tests/__init__.py | 3 + .../integration_tests/abnormal_state.json | 5 + .../integration_tests/acceptance.py | 16 ++ .../integration_tests/catalog.json | 39 ++++ .../integration_tests/configured_catalog.json | 22 ++ .../integration_tests/invalid_config.json | 3 + .../integration_tests/sample_config.json | 3 + .../integration_tests/sample_state.json | 5 + .../connectors/source-pivotal-tracker/main.py | 13 ++ .../source-pivotal-tracker/requirements.txt | 2 + .../source-pivotal-tracker/setup.py | 29 +++ .../source_pivotal_tracker/__init__.py | 8 + .../source_pivotal_tracker/schemas/TODO.md | 25 +++ .../schemas/customers.json | 16 ++ .../schemas/employees.json | 19 ++ .../source_pivotal_tracker/source.py | 206 ++++++++++++++++++ .../source_pivotal_tracker/spec.json | 16 ++ .../unit_tests/__init__.py | 3 + .../unit_tests/test_incremental_streams.py | 59 +++++ .../unit_tests/test_source.py | 22 ++ .../unit_tests/test_streams.py | 83 +++++++ 27 files changed, 828 insertions(+) create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/.dockerignore create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/Dockerfile create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/README.md create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-docker.sh create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/build.gradle create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/__init__.py create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/abnormal_state.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/acceptance.py create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_state.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/main.py create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/requirements.txt create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/setup.py create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/__init__.py create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/TODO.md create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/customers.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/employees.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/spec.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/__init__.py create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_incremental_streams.py create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/.dockerignore b/airbyte-integrations/connectors/source-pivotal-tracker/.dockerignore new file mode 100644 index 0000000000000..de48c69fffadf --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/.dockerignore @@ -0,0 +1,6 @@ +* +!Dockerfile +!main.py +!source_pivotal_tracker +!setup.py +!secrets diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/Dockerfile b/airbyte-integrations/connectors/source-pivotal-tracker/Dockerfile new file mode 100644 index 0000000000000..bc7549c338383 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/Dockerfile @@ -0,0 +1,38 @@ +FROM python:3.7.11-alpine3.14 as base + +# build and load all requirements +FROM base as builder +WORKDIR /airbyte/integration_code + +# upgrade pip to the latest version +RUN apk --no-cache upgrade \ + && pip install --upgrade pip \ + && apk --no-cache add tzdata build-base + + +COPY setup.py ./ +# install necessary packages to a temporary folder +RUN pip install --prefix=/install . + +# build a clean environment +FROM base +WORKDIR /airbyte/integration_code + +# copy all loaded and built libraries to a pure basic image +COPY --from=builder /install /usr/local +# add default timezone settings +COPY --from=builder /usr/share/zoneinfo/Etc/UTC /etc/localtime +RUN echo "Etc/UTC" > /etc/timezone + +# bash is installed for more convenient debugging. +RUN apk --no-cache add bash + +# copy payload code only +COPY main.py ./ +COPY source_pivotal_tracker ./source_pivotal_tracker + +ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py" +ENTRYPOINT ["python", "/airbyte/integration_code/main.py"] + +LABEL io.airbyte.version=0.1.0 +LABEL io.airbyte.name=airbyte/source-pivotal-tracker diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/README.md b/airbyte-integrations/connectors/source-pivotal-tracker/README.md new file mode 100644 index 0000000000000..a432322c5fd5f --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/README.md @@ -0,0 +1,132 @@ +# Pivotal Tracker Source + +This is the repository for the Pivotal Tracker source connector, written in Python. +For information about how to use this connector within Airbyte, see [the documentation](https://docs.airbyte.io/integrations/sources/pivotal-tracker). + +## Local development + +### Prerequisites +**To iterate on this connector, make sure to complete this prerequisites section.** + +#### Minimum Python version required `= 3.7.0` + +#### Build & Activate Virtual Environment and install dependencies +From this connector directory, create a virtual environment: +``` +python -m venv .venv +``` + +This will generate a virtualenv for this module in `.venv/`. Make sure this venv is active in your +development environment of choice. To activate it from the terminal, run: +``` +source .venv/bin/activate +pip install -r requirements.txt +pip install '.[tests]' +``` +If you are in an IDE, follow your IDE's instructions to activate the virtualenv. + +Note that while we are installing dependencies from `requirements.txt`, you should only edit `setup.py` for your dependencies. `requirements.txt` is +used for editable installs (`pip install -e`) to pull in Python dependencies from the monorepo and will call `setup.py`. +If this is mumbo jumbo to you, don't worry about it, just put your deps in `setup.py` but install using `pip install -r requirements.txt` and everything +should work as you expect. + +#### Building via Gradle +You can also build the connector in Gradle. This is typically used in CI and not needed for your development workflow. + +To build using Gradle, from the Airbyte repository root, run: +``` +./gradlew :airbyte-integrations:connectors:source-pivotal-tracker:build +``` + +#### Create credentials +**If you are a community contributor**, follow the instructions in the [documentation](https://docs.airbyte.io/integrations/sources/pivotal-tracker) +to generate the necessary credentials. Then create a file `secrets/config.json` conforming to the `source_pivotal_tracker/spec.json` file. +Note that any directory named `secrets` is gitignored across the entire Airbyte repo, so there is no danger of accidentally checking in sensitive information. +See `integration_tests/sample_config.json` for a sample config file. + +**If you are an Airbyte core member**, copy the credentials in Lastpass under the secret name `source pivotal-tracker test creds` +and place them into `secrets/config.json`. + +### Locally running the connector +``` +python main.py spec +python main.py check --config secrets/config.json +python main.py discover --config secrets/config.json +python main.py read --config secrets/config.json --catalog integration_tests/configured_catalog.json +``` + +### Locally running the connector docker image + +#### Build +First, make sure you build the latest Docker image: +``` +docker build . -t airbyte/source-pivotal-tracker:dev +``` + +You can also build the connector image via Gradle: +``` +./gradlew :airbyte-integrations:connectors:source-pivotal-tracker:airbyteDocker +``` +When building via Gradle, the docker image name and tag, respectively, are the values of the `io.airbyte.name` and `io.airbyte.version` `LABEL`s in +the Dockerfile. + +#### Run +Then run any of the connector commands as follows: +``` +docker run --rm airbyte/source-pivotal-tracker:dev spec +docker run --rm -v $(pwd)/secrets:/secrets airbyte/source-pivotal-tracker:dev check --config /secrets/config.json +docker run --rm -v $(pwd)/secrets:/secrets airbyte/source-pivotal-tracker:dev discover --config /secrets/config.json +docker run --rm -v $(pwd)/secrets:/secrets -v $(pwd)/integration_tests:/integration_tests airbyte/source-pivotal-tracker:dev read --config /secrets/config.json --catalog /integration_tests/configured_catalog.json +``` +## Testing +Make sure to familiarize yourself with [pytest test discovery](https://docs.pytest.org/en/latest/goodpractices.html#test-discovery) to know how your test files and methods should be named. +First install test dependencies into your virtual environment: +``` +pip install .[tests] +``` +### Unit Tests +To run unit tests locally, from the connector directory run: +``` +python -m pytest unit_tests +``` + +### Integration Tests +There are two types of integration tests: Acceptance Tests (Airbyte's test suite for all source connectors) and custom integration tests (which are specific to this connector). +#### Custom Integration tests +Place custom tests inside `integration_tests/` folder, then, from the connector root, run +``` +python -m pytest integration_tests +``` +#### Acceptance Tests +Customize `acceptance-test-config.yml` file to configure tests. See [Source Acceptance Tests](https://docs.airbyte.io/connector-development/testing-connectors/source-acceptance-tests-reference) for more information. +If your connector requires to create or destroy resources for use during acceptance tests create fixtures for it and place them inside integration_tests/acceptance.py. +To run your integration tests with acceptance tests, from the connector root, run +``` +python -m pytest integration_tests -p integration_tests.acceptance +``` +To run your integration tests with docker + +### Using gradle to run tests +All commands should be run from airbyte project root. +To run unit tests: +``` +./gradlew :airbyte-integrations:connectors:source-pivotal-tracker:unitTest +``` +To run acceptance and custom integration tests: +``` +./gradlew :airbyte-integrations:connectors:source-pivotal-tracker:integrationTest +``` + +## Dependency Management +All of your dependencies should go in `setup.py`, NOT `requirements.txt`. The requirements file is only used to connect internal Airbyte dependencies in the monorepo for local development. +We split dependencies between two groups, dependencies that are: +* required for your connector to work need to go to `MAIN_REQUIREMENTS` list. +* required for the testing need to go to `TEST_REQUIREMENTS` list + +### Publishing a new version of the connector +You've checked out the repo, implemented a million dollar feature, and you're ready to share your changes with the world. Now what? +1. Make sure your changes are passing unit and integration tests. +1. Bump the connector version in `Dockerfile` -- just increment the value of the `LABEL io.airbyte.version` appropriately (we use [SemVer](https://semver.org/)). +1. Create a Pull Request. +1. Pat yourself on the back for being an awesome contributor. +1. Someone from Airbyte will take a look at your PR and iterate with you to merge it into master. diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml b/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml new file mode 100644 index 0000000000000..80cca323c3735 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml @@ -0,0 +1,30 @@ +# See [Source Acceptance Tests](https://docs.airbyte.io/connector-development/testing-connectors/source-acceptance-tests-reference) +# for more information about how to configure these tests +connector_image: airbyte/source-pivotal-tracker:dev +tests: + spec: + - spec_path: "source_pivotal_tracker/spec.json" + connection: + - config_path: "secrets/config.json" + status: "succeed" + - config_path: "integration_tests/invalid_config.json" + status: "failed" + discovery: + - config_path: "secrets/config.json" + basic_read: + - config_path: "secrets/config.json" + configured_catalog_path: "integration_tests/configured_catalog.json" + empty_streams: [] +# TODO uncomment this block to specify that the tests should assert the connector outputs the records provided in the input file a file +# expect_records: +# path: "integration_tests/expected_records.txt" +# extra_fields: no +# exact_order: no +# extra_records: yes + incremental: # TODO if your connector does not implement incremental sync, remove this block + - config_path: "secrets/config.json" + configured_catalog_path: "integration_tests/configured_catalog.json" + future_state_path: "integration_tests/abnormal_state.json" + full_refresh: + - config_path: "secrets/config.json" + configured_catalog_path: "integration_tests/configured_catalog.json" diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-docker.sh b/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-docker.sh new file mode 100644 index 0000000000000..c51577d10690c --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-docker.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env sh + +# Build latest connector image +docker build . -t $(cat acceptance-test-config.yml | grep "connector_image" | head -n 1 | cut -d: -f2-) + +# Pull latest acctest image +docker pull airbyte/source-acceptance-test:latest + +# Run +docker run --rm -it \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v /tmp:/tmp \ + -v $(pwd):/test_input \ + airbyte/source-acceptance-test \ + --acceptance-test-config /test_input + diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/build.gradle b/airbyte-integrations/connectors/source-pivotal-tracker/build.gradle new file mode 100644 index 0000000000000..35e230419defc --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'airbyte-python' + id 'airbyte-docker' + id 'airbyte-source-acceptance-test' +} + +airbytePython { + moduleDirectory 'source_pivotal_tracker' +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/__init__.py b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/__init__.py new file mode 100644 index 0000000000000..46b7376756ec6 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/__init__.py @@ -0,0 +1,3 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/abnormal_state.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/abnormal_state.json new file mode 100644 index 0000000000000..52b0f2c2118f4 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/abnormal_state.json @@ -0,0 +1,5 @@ +{ + "todo-stream-name": { + "todo-field-name": "todo-abnormal-value" + } +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/acceptance.py b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/acceptance.py new file mode 100644 index 0000000000000..056971f954502 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/acceptance.py @@ -0,0 +1,16 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + + +import pytest + +pytest_plugins = ("source_acceptance_test.plugin",) + + +@pytest.fixture(scope="session", autouse=True) +def connector_setup(): + """This fixture is a placeholder for external resources that acceptance test might require.""" + # TODO: setup test dependencies if needed. otherwise remove the TODO comments + yield + # TODO: clean up test dependencies diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json new file mode 100644 index 0000000000000..6799946a68514 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json @@ -0,0 +1,39 @@ +{ + "streams": [ + { + "name": "TODO fix this file", + "supported_sync_modes": ["full_refresh", "incremental"], + "source_defined_cursor": true, + "default_cursor_field": "column1", + "json_schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "column1": { + "type": "string" + }, + "column2": { + "type": "number" + } + } + } + }, + { + "name": "table1", + "supported_sync_modes": ["full_refresh", "incremental"], + "source_defined_cursor": false, + "json_schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "column1": { + "type": "string" + }, + "column2": { + "type": "number" + } + } + } + } + ] +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json new file mode 100644 index 0000000000000..36f0468db0d8f --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json @@ -0,0 +1,22 @@ +{ + "streams": [ + { + "stream": { + "name": "customers", + "json_schema": {}, + "supported_sync_modes": ["full_refresh"] + }, + "sync_mode": "full_refresh", + "destination_sync_mode": "overwrite" + }, + { + "stream": { + "name": "employees", + "json_schema": {}, + "supported_sync_modes": ["full_refresh", "incremental"] + }, + "sync_mode": "incremental", + "destination_sync_mode": "append" + } + ] +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json new file mode 100644 index 0000000000000..f3732995784f2 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json @@ -0,0 +1,3 @@ +{ + "todo-wrong-field": "this should be an incomplete config file, used in standard tests" +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json new file mode 100644 index 0000000000000..ecc4913b84c74 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json @@ -0,0 +1,3 @@ +{ + "fix-me": "TODO" +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_state.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_state.json new file mode 100644 index 0000000000000..3587e579822d0 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_state.json @@ -0,0 +1,5 @@ +{ + "todo-stream-name": { + "todo-field-name": "value" + } +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/main.py b/airbyte-integrations/connectors/source-pivotal-tracker/main.py new file mode 100644 index 0000000000000..410935adc9d70 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/main.py @@ -0,0 +1,13 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + + +import sys + +from airbyte_cdk.entrypoint import launch +from source_pivotal_tracker import SourcePivotalTracker + +if __name__ == "__main__": + source = SourcePivotalTracker() + launch(source, sys.argv[1:]) diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/requirements.txt b/airbyte-integrations/connectors/source-pivotal-tracker/requirements.txt new file mode 100644 index 0000000000000..0411042aa0911 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/requirements.txt @@ -0,0 +1,2 @@ +-e ../../bases/source-acceptance-test +-e . diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/setup.py b/airbyte-integrations/connectors/source-pivotal-tracker/setup.py new file mode 100644 index 0000000000000..27643f6b05ea6 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/setup.py @@ -0,0 +1,29 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + + +from setuptools import find_packages, setup + +MAIN_REQUIREMENTS = [ + "airbyte-cdk~=0.1", +] + +TEST_REQUIREMENTS = [ + "pytest~=6.1", + "pytest-mock~=3.6.1", + "source-acceptance-test", +] + +setup( + name="source_pivotal_tracker", + description="Source implementation for Pivotal Tracker.", + author="Airbyte", + author_email="contact@airbyte.io", + packages=find_packages(), + install_requires=MAIN_REQUIREMENTS, + package_data={"": ["*.json", "schemas/*.json", "schemas/shared/*.json"]}, + extras_require={ + "tests": TEST_REQUIREMENTS, + }, +) diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/__init__.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/__init__.py new file mode 100644 index 0000000000000..8c72af827305e --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/__init__.py @@ -0,0 +1,8 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + + +from .source import SourcePivotalTracker + +__all__ = ["SourcePivotalTracker"] diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/TODO.md b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/TODO.md new file mode 100644 index 0000000000000..cf1efadb3c9c9 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/TODO.md @@ -0,0 +1,25 @@ +# TODO: Define your stream schemas +Your connector must describe the schema of each stream it can output using [JSONSchema](https://json-schema.org). + +The simplest way to do this is to describe the schema of your streams using one `.json` file per stream. You can also dynamically generate the schema of your stream in code, or you can combine both approaches: start with a `.json` file and dynamically add properties to it. + +The schema of a stream is the return value of `Stream.get_json_schema`. + +## Static schemas +By default, `Stream.get_json_schema` reads a `.json` file in the `schemas/` directory whose name is equal to the value of the `Stream.name` property. In turn `Stream.name` by default returns the name of the class in snake case. Therefore, if you have a class `class EmployeeBenefits(HttpStream)` the default behavior will look for a file called `schemas/employee_benefits.json`. You can override any of these behaviors as you need. + +Important note: any objects referenced via `$ref` should be placed in the `shared/` directory in their own `.json` files. + +## Dynamic schemas +If you'd rather define your schema in code, override `Stream.get_json_schema` in your stream class to return a `dict` describing the schema using [JSONSchema](https://json-schema.org). + +## Dynamically modifying static schemas +Override `Stream.get_json_schema` to run the default behavior, edit the returned value, then return the edited value: +``` +def get_json_schema(self): + schema = super().get_json_schema() + schema['dynamically_determined_property'] = "property" + return schema +``` + +Delete this file once you're done. Or don't. Up to you :) diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/customers.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/customers.json new file mode 100644 index 0000000000000..9a4b134858363 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/customers.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "signup_date": { + "type": ["null", "string"], + "format": "date-time" + } + } +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/employees.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/employees.json new file mode 100644 index 0000000000000..2fa01a0fa1ff9 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/employees.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "years_of_service": { + "type": ["null", "integer"] + }, + "start_date": { + "type": ["null", "string"], + "format": "date-time" + } + } +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py new file mode 100644 index 0000000000000..a69bcc3e938b6 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py @@ -0,0 +1,206 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + + +from abc import ABC +from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Tuple + +import requests +from airbyte_cdk.sources import AbstractSource +from airbyte_cdk.sources.streams import Stream +from airbyte_cdk.sources.streams.http import HttpStream +from airbyte_cdk.sources.streams.http.auth import TokenAuthenticator + +""" +TODO: Most comments in this class are instructive and should be deleted after the source is implemented. + +This file provides a stubbed example of how to use the Airbyte CDK to develop both a source connector which supports full refresh or and an +incremental syncs from an HTTP API. + +The various TODOs are both implementation hints and steps - fulfilling all the TODOs should be sufficient to implement one basic and one incremental +stream from a source. This pattern is the same one used by Airbyte internally to implement connectors. + +The approach here is not authoritative, and devs are free to use their own judgement. + +There are additional required TODOs in the files within the integration_tests folder and the spec.json file. +""" + + +# Basic full refresh stream +class PivotalTrackerStream(HttpStream, ABC): + """ + TODO remove this comment + + This class represents a stream output by the connector. + This is an abstract base class meant to contain all the common functionality at the API level e.g: the API base URL, pagination strategy, + parsing responses etc.. + + Each stream should extend this class (or another abstract subclass of it) to specify behavior unique to that stream. + + Typically for REST APIs each stream corresponds to a resource in the API. For example if the API + contains the endpoints + - GET v1/customers + - GET v1/employees + + then you should have three classes: + `class PivotalTrackerStream(HttpStream, ABC)` which is the current class + `class Customers(PivotalTrackerStream)` contains behavior to pull data for customers using v1/customers + `class Employees(PivotalTrackerStream)` contains behavior to pull data for employees using v1/employees + + If some streams implement incremental sync, it is typical to create another class + `class IncrementalPivotalTrackerStream((PivotalTrackerStream), ABC)` then have concrete stream implementations extend it. An example + is provided below. + + See the reference docs for the full list of configurable options. + """ + + # TODO: Fill in the url base. Required. + url_base = "https://example-api.com/v1/" + + def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]: + """ + TODO: Override this method to define a pagination strategy. If you will not be using pagination, no action is required - just return None. + + This method should return a Mapping (e.g: dict) containing whatever information required to make paginated requests. This dict is passed + to most other methods in this class to help you form headers, request bodies, query params, etc.. + + For example, if the API accepts a 'page' parameter to determine which page of the result to return, and a response from the API contains a + 'page' number, then this method should probably return a dict {'page': response.json()['page'] + 1} to increment the page count by 1. + The request_params method should then read the input next_page_token and set the 'page' param to next_page_token['page']. + + :param response: the most recent response from the API + :return If there is another page in the result, a mapping (e.g: dict) containing information needed to query the next page in the response. + If there are no more pages in the result, return None. + """ + return None + + def request_params( + self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, any] = None, next_page_token: Mapping[str, Any] = None + ) -> MutableMapping[str, Any]: + """ + TODO: Override this method to define any query parameters to be set. Remove this method if you don't need to define request params. + Usually contains common params e.g. pagination size etc. + """ + return {} + + def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]: + """ + TODO: Override this method to define how a response is parsed. + :return an iterable containing each record in the response + """ + yield {} + + +class Customers(PivotalTrackerStream): + """ + TODO: Change class name to match the table/data source this stream corresponds to. + """ + + # TODO: Fill in the primary key. Required. This is usually a unique field in the stream, like an ID or a timestamp. + primary_key = "customer_id" + + def path( + self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None + ) -> str: + """ + TODO: Override this method to define the path this stream corresponds to. E.g. if the url is https://example-api.com/v1/customers then this + should return "customers". Required. + """ + return "customers" + + +# Basic incremental stream +class IncrementalPivotalTrackerStream(PivotalTrackerStream, ABC): + """ + TODO fill in details of this class to implement functionality related to incremental syncs for your connector. + if you do not need to implement incremental sync for any streams, remove this class. + """ + + # TODO: Fill in to checkpoint stream reads after N records. This prevents re-reading of data if the stream fails for any reason. + state_checkpoint_interval = None + + @property + def cursor_field(self) -> str: + """ + TODO + Override to return the cursor field used by this stream e.g: an API entity might always use created_at as the cursor field. This is + usually id or date based. This field's presence tells the framework this in an incremental stream. Required for incremental. + + :return str: The name of the cursor field. + """ + return [] + + def get_updated_state(self, current_stream_state: MutableMapping[str, Any], latest_record: Mapping[str, Any]) -> Mapping[str, Any]: + """ + Override to determine the latest state after reading the latest record. This typically compared the cursor_field from the latest record and + the current state and picks the 'most' recent cursor. This is how a stream's state is determined. Required for incremental. + """ + return {} + + +class Employees(IncrementalPivotalTrackerStream): + """ + TODO: Change class name to match the table/data source this stream corresponds to. + """ + + # TODO: Fill in the cursor_field. Required. + cursor_field = "start_date" + + # TODO: Fill in the primary key. Required. This is usually a unique field in the stream, like an ID or a timestamp. + primary_key = "employee_id" + + def path(self, **kwargs) -> str: + """ + TODO: Override this method to define the path this stream corresponds to. E.g. if the url is https://example-api.com/v1/employees then this should + return "single". Required. + """ + return "employees" + + def stream_slices(self, stream_state: Mapping[str, Any] = None, **kwargs) -> Iterable[Optional[Mapping[str, any]]]: + """ + TODO: Optionally override this method to define this stream's slices. If slicing is not needed, delete this method. + + Slices control when state is saved. Specifically, state is saved after a slice has been fully read. + This is useful if the API offers reads by groups or filters, and can be paired with the state object to make reads efficient. See the "concepts" + section of the docs for more information. + + The function is called before reading any records in a stream. It returns an Iterable of dicts, each containing the + necessary data to craft a request for a slice. The stream state is usually referenced to determine what slices need to be created. + This means that data in a slice is usually closely related to a stream's cursor_field and stream_state. + + An HTTP request is made for each returned slice. The same slice can be accessed in the path, request_params and request_header functions to help + craft that specific request. + + For example, if https://example-api.com/v1/employees offers a date query params that returns data for that particular day, one way to implement + this would be to consult the stream state object for the last synced date, then return a slice containing each date from the last synced date + till now. The request_params function would then grab the date from the stream_slice and make it part of the request by injecting it into + the date query param. + """ + raise NotImplementedError("Implement stream slices or delete this method!") + + +# Source +class SourcePivotalTracker(AbstractSource): + def check_connection(self, logger, config) -> Tuple[bool, any]: + """ + TODO: Implement a connection check to validate that the user-provided config can be used to connect to the underlying API + + See https://github.com/airbytehq/airbyte/blob/master/airbyte-integrations/connectors/source-stripe/source_stripe/source.py#L232 + for an example. + + :param config: the user-input config object conforming to the connector's spec.json + :param logger: logger object + :return Tuple[bool, any]: (True, None) if the input config can be used to connect to the API successfully, (False, error) otherwise. + """ + return True, None + + def streams(self, config: Mapping[str, Any]) -> List[Stream]: + """ + TODO: Replace the streams below with your own streams. + + :param config: A Mapping of the user input configuration as defined in the connector spec. + """ + # TODO remove the authenticator if not required. + auth = TokenAuthenticator(token="api_key") # Oauth2Authenticator is also available if you need oauth support + return [Customers(authenticator=auth), Employees(authenticator=auth)] diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/spec.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/spec.json new file mode 100644 index 0000000000000..0f35b36a51bed --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/spec.json @@ -0,0 +1,16 @@ +{ + "documentationUrl": "https://docsurl.com", + "connectionSpecification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Pivotal Tracker Spec", + "type": "object", + "required": ["TODO"], + "additionalProperties": false, + "properties": { + "TODO: This schema defines the configuration required for the source. This usually involves metadata such as database and/or authentication information.": { + "type": "string", + "description": "describe me" + } + } + } +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/__init__.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/__init__.py new file mode 100644 index 0000000000000..46b7376756ec6 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/__init__.py @@ -0,0 +1,3 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_incremental_streams.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_incremental_streams.py new file mode 100644 index 0000000000000..662ee5170fa67 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_incremental_streams.py @@ -0,0 +1,59 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + + +from airbyte_cdk.models import SyncMode +from pytest import fixture +from source_pivotal_tracker.source import IncrementalPivotalTrackerStream + + +@fixture +def patch_incremental_base_class(mocker): + # Mock abstract methods to enable instantiating abstract class + mocker.patch.object(IncrementalPivotalTrackerStream, "path", "v0/example_endpoint") + mocker.patch.object(IncrementalPivotalTrackerStream, "primary_key", "test_primary_key") + mocker.patch.object(IncrementalPivotalTrackerStream, "__abstractmethods__", set()) + + +def test_cursor_field(patch_incremental_base_class): + stream = IncrementalPivotalTrackerStream() + # TODO: replace this with your expected cursor field + expected_cursor_field = [] + assert stream.cursor_field == expected_cursor_field + + +def test_get_updated_state(patch_incremental_base_class): + stream = IncrementalPivotalTrackerStream() + # TODO: replace this with your input parameters + inputs = {"current_stream_state": None, "latest_record": None} + # TODO: replace this with your expected updated stream state + expected_state = {} + assert stream.get_updated_state(**inputs) == expected_state + + +def test_stream_slices(patch_incremental_base_class): + stream = IncrementalPivotalTrackerStream() + # TODO: replace this with your input parameters + inputs = {"sync_mode": SyncMode.incremental, "cursor_field": [], "stream_state": {}} + # TODO: replace this with your expected stream slices list + expected_stream_slice = [None] + assert stream.stream_slices(**inputs) == expected_stream_slice + + +def test_supports_incremental(patch_incremental_base_class, mocker): + mocker.patch.object(IncrementalPivotalTrackerStream, "cursor_field", "dummy_field") + stream = IncrementalPivotalTrackerStream() + assert stream.supports_incremental + + +def test_source_defined_cursor(patch_incremental_base_class): + stream = IncrementalPivotalTrackerStream() + assert stream.source_defined_cursor + + +def test_stream_checkpoint_interval(patch_incremental_base_class): + stream = IncrementalPivotalTrackerStream() + # TODO: replace this with your expected checkpoint interval + expected_checkpoint_interval = None + assert stream.state_checkpoint_interval == expected_checkpoint_interval diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py new file mode 100644 index 0000000000000..83db74ebe150f --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py @@ -0,0 +1,22 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + +from unittest.mock import MagicMock + +from source_pivotal_tracker.source import SourcePivotalTracker + + +def test_check_connection(mocker): + source = SourcePivotalTracker() + logger_mock, config_mock = MagicMock(), MagicMock() + assert source.check_connection(logger_mock, config_mock) == (True, None) + + +def test_streams(mocker): + source = SourcePivotalTracker() + config_mock = MagicMock() + streams = source.streams(config_mock) + # TODO: replace this with your streams number + expected_streams_number = 2 + assert len(streams) == expected_streams_number diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py new file mode 100644 index 0000000000000..a32e6c134ca55 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py @@ -0,0 +1,83 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + +from http import HTTPStatus +from unittest.mock import MagicMock + +import pytest +from source_pivotal_tracker.source import PivotalTrackerStream + + +@pytest.fixture +def patch_base_class(mocker): + # Mock abstract methods to enable instantiating abstract class + mocker.patch.object(PivotalTrackerStream, "path", "v0/example_endpoint") + mocker.patch.object(PivotalTrackerStream, "primary_key", "test_primary_key") + mocker.patch.object(PivotalTrackerStream, "__abstractmethods__", set()) + + +def test_request_params(patch_base_class): + stream = PivotalTrackerStream() + # TODO: replace this with your input parameters + inputs = {"stream_slice": None, "stream_state": None, "next_page_token": None} + # TODO: replace this with your expected request parameters + expected_params = {} + assert stream.request_params(**inputs) == expected_params + + +def test_next_page_token(patch_base_class): + stream = PivotalTrackerStream() + # TODO: replace this with your input parameters + inputs = {"response": MagicMock()} + # TODO: replace this with your expected next page token + expected_token = None + assert stream.next_page_token(**inputs) == expected_token + + +def test_parse_response(patch_base_class): + stream = PivotalTrackerStream() + # TODO: replace this with your input parameters + inputs = {"response": MagicMock()} + # TODO: replace this with your expected parced object + expected_parsed_object = {} + assert next(stream.parse_response(**inputs)) == expected_parsed_object + + +def test_request_headers(patch_base_class): + stream = PivotalTrackerStream() + # TODO: replace this with your input parameters + inputs = {"stream_slice": None, "stream_state": None, "next_page_token": None} + # TODO: replace this with your expected request headers + expected_headers = {} + assert stream.request_headers(**inputs) == expected_headers + + +def test_http_method(patch_base_class): + stream = PivotalTrackerStream() + # TODO: replace this with your expected http request method + expected_method = "GET" + assert stream.http_method == expected_method + + +@pytest.mark.parametrize( + ("http_status", "should_retry"), + [ + (HTTPStatus.OK, False), + (HTTPStatus.BAD_REQUEST, False), + (HTTPStatus.TOO_MANY_REQUESTS, True), + (HTTPStatus.INTERNAL_SERVER_ERROR, True), + ], +) +def test_should_retry(patch_base_class, http_status, should_retry): + response_mock = MagicMock() + response_mock.status_code = http_status + stream = PivotalTrackerStream() + assert stream.should_retry(response_mock) == should_retry + + +def test_backoff_time(patch_base_class): + response_mock = MagicMock() + stream = PivotalTrackerStream() + expected_backoff_time = None + assert stream.backoff_time(response_mock) == expected_backoff_time From 8a037b63c2ac271bd56bb22ec0e9ce0b57295143 Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Wed, 9 Mar 2022 16:36:23 -0800 Subject: [PATCH 02/20] Projects stream --- .../integration_tests/configured_catalog.json | 11 +- .../integration_tests/invalid_config.json | 6 +- .../integration_tests/sample_config.json | 6 +- .../schemas/customers.json | 16 -- .../schemas/employees.json | 19 -- .../schemas/projects.json | 90 +++++++++ .../source_pivotal_tracker/source.py | 181 ++++-------------- .../source_pivotal_tracker/spec.json | 7 +- 8 files changed, 138 insertions(+), 198 deletions(-) delete mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/customers.json delete mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/employees.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json index 36f0468db0d8f..e111623b73a3d 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json @@ -2,21 +2,12 @@ "streams": [ { "stream": { - "name": "customers", + "name": "projects", "json_schema": {}, "supported_sync_modes": ["full_refresh"] }, "sync_mode": "full_refresh", "destination_sync_mode": "overwrite" - }, - { - "stream": { - "name": "employees", - "json_schema": {}, - "supported_sync_modes": ["full_refresh", "incremental"] - }, - "sync_mode": "incremental", - "destination_sync_mode": "append" } ] } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json index f3732995784f2..eb76800bde3ad 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json @@ -1,3 +1,3 @@ -{ - "todo-wrong-field": "this should be an incomplete config file, used in standard tests" -} +{ + "api_token": "5c054d0de3440452190fdc5d5a04d871" +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json index ecc4913b84c74..9761db59b865c 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json @@ -1,3 +1,3 @@ -{ - "fix-me": "TODO" -} +{ + "api_token": "" +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/customers.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/customers.json deleted file mode 100644 index 9a4b134858363..0000000000000 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/customers.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "id": { - "type": ["null", "string"] - }, - "name": { - "type": ["null", "string"] - }, - "signup_date": { - "type": ["null", "string"], - "format": "date-time" - } - } -} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/employees.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/employees.json deleted file mode 100644 index 2fa01a0fa1ff9..0000000000000 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/employees.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "id": { - "type": ["null", "string"] - }, - "name": { - "type": ["null", "string"] - }, - "years_of_service": { - "type": ["null", "integer"] - }, - "start_date": { - "type": ["null", "string"], - "format": "date-time" - } - } -} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json new file mode 100644 index 0000000000000..379f651aa5d26 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json @@ -0,0 +1,90 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "account_id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "version": { + "type": ["null", "integer"] + }, + "iteration_length": { + "type": ["null", "integer"] + }, + "current_iteration_number": { + "type": ["null", "integer"] + }, + "week_start_day": { + "type": ["null", "string"] + }, + "point_scale": { + "type": ["null", "string"] + }, + "point_scale_is_custom": { + "type": ["null", "boolean"] + }, + "bugs_and_chores_are_estimatable": { + "type": ["null", "boolean"] + }, + "automatic_planning": { + "type": ["null", "boolean"] + }, + "enable_tasks": { + "type": ["null", "boolean"] + }, + "time_zone": { + "type": ["null", "object"] + }, + "velocity_averaged_over": { + "type": ["null", "integer"] + }, + "number_of_done_iterations_to_show": { + "type": ["null", "integer"] + }, + "has_google_domain": { + "type": ["null", "boolean"] + }, + "enable_incoming_emails": { + "type": ["null", "boolean"] + }, + "initial_velocity": { + "type": ["null", "integer"] + }, + "public": { + "type": ["null", "boolean"] + }, + "atom_enabled": { + "type": ["null", "boolean"] + }, + "enable_following": { + "type": ["null", "boolean"] + }, + "show_story_priority": { + "type": ["null", "boolean"] + }, + "project_type": { + "type": ["null", "string"] + }, + "start_time": { + "type": ["null", "string"], + "format": "date-time" + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + } + } +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py index a69bcc3e938b6..9852444fa7d44 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py @@ -3,60 +3,21 @@ # -from abc import ABC +from abc import ABC, abstractmethod from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Tuple import requests from airbyte_cdk.sources import AbstractSource from airbyte_cdk.sources.streams import Stream from airbyte_cdk.sources.streams.http import HttpStream -from airbyte_cdk.sources.streams.http.auth import TokenAuthenticator +from airbyte_cdk.sources.streams.http.auth import HttpAuthenticator +from airbyte_cdk.models import SyncMode -""" -TODO: Most comments in this class are instructive and should be deleted after the source is implemented. -This file provides a stubbed example of how to use the Airbyte CDK to develop both a source connector which supports full refresh or and an -incremental syncs from an HTTP API. - -The various TODOs are both implementation hints and steps - fulfilling all the TODOs should be sufficient to implement one basic and one incremental -stream from a source. This pattern is the same one used by Airbyte internally to implement connectors. - -The approach here is not authoritative, and devs are free to use their own judgement. - -There are additional required TODOs in the files within the integration_tests folder and the spec.json file. -""" - - -# Basic full refresh stream class PivotalTrackerStream(HttpStream, ABC): - """ - TODO remove this comment - - This class represents a stream output by the connector. - This is an abstract base class meant to contain all the common functionality at the API level e.g: the API base URL, pagination strategy, - parsing responses etc.. - Each stream should extend this class (or another abstract subclass of it) to specify behavior unique to that stream. - - Typically for REST APIs each stream corresponds to a resource in the API. For example if the API - contains the endpoints - - GET v1/customers - - GET v1/employees - - then you should have three classes: - `class PivotalTrackerStream(HttpStream, ABC)` which is the current class - `class Customers(PivotalTrackerStream)` contains behavior to pull data for customers using v1/customers - `class Employees(PivotalTrackerStream)` contains behavior to pull data for employees using v1/employees - - If some streams implement incremental sync, it is typical to create another class - `class IncrementalPivotalTrackerStream((PivotalTrackerStream), ABC)` then have concrete stream implementations extend it. An example - is provided below. - - See the reference docs for the full list of configurable options. - """ - - # TODO: Fill in the url base. Required. - url_base = "https://example-api.com/v1/" + url_base = "https://www.pivotaltracker.com/services/v5/" + primary_key = "id" def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]: """ @@ -85,122 +46,54 @@ def request_params( return {} def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]: - """ - TODO: Override this method to define how a response is parsed. - :return an iterable containing each record in the response - """ - yield {} - - -class Customers(PivotalTrackerStream): - """ - TODO: Change class name to match the table/data source this stream corresponds to. - """ + for record in response.json(): # everything is in a list + yield record - # TODO: Fill in the primary key. Required. This is usually a unique field in the stream, like an ID or a timestamp. - primary_key = "customer_id" +class Projects(PivotalTrackerStream): def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: - """ - TODO: Override this method to define the path this stream corresponds to. E.g. if the url is https://example-api.com/v1/customers then this - should return "customers". Required. - """ - return "customers" + return "projects" -# Basic incremental stream -class IncrementalPivotalTrackerStream(PivotalTrackerStream, ABC): - """ - TODO fill in details of this class to implement functionality related to incremental syncs for your connector. - if you do not need to implement incremental sync for any streams, remove this class. - """ +# Custom token authenticator because no "Bearer" +class PivotalAuthenticator(HttpAuthenticator): + def __init__(self, token: str): + self._token = token - # TODO: Fill in to checkpoint stream reads after N records. This prevents re-reading of data if the stream fails for any reason. - state_checkpoint_interval = None + def get_auth_header(self) -> Mapping[str, Any]: + return {"X-TrackerToken": self._token} - @property - def cursor_field(self) -> str: - """ - TODO - Override to return the cursor field used by this stream e.g: an API entity might always use created_at as the cursor field. This is - usually id or date based. This field's presence tells the framework this in an incremental stream. Required for incremental. - :return str: The name of the cursor field. - """ - return [] - - def get_updated_state(self, current_stream_state: MutableMapping[str, Any], latest_record: Mapping[str, Any]) -> Mapping[str, Any]: - """ - Override to determine the latest state after reading the latest record. This typically compared the cursor_field from the latest record and - the current state and picks the 'most' recent cursor. This is how a stream's state is determined. Required for incremental. - """ - return {} - - -class Employees(IncrementalPivotalTrackerStream): - """ - TODO: Change class name to match the table/data source this stream corresponds to. - """ - - # TODO: Fill in the cursor_field. Required. - cursor_field = "start_date" - - # TODO: Fill in the primary key. Required. This is usually a unique field in the stream, like an ID or a timestamp. - primary_key = "employee_id" - - def path(self, **kwargs) -> str: - """ - TODO: Override this method to define the path this stream corresponds to. E.g. if the url is https://example-api.com/v1/employees then this should - return "single". Required. - """ - return "employees" +# Source +class SourcePivotalTracker(AbstractSource): + @staticmethod + def _get_authenticator(config: Mapping[str, Any]) -> HttpAuthenticator: + token = config.get("api_token") + return PivotalAuthenticator(token) - def stream_slices(self, stream_state: Mapping[str, Any] = None, **kwargs) -> Iterable[Optional[Mapping[str, any]]]: + @staticmethod + def _generate_project_ids(auth: HttpAuthenticator) -> List[str]: """ - TODO: Optionally override this method to define this stream's slices. If slicing is not needed, delete this method. - - Slices control when state is saved. Specifically, state is saved after a slice has been fully read. - This is useful if the API offers reads by groups or filters, and can be paired with the state object to make reads efficient. See the "concepts" - section of the docs for more information. - - The function is called before reading any records in a stream. It returns an Iterable of dicts, each containing the - necessary data to craft a request for a slice. The stream state is usually referenced to determine what slices need to be created. - This means that data in a slice is usually closely related to a stream's cursor_field and stream_state. - - An HTTP request is made for each returned slice. The same slice can be accessed in the path, request_params and request_header functions to help - craft that specific request. - - For example, if https://example-api.com/v1/employees offers a date query params that returns data for that particular day, one way to implement - this would be to consult the stream state object for the last synced date, then return a slice containing each date from the last synced date - till now. The request_params function would then grab the date from the stream_slice and make it part of the request by injecting it into - the date query param. + Args: + config (dict): Dict representing connector's config + Returns: + List[str]: List of project ids accessible by the api_token """ - raise NotImplementedError("Implement stream slices or delete this method!") + projects = Projects(authenticator=auth) + records = projects.read_records(SyncMode.full_refresh) + project_ids: List[str] = [] + for record in records: + project_ids.append(record["id"]) + return project_ids -# Source -class SourcePivotalTracker(AbstractSource): def check_connection(self, logger, config) -> Tuple[bool, any]: - """ - TODO: Implement a connection check to validate that the user-provided config can be used to connect to the underlying API - - See https://github.com/airbytehq/airbyte/blob/master/airbyte-integrations/connectors/source-stripe/source_stripe/source.py#L232 - for an example. - - :param config: the user-input config object conforming to the connector's spec.json - :param logger: logger object - :return Tuple[bool, any]: (True, None) if the input config can be used to connect to the API successfully, (False, error) otherwise. - """ + auth = SourcePivotalTracker._get_authenticator(config) + self._generate_project_ids(auth) return True, None def streams(self, config: Mapping[str, Any]) -> List[Stream]: - """ - TODO: Replace the streams below with your own streams. - - :param config: A Mapping of the user input configuration as defined in the connector spec. - """ - # TODO remove the authenticator if not required. - auth = TokenAuthenticator(token="api_key") # Oauth2Authenticator is also available if you need oauth support - return [Customers(authenticator=auth), Employees(authenticator=auth)] + auth = self._get_authenticator(config) + return [Projects(authenticator=auth)] diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/spec.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/spec.json index 0f35b36a51bed..40eaea4381092 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/spec.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/spec.json @@ -4,12 +4,13 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Pivotal Tracker Spec", "type": "object", - "required": ["TODO"], + "required": ["api_token"], "additionalProperties": false, "properties": { - "TODO: This schema defines the configuration required for the source. This usually involves metadata such as database and/or authentication information.": { + "api_token": { "type": "string", - "description": "describe me" + "description": "Pivotal Tracker API token", + "examples": ["5c054d0de3440452190fdc5d5a04d871"] } } } From 4f0720aa5bc55e764a8b81f2da1a60a1f38b1a0f Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Wed, 9 Mar 2022 17:01:08 -0800 Subject: [PATCH 03/20] Adds tracker stories stream --- .../integration_tests/configured_catalog.json | 9 ++ .../schemas/stories.json | 85 +++++++++++++++++++ .../source_pivotal_tracker/source.py | 32 ++++++- 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json index e111623b73a3d..681c56106554e 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json @@ -8,6 +8,15 @@ }, "sync_mode": "full_refresh", "destination_sync_mode": "overwrite" + }, + { + "stream": { + "name": "stories", + "json_schema": {}, + "supported_sync_modes": ["full_refresh"] + }, + "sync_mode": "full_refresh", + "destination_sync_mode": "overwrite" } ] } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json new file mode 100644 index 0000000000000..2bd7821caa116 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json @@ -0,0 +1,85 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "project_id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "story_type": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "estimate": { + "type": ["null", "integer"] + }, + "story_priority": { + "type": ["null", "string"] + }, + "current_state": { + "type": ["null", "string"] + }, + "requested_by_id": { + "type": ["null", "integer"] + }, + "owned_by_id": { + "type": ["null", "integer"] + }, + "url": { + "type": ["null", "string"] + }, + "owner_ids": { + "type": "array", + "items": { + "type": "integer" + } + }, + "labels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "project_id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + } + } + } + }, + "accepted_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + } + } +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py index 9852444fa7d44..a5caad6572eab 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py @@ -46,6 +46,7 @@ def request_params( return {} def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]: + # print(response.json()) for record in response.json(): # everything is in a list yield record @@ -57,6 +58,34 @@ def path( return "projects" +class ProjectBasedStream(PivotalTrackerStream): + @property + @abstractmethod + def subpath(self) -> str: + """ + Within the project. For example, "stories" producing: + https://www.pivotaltracker.com/services/v5/projects/{project_id}/stories + """ + + def __init__(self, project_ids: List[str], **kwargs): + super().__init__(**kwargs) + self.project_ids = project_ids + + def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str: + return f"projects/{stream_slice['project_id']}/{self.subpath}" + + def stream_slices(self, stream_state: Mapping[str, Any] = None, **kwargs) -> Iterable[Optional[Mapping[str, any]]]: + for project_id in self.project_ids: + yield {"project_id": project_id} + + +class Stories(ProjectBasedStream): + + # TODO: cursor_field = "updated_at" + # TODO: get_updated_state + subpath = "stories" + + # Custom token authenticator because no "Bearer" class PivotalAuthenticator(HttpAuthenticator): def __init__(self, token: str): @@ -96,4 +125,5 @@ def check_connection(self, logger, config) -> Tuple[bool, any]: def streams(self, config: Mapping[str, Any]) -> List[Stream]: auth = self._get_authenticator(config) - return [Projects(authenticator=auth)] + project_ids = self._generate_project_ids(auth) + return [Projects(authenticator=auth), Stories(project_ids, authenticator=auth)] From 47a80b77cb82afbabf2cee3b63c6a04cebee32f5 Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Wed, 9 Mar 2022 17:26:42 -0800 Subject: [PATCH 04/20] Story pagination --- .../source_pivotal_tracker/source.py | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py index a5caad6572eab..91d98298a1b85 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py @@ -20,30 +20,27 @@ class PivotalTrackerStream(HttpStream, ABC): primary_key = "id" def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]: - """ - TODO: Override this method to define a pagination strategy. If you will not be using pagination, no action is required - just return None. - This method should return a Mapping (e.g: dict) containing whatever information required to make paginated requests. This dict is passed - to most other methods in this class to help you form headers, request bodies, query params, etc.. + headers = response.headers + if not "X-Tracker-Pagination-Total" in headers: + return None # not paginating - For example, if the API accepts a 'page' parameter to determine which page of the result to return, and a response from the API contains a - 'page' number, then this method should probably return a dict {'page': response.json()['page'] + 1} to increment the page count by 1. - The request_params method should then read the input next_page_token and set the 'page' param to next_page_token['page']. + page_size = int(headers["X-Tracker-Pagination-Limit"]) + records_returned = int(headers["X-Tracker-Pagination-Returned"]) + current_offset = int(headers["X-Tracker-Pagination-Offset"]) - :param response: the most recent response from the API - :return If there is another page in the result, a mapping (e.g: dict) containing information needed to query the next page in the response. - If there are no more pages in the result, return None. - """ - return None + if records_returned < page_size: + return None # no more + + return {"offset": current_offset + page_size} def request_params( self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, any] = None, next_page_token: Mapping[str, Any] = None ) -> MutableMapping[str, Any]: - """ - TODO: Override this method to define any query parameters to be set. Remove this method if you don't need to define request params. - Usually contains common params e.g. pagination size etc. - """ - return {} + params: MutableMapping[str, Any] = {} + if next_page_token: + params["offset"] = next_page_token["offset"] + return params def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]: # print(response.json()) @@ -80,9 +77,6 @@ def stream_slices(self, stream_state: Mapping[str, Any] = None, **kwargs) -> Ite class Stories(ProjectBasedStream): - - # TODO: cursor_field = "updated_at" - # TODO: get_updated_state subpath = "stories" From a9a229590e3a79f7c5e58ba9b7d6f96c1f17c721 Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Thu, 10 Mar 2022 10:46:22 -0800 Subject: [PATCH 05/20] labels --- .../integration_tests/configured_catalog.json | 9 +++++++ .../schemas/labels.json | 26 +++++++++++++++++++ .../source_pivotal_tracker/source.py | 7 ++++- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/labels.json diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json index 681c56106554e..1b4bebd4e4d47 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json @@ -17,6 +17,15 @@ }, "sync_mode": "full_refresh", "destination_sync_mode": "overwrite" + }, + { + "stream": { + "name": "labels", + "json_schema": {}, + "supported_sync_modes": ["full_refresh"] + }, + "sync_mode": "full_refresh", + "destination_sync_mode": "overwrite" } ] } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/labels.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/labels.json new file mode 100644 index 0000000000000..d24b179a44f74 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/labels.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "project_id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + } + } +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py index 91d98298a1b85..dbdc58a3efb95 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py @@ -80,6 +80,10 @@ class Stories(ProjectBasedStream): subpath = "stories" +class Labels(ProjectBasedStream): + subpath = "labels" + + # Custom token authenticator because no "Bearer" class PivotalAuthenticator(HttpAuthenticator): def __init__(self, token: str): @@ -120,4 +124,5 @@ def check_connection(self, logger, config) -> Tuple[bool, any]: def streams(self, config: Mapping[str, Any]) -> List[Stream]: auth = self._get_authenticator(config) project_ids = self._generate_project_ids(auth) - return [Projects(authenticator=auth), Stories(project_ids, authenticator=auth)] + project_args = {"project_ids": project_ids, "authenticator": auth} + return [Projects(authenticator=auth), Stories(**project_args), Labels(**project_args)] From b5fbc97bf5428b4b4d256cb1f16de2b6eb814a33 Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Thu, 10 Mar 2022 10:53:36 -0800 Subject: [PATCH 06/20] Releases --- .../integration_tests/configured_catalog.json | 9 +++ .../schemas/releases.json | 68 +++++++++++++++++++ .../source_pivotal_tracker/source.py | 6 +- 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json index 1b4bebd4e4d47..349c89c572c34 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json @@ -26,6 +26,15 @@ }, "sync_mode": "full_refresh", "destination_sync_mode": "overwrite" + }, + { + "stream": { + "name": "releases", + "json_schema": {}, + "supported_sync_modes": ["full_refresh"] + }, + "sync_mode": "full_refresh", + "destination_sync_mode": "overwrite" } ] } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json new file mode 100644 index 0000000000000..e94a050ee5614 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json @@ -0,0 +1,68 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "project_id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "current_state": { + "type": ["null", "string"] + }, + "url": { + "type": ["null", "string"] + }, + "deadline": { + "type": ["null", "string"], + "format": "date-time" + }, + "labels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "project_id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + } + } + } + }, + "accepted_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + } + } +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py index dbdc58a3efb95..56bb8b9fb1211 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py @@ -84,6 +84,10 @@ class Labels(ProjectBasedStream): subpath = "labels" +class Releases(ProjectBasedStream): + subpath = "releases" + + # Custom token authenticator because no "Bearer" class PivotalAuthenticator(HttpAuthenticator): def __init__(self, token: str): @@ -125,4 +129,4 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]: auth = self._get_authenticator(config) project_ids = self._generate_project_ids(auth) project_args = {"project_ids": project_ids, "authenticator": auth} - return [Projects(authenticator=auth), Stories(**project_args), Labels(**project_args)] + return [Projects(authenticator=auth), Stories(**project_args), Labels(**project_args), Releases(**project_args)] From 585b79a460366f6943ffab23f29381938c6059ff Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Thu, 10 Mar 2022 13:58:40 -0800 Subject: [PATCH 07/20] Shared schema with allOf --- .../schemas/labels.json | 19 +------- .../schemas/projects.json | 16 +------ .../schemas/releases.json | 45 ++---------------- .../schemas/shared/label.json | 8 ++++ .../schemas/shared/object.json | 19 ++++++++ .../schemas/shared/project.json | 8 ++++ .../schemas/stories.json | 47 ++----------------- 7 files changed, 44 insertions(+), 118 deletions(-) create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/object.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/project.json diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/labels.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/labels.json index d24b179a44f74..b478d54feb89a 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/labels.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/labels.json @@ -1,26 +1,9 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", + "allOf": [{ "$ref": "project.json" }], "properties": { - "id": { - "type": ["null", "integer"] - }, - "project_id": { - "type": ["null", "integer"] - }, - "kind": { - "type": ["null", "string"] - }, "name": { "type": ["null", "string"] - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" } } } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json index 379f651aa5d26..10e35f3c59e14 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json @@ -1,16 +1,10 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", + "allOf": [{ "$ref": "object.json" }], "properties": { - "id": { - "type": ["null", "integer"] - }, "account_id": { "type": ["null", "integer"] }, - "kind": { - "type": ["null", "string"] - }, "name": { "type": ["null", "string"] }, @@ -77,14 +71,6 @@ "start_time": { "type": ["null", "string"], "format": "date-time" - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" } } } \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json index e94a050ee5614..edd7f24da1e36 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json @@ -1,16 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", + "allOf": [{ "$ref": "project.json" }], "properties": { - "id": { - "type": ["null", "integer"] - }, - "project_id": { - "type": ["null", "integer"] - }, - "kind": { - "type": ["null", "string"] - }, "name": { "type": ["null", "string"] }, @@ -27,42 +18,12 @@ "labels": { "type": "array", "items": { - "type": "object", - "properties": { - "id": { - "type": ["null", "integer"] - }, - "project_id": { - "type": ["null", "integer"] - }, - "kind": { - "type": ["null", "string"] - }, - "name": { - "type": ["null", "string"] - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - } - } - } + "$ref": "label.json" + } }, "accepted_at": { "type": ["null", "string"], "format": "date-time" - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" } } } \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json new file mode 100644 index 0000000000000..937794689153a --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json @@ -0,0 +1,8 @@ +{ + "allOf": [{ "$ref": "project.json" }], + "properties": { + "name": { + "type": ["null", "string"] + } + } +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/object.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/object.json new file mode 100644 index 0000000000000..283b95337e7bd --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/object.json @@ -0,0 +1,19 @@ +{ + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + } + } +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/project.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/project.json new file mode 100644 index 0000000000000..465914b7669e7 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/project.json @@ -0,0 +1,8 @@ +{ + "allOf": [{ "$ref": "object.json" }], + "properties": { + "project_id": { + "type": ["null", "integer"] + } + } +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json index 2bd7821caa116..3c99e35867fae 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json @@ -1,22 +1,13 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", + "allOf": [{ "$ref": "project.json" }], "properties": { - "id": { - "type": ["null", "integer"] - }, - "project_id": { - "type": ["null", "integer"] - }, - "kind": { + "name": { "type": ["null", "string"] }, "story_type": { "type": ["null", "string"] }, - "name": { - "type": ["null", "string"] - }, "estimate": { "type": ["null", "integer"] }, @@ -44,42 +35,12 @@ "labels": { "type": "array", "items": { - "type": "object", - "properties": { - "id": { - "type": ["null", "integer"] - }, - "project_id": { - "type": ["null", "integer"] - }, - "kind": { - "type": ["null", "string"] - }, - "name": { - "type": ["null", "string"] - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - } - } - } + "$ref": "label.json" + } }, "accepted_at": { "type": ["null", "string"], "format": "date-time" - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" } } } From 2845d241a256165776f408f3b373f4bb23af4699 Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Thu, 10 Mar 2022 15:57:00 -0800 Subject: [PATCH 08/20] =?UTF-8?q?Top=20level=20$ref=20didn=E2=80=99t=20wor?= =?UTF-8?q?k?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../source_pivotal_tracker/schemas.zip | Bin 0 -> 4850 bytes .../source_pivotal_tracker/schemas/labels.json | 7 +------ .../source_pivotal_tracker/schemas/projects.json | 2 +- .../source_pivotal_tracker/schemas/releases.json | 2 +- .../schemas/shared/label.json | 2 +- .../schemas/shared/project.json | 2 +- .../source_pivotal_tracker/schemas/stories.json | 2 +- 7 files changed, 6 insertions(+), 11 deletions(-) create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas.zip diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas.zip b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas.zip new file mode 100644 index 0000000000000000000000000000000000000000..195c2fae9a8298158180c9249b1667f590d90808 GIT binary patch literal 4850 zcmd5}iV|&z z3fZ?a4r!HC)==Wq_r5aCo1^bKeSe+X^?Jtjj(dN8_x&Rh;9MveC^UO->xB=Czj$EM zuwZvjil1AsijDOu7$Tp5A+w(S2Uo&4xTZN^u!a97E@AQBKf7bL<1_W*u^zOthTJIv0774T*aBp1GJWQuRFvUhNR|3Vnr zaTu~Q@POx%FfP3&5s4)9t`^f~bL&zPv2ha`eYN9{R!mE4=dswggQDl&okwjfOnW1d z@y@U=HH}*R`8cW5AS*~Hg%mtx5SCiuT?u%F;8ExkNyPzvfWEN3+yL=16b^%-AYKMj z1A@FL5H}M~V#rQ_i{QD$&2z~${!yyP=rLXH$pc4A`xy5cHS$qNbK&HsEm;M}LtJ*? z|G;}nuDdxubE=ir>X1`g<`b{Q!yMY@Q9f6sqkZ;q>L_yBhEVWwa_|PecMVG2go&$9 z)^@ACZF&xGswhOD<{w#YR)1Y=JdAJe%x^hQ4HEF!q$u~~TQMIi-7rFf%)~1ycO@OD ziijHh6q5Rk=_N-(N1{j7?k6XWE>s?vKOG^gFfUd+#q2@2Jf5M8s^)%{nlkmCYqK<~ z8L}k`7~MC#R+k)DF>-s@N&c2~;rv`>CpyqzI?t!Ke>VR5%@d<7GFdD9=2u02o!VZ~ z($c1?-WJWhsdhbCt#0nTZ{xj%EN7ew>f!=g8qDRlECcC=`8%+C0sNaEnq^P7Ad1Jg z>)~pSA-e$&crHmWyJUHRUf(ZS=!zeeSz8!cDEoOVG?kw`n(Ple;JHLunzeaApDOb1 zoSnc3P0rx;0Q^14qxx2DkZFk6@eS!pI<}h$IdTf)RZ{53vzz{0wUSe=y#CS0^=41* zzua@zp+>@oBCw)w+jvdBXN}#e^80(MKJBK&)nM!qeq(UcsWhvNJ3LjnBa1n3-8+Xd z1{;w~J?dfFz~-`;^J9I@34X|k98DSOk^LbbUlh;kl3(?Vh-!QE%GMT@cbYzkC(~T9 zjzxvKZKrYv_U@CZK3|Yd71p*x=h;f+*P`VRLZV)LBfPbz48pI7v|HfW>)95 zF9osBtR{reE|wn>@IGxl_c-&{I8Plr{rlwmmGb?&GifGkT4g=?-()B23K(`^Uz_9V zL!`7NjWYN48umGe9_4lQt?tgSzLz+W<8;yN;>8Q|Sj*db?I$=7xrZz93g{?(df!!P ztd(jPCz+5657)38nejT*^RhbU^^lB&PKv=c!{5qGhUPr7dq3RTxkG=g2H#z$>({nu zXn8#}OSZPzR7jxRoF|)q9V%&1uB#jIDYl-jhJEoir0Mm(&TY!{2pCwGvN4ZAn7DJP zfBit@#wzs`Exn=YVJf$SB}zQM=CpTk!Y${^YK7^H2#%+nqfMd#na!f2ZWd)H=JqIT z$@97RpCC=Yf%ln-{)OjHXJoyJ<4Gcw8^=vVGX~?;1H4|pSgVz6@zH;^Nbw^&Fhl603+4j`x2`6w3f%~!i%>lt$0=S0X2McRXDZ4CpiT;HHMjQg&Q9zu+ z5K!3{6yQy9r$Vqh3cze|#DM1#G&p0^=g&|@9v-ukJ|MMMXRe$SL9ua=7c-S4zsiW; zbLq>RPRcOuiJ+3MH2q~TIX63F=yXw0guG^3?DTFM{-Vw&&&LzBMhR2t=k%{fo*9k! zOD~m~5Ek-~cfWXDGhF*_b6%+`uZ7%Obnj#tPBFS=Vy*C@IgzppIr61r`unpDFND~P zoF!S1p604MAK+da$iUf?Ca!%w8n(e~z}vAZ*G|Z~c1Irb-BtSi<_)QHhtD4Wz_kA} ztVp(9Rt{(BIdR=N=Jz=N4TtLZGndty3PFx)yzmq`+DbNwB*l% z!lzaEZnwmi*^oPQGB_V*5L^sN!Xn2*!-+c+ybJ|G}$7u4^b>UIaTqy_Bub?Vg$nM&G z0?Nai5m<49-m$0mN1fGmL~fs()o(B8__C828!-7{k{@jA{rsY{g}`(H%gt_UXd{4a zExmkO|GX#uh0QGuX>K%dk)h2UMDeA#1w$8;lZ(v_o=cj$nApsBUln<84ka7bpRHMA zCz!-n3&rRiuT%to&!|mbZkK@RJeu#`jo&#xO1S(0-a9UjN)$&N zloQXNarj7mi^?#J(mJsj8zXZag73UAw9yEB3WBLcdn&i?k%^sl9o#X}AA{wE3n|2GY_v zpuw|FET;AAja{gi@{Iw3)*1pjDsc@}jBSq&7u!8&`d)SLf*QrT!`vM)nt?PSn1c(rfg&idvQY25{t@z)gTwn$4AZ>xtfQSvAOM?8N zM3_yDt&kcuz;Jw|6L>YN6ZFY$1Hn9dXR<#g8}aItwFvD~J3jQBwl-de5C32zE;Do( zf6bNqhLg0@%7yw6pIwOu=>g~;k-));g8!V>0bf~=5iEGH>h%LD1Bu=e$5~ogG!IIh zK$^#r5xOz(@%wE4$D|LcA0%K<(gPAOmN4i@4z}07K*yj4K<)(H_d)K&5(d2#V&GSp z6w3mrX*u4&vM_=b12_gN@rxH_A^-oAMSQC&n17H*K=&k&l(2+Br$Cf1w-7DEDp;Pg zltYUPUTT&w=()iB`d(aC2>*ETg4zLRA9N~$bAlxdx*vj_p`VYxWmcl4CZM5! Date: Thu, 10 Mar 2022 16:11:32 -0800 Subject: [PATCH 09/20] Be more verbose instead of $ref --- .../schemas/projects.json | 21 +++++++++++++++++-- .../schemas/releases.json | 19 ++++++++++++++++- .../schemas/shared/label.json | 19 ++++++++++++++++- .../schemas/shared/object.json | 19 ----------------- .../schemas/shared/project.json | 8 ------- .../schemas/stories.json | 19 ++++++++++++++++- 6 files changed, 73 insertions(+), 32 deletions(-) delete mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/object.json delete mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/project.json diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json index 493935449606f..dbdede7b14140 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json @@ -1,13 +1,30 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "object.json", + "type": "object", "properties": { - "account_id": { + "id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "project_id": { "type": ["null", "integer"] }, "name": { "type": ["null", "string"] }, + "account_id": { + "type": ["null", "integer"] + }, "version": { "type": ["null", "integer"] }, diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json index 350a0f1c2a881..710e059e46fd8 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json @@ -1,7 +1,24 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "project.json", + "type": "object", "properties": { + "id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "project_id": { + "type": ["null", "integer"] + }, "name": { "type": ["null", "string"] }, diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json index 5708f414eff66..f9237b06756ca 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json @@ -1,6 +1,23 @@ { - "$ref": "project.json", + "type": "object", "properties": { + "id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "project_id": { + "type": ["null", "integer"] + }, "name": { "type": ["null", "string"] } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/object.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/object.json deleted file mode 100644 index 283b95337e7bd..0000000000000 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/object.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "object", - "properties": { - "id": { - "type": ["null", "integer"] - }, - "kind": { - "type": ["null", "string"] - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - } - } -} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/project.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/project.json deleted file mode 100644 index 531e116b2a479..0000000000000 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$ref": "object.json", - "properties": { - "project_id": { - "type": ["null", "integer"] - } - } -} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json index 5ed25a6a0affa..c97c47dc4a98d 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json @@ -1,7 +1,24 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "project.json", + "type": "object", "properties": { + "id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "project_id": { + "type": ["null", "integer"] + }, "name": { "type": ["null", "string"] }, From 542004a26d829c6efbb13ad2d88a8f86ae7fcfae Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Thu, 10 Mar 2022 16:21:36 -0800 Subject: [PATCH 10/20] Project memberships --- .../integration_tests/configured_catalog.json | 9 ++++ .../schemas/project_memberships.json | 41 +++++++++++++++++++ .../schemas/shared/person.json | 23 +++++++++++ .../source_pivotal_tracker/source.py | 12 +++++- 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/project_memberships.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/person.json diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json index 349c89c572c34..591b5aa3579be 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json @@ -35,6 +35,15 @@ }, "sync_mode": "full_refresh", "destination_sync_mode": "overwrite" + }, + { + "stream": { + "name": "project_memberships", + "json_schema": {}, + "supported_sync_modes": ["full_refresh"] + }, + "sync_mode": "full_refresh", + "destination_sync_mode": "overwrite" } ] } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/project_memberships.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/project_memberships.json new file mode 100644 index 0000000000000..39adc63c16623 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/project_memberships.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "project_id": { + "type": ["null", "integer"] + }, + "favorite": { + "type": ["null", "boolean"] + }, + "project_color": { + "type": ["null", "string"] + }, + "role": { + "type": ["null", "string"] + }, + "wants_comment_notification_emails": { + "type": ["null", "boolean"] + }, + "will_receive_mention_notifications_or_emails": { + "type": ["null", "boolean"] + }, + "person": { + "$ref": "person.json" + } + } +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/person.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/person.json new file mode 100644 index 0000000000000..7b1cc94867552 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/person.json @@ -0,0 +1,23 @@ +{ + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "initials": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "username": { + "type": ["null", "string"] + } + } +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py index 56bb8b9fb1211..35246e3ad729f 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py @@ -88,6 +88,10 @@ class Releases(ProjectBasedStream): subpath = "releases" +class ProjectMemberships(ProjectBasedStream): + subpath = "memberships" + + # Custom token authenticator because no "Bearer" class PivotalAuthenticator(HttpAuthenticator): def __init__(self, token: str): @@ -129,4 +133,10 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]: auth = self._get_authenticator(config) project_ids = self._generate_project_ids(auth) project_args = {"project_ids": project_ids, "authenticator": auth} - return [Projects(authenticator=auth), Stories(**project_args), Labels(**project_args), Releases(**project_args)] + return [ + Projects(authenticator=auth), + Stories(**project_args), + Labels(**project_args), + ProjectMemberships(**project_args), + Releases(**project_args), + ] From 573f22a3346a7e632957ff5e3de387d79d04a947 Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Thu, 10 Mar 2022 16:27:04 -0800 Subject: [PATCH 11/20] epics --- .../integration_tests/configured_catalog.json | 9 ++++++ .../source_pivotal_tracker/schemas/epics.json | 32 +++++++++++++++++++ .../source_pivotal_tracker/source.py | 11 +++++-- 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/epics.json diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json index 591b5aa3579be..fa5fc528fb15c 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json @@ -44,6 +44,15 @@ }, "sync_mode": "full_refresh", "destination_sync_mode": "overwrite" + }, + { + "stream": { + "name": "epics", + "json_schema": {}, + "supported_sync_modes": ["full_refresh"] + }, + "sync_mode": "full_refresh", + "destination_sync_mode": "overwrite" } ] } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/epics.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/epics.json new file mode 100644 index 0000000000000..6ad1f09e609f2 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/epics.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "project_id": { + "type": ["null", "integer"] + }, + "name": { + "type": ["null", "string"] + }, + "url": { + "type": ["null", "string"] + }, + "label": { + "$ref": "label.json" + } + } +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py index 35246e3ad729f..499b1ee131498 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py @@ -80,6 +80,10 @@ class Stories(ProjectBasedStream): subpath = "stories" +class ProjectMemberships(ProjectBasedStream): + subpath = "memberships" + + class Labels(ProjectBasedStream): subpath = "labels" @@ -88,8 +92,8 @@ class Releases(ProjectBasedStream): subpath = "releases" -class ProjectMemberships(ProjectBasedStream): - subpath = "memberships" +class Epics(ProjectBasedStream): + subpath = "epics" # Custom token authenticator because no "Bearer" @@ -136,7 +140,8 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]: return [ Projects(authenticator=auth), Stories(**project_args), - Labels(**project_args), ProjectMemberships(**project_args), + Labels(**project_args), Releases(**project_args), + Epics(**project_args), ] From 5a154e7c329c99bd7444dfdcdb2f32e8dee8dc0c Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Thu, 10 Mar 2022 17:04:23 -0800 Subject: [PATCH 12/20] activity --- .../integration_tests/configured_catalog.json | 9 +++ .../schemas/activity.json | 68 +++++++++++++++++++ .../source_pivotal_tracker/source.py | 12 ++++ 3 files changed, 89 insertions(+) create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/activity.json diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json index fa5fc528fb15c..015b0050a2efb 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/configured_catalog.json @@ -53,6 +53,15 @@ }, "sync_mode": "full_refresh", "destination_sync_mode": "overwrite" + }, + { + "stream": { + "name": "activity", + "json_schema": {}, + "supported_sync_modes": ["full_refresh"] + }, + "sync_mode": "full_refresh", + "destination_sync_mode": "overwrite" } ] } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/activity.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/activity.json new file mode 100644 index 0000000000000..f5189758b86d3 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/activity.json @@ -0,0 +1,68 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "guid": { + "type": ["null", "string"] + }, + "kind": { + "type": ["null", "string"] + }, + "project_id": { + "type": ["null", "integer"] + }, + "occurred_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "performed_by": { + "$ref": "person.json" + }, + "message": { + "type": ["null", "string"] + }, + "highlight": { + "type": ["null", "string"] + }, + "project_version": { + "type": ["null", "integer"] + }, + "changes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + }, + "change_type": { + "type": ["null", "string"] + }, + "original_values": { + "type": ["null", "object"] + }, + "new_values": { + "type": ["null", "object"] + } + } + } + }, + "primary_resources": { + "type": ["null", "array"], + "items": { + "type": "object", + "properties": { + "id": { + "type": ["null", "integer"] + }, + "kind": { + "type": ["null", "string"] + } + } + } + } + } +} \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py index 499b1ee131498..5146a9cce8b61 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py @@ -96,6 +96,17 @@ class Epics(ProjectBasedStream): subpath = "epics" +class Activity(ProjectBasedStream): + subpath = "activity" + primary_key = "guid" + + def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]: + for record in super().parse_response(response, **kwargs): + if "project" in record: + record["project_id"] = record["project"]["id"] + yield record + + # Custom token authenticator because no "Bearer" class PivotalAuthenticator(HttpAuthenticator): def __init__(self, token: str): @@ -144,4 +155,5 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]: Labels(**project_args), Releases(**project_args), Epics(**project_args), + Activity(**project_args), ] From 81307d74abee158c39383457172387ee4e398875 Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Thu, 10 Mar 2022 17:37:49 -0800 Subject: [PATCH 13/20] Cleaning up todos --- .../acceptance-test-config.yml | 15 +- .../integration_tests/abnormal_state.json | 5 - .../integration_tests/catalog.json | 910 +++++++++++++++++- .../integration_tests/sample_state.json | 5 - .../source_pivotal_tracker/schemas/TODO.md | 25 - .../unit_tests/test_incremental_streams.py | 59 -- .../unit_tests/test_source.py | 3 +- 7 files changed, 899 insertions(+), 123 deletions(-) delete mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/abnormal_state.json delete mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_state.json delete mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/TODO.md delete mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_incremental_streams.py diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml b/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml index 80cca323c3735..8260b8aba2da7 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml @@ -15,16 +15,11 @@ tests: - config_path: "secrets/config.json" configured_catalog_path: "integration_tests/configured_catalog.json" empty_streams: [] -# TODO uncomment this block to specify that the tests should assert the connector outputs the records provided in the input file a file -# expect_records: -# path: "integration_tests/expected_records.txt" -# extra_fields: no -# exact_order: no -# extra_records: yes - incremental: # TODO if your connector does not implement incremental sync, remove this block - - config_path: "secrets/config.json" - configured_catalog_path: "integration_tests/configured_catalog.json" - future_state_path: "integration_tests/abnormal_state.json" + expect_records: + path: "integration_tests/expected_records.txt" + extra_fields: no + exact_order: no + extra_records: yes full_refresh: - config_path: "secrets/config.json" configured_catalog_path: "integration_tests/configured_catalog.json" diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/abnormal_state.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/abnormal_state.json deleted file mode 100644 index 52b0f2c2118f4..0000000000000 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/abnormal_state.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "todo-stream-name": { - "todo-field-name": "todo-abnormal-value" - } -} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json index 6799946a68514..42842051d4c1c 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json @@ -1,39 +1,915 @@ { "streams": [ { - "name": "TODO fix this file", - "supported_sync_modes": ["full_refresh", "incremental"], - "source_defined_cursor": true, - "default_cursor_field": "column1", + "name": "projects", "json_schema": { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { - "column1": { - "type": "string" + "id": { + "type": [ + "null", + "integer" + ] }, - "column2": { - "type": "number" + "kind": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "project_id": { + "type": [ + "null", + "integer" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "account_id": { + "type": [ + "null", + "integer" + ] + }, + "version": { + "type": [ + "null", + "integer" + ] + }, + "iteration_length": { + "type": [ + "null", + "integer" + ] + }, + "current_iteration_number": { + "type": [ + "null", + "integer" + ] + }, + "week_start_day": { + "type": [ + "null", + "string" + ] + }, + "point_scale": { + "type": [ + "null", + "string" + ] + }, + "point_scale_is_custom": { + "type": [ + "null", + "boolean" + ] + }, + "bugs_and_chores_are_estimatable": { + "type": [ + "null", + "boolean" + ] + }, + "automatic_planning": { + "type": [ + "null", + "boolean" + ] + }, + "enable_tasks": { + "type": [ + "null", + "boolean" + ] + }, + "time_zone": { + "type": [ + "null", + "object" + ] + }, + "velocity_averaged_over": { + "type": [ + "null", + "integer" + ] + }, + "number_of_done_iterations_to_show": { + "type": [ + "null", + "integer" + ] + }, + "has_google_domain": { + "type": [ + "null", + "boolean" + ] + }, + "enable_incoming_emails": { + "type": [ + "null", + "boolean" + ] + }, + "initial_velocity": { + "type": [ + "null", + "integer" + ] + }, + "public": { + "type": [ + "null", + "boolean" + ] + }, + "atom_enabled": { + "type": [ + "null", + "boolean" + ] + }, + "enable_following": { + "type": [ + "null", + "boolean" + ] + }, + "show_story_priority": { + "type": [ + "null", + "boolean" + ] + }, + "project_type": { + "type": [ + "null", + "string" + ] + }, + "start_time": { + "type": [ + "null", + "string" + ], + "format": "date-time" + } + } + }, + "supported_sync_modes": [ + "full_refresh" + ], + "source_defined_primary_key": [ + [ + "id" + ] + ] + }, + { + "name": "stories", + "json_schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "project_id": { + "type": [ + "null", + "integer" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "story_type": { + "type": [ + "null", + "string" + ] + }, + "estimate": { + "type": [ + "null", + "integer" + ] + }, + "story_priority": { + "type": [ + "null", + "string" + ] + }, + "current_state": { + "type": [ + "null", + "string" + ] + }, + "requested_by_id": { + "type": [ + "null", + "integer" + ] + }, + "owned_by_id": { + "type": [ + "null", + "integer" + ] + }, + "url": { + "type": [ + "null", + "string" + ] + }, + "owner_ids": { + "type": "array", + "items": { + "type": "integer" + } + }, + "labels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "project_id": { + "type": [ + "null", + "integer" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + } + } + } + }, + "accepted_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + } + } + }, + "supported_sync_modes": [ + "full_refresh" + ], + "source_defined_primary_key": [ + [ + "id" + ] + ] + }, + { + "name": "project_memberships", + "json_schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "project_id": { + "type": [ + "null", + "integer" + ] + }, + "favorite": { + "type": [ + "null", + "boolean" + ] + }, + "project_color": { + "type": [ + "null", + "string" + ] + }, + "role": { + "type": [ + "null", + "string" + ] + }, + "wants_comment_notification_emails": { + "type": [ + "null", + "boolean" + ] + }, + "will_receive_mention_notifications_or_emails": { + "type": [ + "null", + "boolean" + ] + }, + "person": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "initials": { + "type": [ + "null", + "string" + ] + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "username": { + "type": [ + "null", + "string" + ] + } + } + } + } + }, + "supported_sync_modes": [ + "full_refresh" + ], + "source_defined_primary_key": [ + [ + "id" + ] + ] + }, + { + "name": "labels", + "json_schema": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "project_id": { + "type": [ + "null", + "integer" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + } + } + }, + "supported_sync_modes": [ + "full_refresh" + ], + "source_defined_primary_key": [ + [ + "id" + ] + ] + }, + { + "name": "releases", + "json_schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "project_id": { + "type": [ + "null", + "integer" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "current_state": { + "type": [ + "null", + "string" + ] + }, + "url": { + "type": [ + "null", + "string" + ] + }, + "deadline": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "labels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "project_id": { + "type": [ + "null", + "integer" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + } + } + } + }, + "accepted_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + } + } + }, + "supported_sync_modes": [ + "full_refresh" + ], + "source_defined_primary_key": [ + [ + "id" + ] + ] + }, + { + "name": "epics", + "json_schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "project_id": { + "type": [ + "null", + "integer" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "url": { + "type": [ + "null", + "string" + ] + }, + "label": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "project_id": { + "type": [ + "null", + "integer" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + } + } } } - } + }, + "supported_sync_modes": [ + "full_refresh" + ], + "source_defined_primary_key": [ + [ + "id" + ] + ] }, { - "name": "table1", - "supported_sync_modes": ["full_refresh", "incremental"], - "source_defined_cursor": false, + "name": "activity", "json_schema": { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { - "column1": { - "type": "string" + "guid": { + "type": [ + "null", + "string" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "project_id": { + "type": [ + "null", + "integer" + ] + }, + "occured_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "performed_by": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "initials": { + "type": [ + "null", + "string" + ] + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "username": { + "type": [ + "null", + "string" + ] + } + } + }, + "message": { + "type": [ + "null", + "string" + ] + }, + "highlight": { + "type": [ + "null", + "string" + ] + }, + "project_version": { + "type": [ + "null", + "integer" + ] + }, + "changes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + }, + "change_type": { + "type": [ + "null", + "string" + ] + }, + "original_values": { + "type": [ + "null", + "object" + ] + }, + "new_values": { + "type": [ + "null", + "object" + ] + } + } + } }, - "column2": { - "type": "number" + "primary_resources": { + "type": [ + "null", + "array" + ], + "items": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ] + }, + "kind": { + "type": [ + "null", + "string" + ] + } + } + } } } - } + }, + "supported_sync_modes": [ + "full_refresh" + ], + "source_defined_primary_key": [ + [ + "guid" + ] + ] } ] } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_state.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_state.json deleted file mode 100644 index 3587e579822d0..0000000000000 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_state.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "todo-stream-name": { - "todo-field-name": "value" - } -} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/TODO.md b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/TODO.md deleted file mode 100644 index cf1efadb3c9c9..0000000000000 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/TODO.md +++ /dev/null @@ -1,25 +0,0 @@ -# TODO: Define your stream schemas -Your connector must describe the schema of each stream it can output using [JSONSchema](https://json-schema.org). - -The simplest way to do this is to describe the schema of your streams using one `.json` file per stream. You can also dynamically generate the schema of your stream in code, or you can combine both approaches: start with a `.json` file and dynamically add properties to it. - -The schema of a stream is the return value of `Stream.get_json_schema`. - -## Static schemas -By default, `Stream.get_json_schema` reads a `.json` file in the `schemas/` directory whose name is equal to the value of the `Stream.name` property. In turn `Stream.name` by default returns the name of the class in snake case. Therefore, if you have a class `class EmployeeBenefits(HttpStream)` the default behavior will look for a file called `schemas/employee_benefits.json`. You can override any of these behaviors as you need. - -Important note: any objects referenced via `$ref` should be placed in the `shared/` directory in their own `.json` files. - -## Dynamic schemas -If you'd rather define your schema in code, override `Stream.get_json_schema` in your stream class to return a `dict` describing the schema using [JSONSchema](https://json-schema.org). - -## Dynamically modifying static schemas -Override `Stream.get_json_schema` to run the default behavior, edit the returned value, then return the edited value: -``` -def get_json_schema(self): - schema = super().get_json_schema() - schema['dynamically_determined_property'] = "property" - return schema -``` - -Delete this file once you're done. Or don't. Up to you :) diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_incremental_streams.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_incremental_streams.py deleted file mode 100644 index 662ee5170fa67..0000000000000 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_incremental_streams.py +++ /dev/null @@ -1,59 +0,0 @@ -# -# Copyright (c) 2021 Airbyte, Inc., all rights reserved. -# - - -from airbyte_cdk.models import SyncMode -from pytest import fixture -from source_pivotal_tracker.source import IncrementalPivotalTrackerStream - - -@fixture -def patch_incremental_base_class(mocker): - # Mock abstract methods to enable instantiating abstract class - mocker.patch.object(IncrementalPivotalTrackerStream, "path", "v0/example_endpoint") - mocker.patch.object(IncrementalPivotalTrackerStream, "primary_key", "test_primary_key") - mocker.patch.object(IncrementalPivotalTrackerStream, "__abstractmethods__", set()) - - -def test_cursor_field(patch_incremental_base_class): - stream = IncrementalPivotalTrackerStream() - # TODO: replace this with your expected cursor field - expected_cursor_field = [] - assert stream.cursor_field == expected_cursor_field - - -def test_get_updated_state(patch_incremental_base_class): - stream = IncrementalPivotalTrackerStream() - # TODO: replace this with your input parameters - inputs = {"current_stream_state": None, "latest_record": None} - # TODO: replace this with your expected updated stream state - expected_state = {} - assert stream.get_updated_state(**inputs) == expected_state - - -def test_stream_slices(patch_incremental_base_class): - stream = IncrementalPivotalTrackerStream() - # TODO: replace this with your input parameters - inputs = {"sync_mode": SyncMode.incremental, "cursor_field": [], "stream_state": {}} - # TODO: replace this with your expected stream slices list - expected_stream_slice = [None] - assert stream.stream_slices(**inputs) == expected_stream_slice - - -def test_supports_incremental(patch_incremental_base_class, mocker): - mocker.patch.object(IncrementalPivotalTrackerStream, "cursor_field", "dummy_field") - stream = IncrementalPivotalTrackerStream() - assert stream.supports_incremental - - -def test_source_defined_cursor(patch_incremental_base_class): - stream = IncrementalPivotalTrackerStream() - assert stream.source_defined_cursor - - -def test_stream_checkpoint_interval(patch_incremental_base_class): - stream = IncrementalPivotalTrackerStream() - # TODO: replace this with your expected checkpoint interval - expected_checkpoint_interval = None - assert stream.state_checkpoint_interval == expected_checkpoint_interval diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py index 83db74ebe150f..bb6a48c3b38fd 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py @@ -17,6 +17,5 @@ def test_streams(mocker): source = SourcePivotalTracker() config_mock = MagicMock() streams = source.streams(config_mock) - # TODO: replace this with your streams number - expected_streams_number = 2 + expected_streams_number = 7 assert len(streams) == expected_streams_number From 5f64062878423e4ab5567c8c110014b86397aa08 Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Thu, 10 Mar 2022 17:40:11 -0800 Subject: [PATCH 14/20] Code formatting --- .../source-pivotal-tracker/source_pivotal_tracker/source.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py index 5146a9cce8b61..b656130eca909 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/source.py @@ -7,11 +7,11 @@ from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Tuple import requests +from airbyte_cdk.models import SyncMode from airbyte_cdk.sources import AbstractSource from airbyte_cdk.sources.streams import Stream from airbyte_cdk.sources.streams.http import HttpStream from airbyte_cdk.sources.streams.http.auth import HttpAuthenticator -from airbyte_cdk.models import SyncMode class PivotalTrackerStream(HttpStream, ABC): @@ -22,7 +22,7 @@ class PivotalTrackerStream(HttpStream, ABC): def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]: headers = response.headers - if not "X-Tracker-Pagination-Total" in headers: + if "X-Tracker-Pagination-Total" not in headers: return None # not paginating page_size = int(headers["X-Tracker-Pagination-Limit"]) From 9cb74907258adb62b74a8a93cf4045e5f4b39882 Mon Sep 17 00:00:00 2001 From: Brian Leonard Date: Thu, 10 Mar 2022 18:37:17 -0800 Subject: [PATCH 15/20] Unit tests --- .../integration_tests/acceptance.py | 2 - .../source-pivotal-tracker/setup.py | 1 + .../unit_tests/conftest.py | 52 + .../unit_tests/responses/activity.json | 2104 +++++++++++++++++ .../unit_tests/responses/epics.json | 93 + .../unit_tests/responses/labels.json | 82 + .../responses/project_memberships.json | 156 ++ .../unit_tests/responses/projects.json | 73 + .../unit_tests/responses/releases.json | 17 + .../unit_tests/responses/stories.json | 71 + .../unit_tests/test_source.py | 16 +- .../unit_tests/test_streams.py | 177 +- 12 files changed, 2757 insertions(+), 87 deletions(-) create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/conftest.py create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/activity.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/epics.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/labels.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/project_memberships.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/projects.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/releases.json create mode 100644 airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/stories.json diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/acceptance.py b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/acceptance.py index 056971f954502..0347f2a0b143d 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/acceptance.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/acceptance.py @@ -11,6 +11,4 @@ @pytest.fixture(scope="session", autouse=True) def connector_setup(): """This fixture is a placeholder for external resources that acceptance test might require.""" - # TODO: setup test dependencies if needed. otherwise remove the TODO comments yield - # TODO: clean up test dependencies diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/setup.py b/airbyte-integrations/connectors/source-pivotal-tracker/setup.py index 27643f6b05ea6..2258f502aef33 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/setup.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/setup.py @@ -13,6 +13,7 @@ "pytest~=6.1", "pytest-mock~=3.6.1", "source-acceptance-test", + "responses~=0.13.3", ] setup( diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/conftest.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/conftest.py new file mode 100644 index 0000000000000..de65a3fb3603d --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/conftest.py @@ -0,0 +1,52 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + +import json +import os + +from pytest import fixture + + +def load_file(fn): + return open(os.path.join("unit_tests", "responses", fn)).read() + + +@fixture +def config_pass(): + return {"api_token": "goodtoken"} + + +@fixture +def projects_response(): + return json.loads(load_file("projects.json")) + + +@fixture +def project_memberships_response(): + return json.loads(load_file("project_memberships.json")) + + +@fixture +def activity_response(): + return json.loads(load_file("activity.json")) + + +@fixture +def labels_response(): + return json.loads(load_file("labels.json")) + + +@fixture +def releases_response(): + return json.loads(load_file("releases.json")) + + +@fixture +def epics_response(): + return json.loads(load_file("epics.json")) + + +@fixture +def stories_response(): + return json.loads(load_file("stories.json")) diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/activity.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/activity.json new file mode 100644 index 0000000000000..ba05af336b702 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/activity.json @@ -0,0 +1,2104 @@ +[ + { + "kind": "story_move_activity", + "guid": "99_65", + "project_version": 65, + "message": "Darth Vader moved 2 stories", + "highlight": "moved", + "changes": + [ + { + "kind": "story", + "change_type": "update", + "id": 562, + "original_values": + { + "before_id": 551 + }, + "new_values": + { + "before_id": 557 + }, + "name": "Garbage smashers on the detention level have malfunctioned", + "story_type": "bug", + "story_priority": "p3" + }, + { + "kind": "story", + "change_type": "update", + "id": 564, + "original_values": + { + "after_id": 557 + }, + "new_values": + { + "after_id": 565 + }, + "name": "Evacuate, in our moment of triumph", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 564, + "name": "Evacuate, in our moment of triumph", + "story_type": "feature", + "url": "http://localhost/story/show/564", + "story_priority": "p3" + }, + { + "kind": "story", + "id": 562, + "name": "Garbage smashers on the detention level have malfunctioned", + "story_type": "bug", + "url": "http://localhost/story/show/562", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_move_activity", + "guid": "99_64", + "project_version": 64, + "message": "Darth Vader moved 2 stories", + "highlight": "moved", + "changes": + [ + { + "kind": "story", + "change_type": "update", + "id": 562, + "original_values": + { + "before_id": 557 + }, + "new_values": + { + "before_id": 551 + }, + "name": "Garbage smashers on the detention level have malfunctioned", + "story_type": "bug", + "story_priority": "p3" + }, + { + "kind": "story", + "change_type": "update", + "id": 564, + "original_values": + { + "after_id": 565 + }, + "new_values": + { + "after_id": 557 + }, + "name": "Evacuate, in our moment of triumph", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 564, + "name": "Evacuate, in our moment of triumph", + "story_type": "feature", + "url": "http://localhost/story/show/564", + "story_priority": "p3" + }, + { + "kind": "story", + "id": 562, + "name": "Garbage smashers on the detention level have malfunctioned", + "story_type": "bug", + "url": "http://localhost/story/show/562", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "comment_create_activity", + "guid": "99_62", + "project_version": 62, + "message": "Darth Vader added comment: \"I want them alive!\"", + "highlight": "added comment:", + "changes": + [ + { + "kind": "comment", + "change_type": "create", + "id": 112, + "new_values": + { + "id": 112, + "story_id": 555, + "text": "I want them alive!", + "person_id": 101, + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "file_attachment_ids": + [ + ], + "google_attachment_ids": + [ + ], + "attachment_ids": + [ + ], + "file_attachments": + [ + ], + "google_attachments": + [ + ] + } + }, + { + "kind": "story", + "change_type": "update", + "id": 555, + "original_values": + { + "follower_ids": + [ + ] + }, + "new_values": + { + "follower_ids": + [ + 101 + ] + }, + "name": "Bring me the passengers", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 555, + "name": "Bring me the passengers", + "story_type": "feature", + "url": "http://localhost/story/show/555", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_update_activity", + "guid": "99_56", + "project_version": 56, + "message": "Darth Vader unstarted this feature", + "highlight": "unstarted", + "changes": + [ + { + "kind": "story", + "change_type": "update", + "id": 555, + "original_values": + { + "current_state": "unscheduled", + "before_id": 551, + "after_id": 557 + }, + "new_values": + { + "current_state": "unstarted", + "before_id": 567, + "after_id": 552 + }, + "name": "Bring me the passengers", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 555, + "name": "Bring me the passengers", + "story_type": "feature", + "url": "http://localhost/story/show/555", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_update_activity", + "guid": "99_54", + "project_version": 54, + "message": "Darth Vader rejected this feature", + "highlight": "rejected", + "changes": + [ + { + "kind": "label", + "change_type": "update", + "id": 2009, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 2, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 3, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 1, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 1, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 2, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 3, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 1, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 1, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "plans" + }, + { + "kind": "story", + "change_type": "update", + "id": 559, + "original_values": + { + "current_state": "delivered" + }, + "new_values": + { + "current_state": "rejected" + }, + "name": "All exhaust ports should be shielded", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 559, + "name": "All exhaust ports should be shielded", + "story_type": "feature", + "url": "http://localhost/story/show/559", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_update_activity", + "guid": "99_48", + "project_version": 48, + "message": "Darth Vader started this feature", + "highlight": "started", + "changes": + [ + { + "kind": "label", + "change_type": "update", + "id": 2008, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 4, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 2, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 4, + "started": 2, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 2, + "started": 1, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "rebel bases" + }, + { + "kind": "label", + "change_type": "update", + "id": 2009, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 5, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 2, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 3, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 1, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "plans" + }, + { + "kind": "story", + "change_type": "update", + "id": 556, + "original_values": + { + "current_state": "unscheduled", + "before_id": 555, + "after_id": 557 + }, + "new_values": + { + "current_state": "started", + "before_id": 567, + "after_id": 560 + }, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 556, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "url": "http://localhost/story/show/556", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_update_activity", + "guid": "99_47", + "project_version": 47, + "message": "Darth Vader accepted this feature", + "highlight": "accepted", + "changes": + [ + { + "kind": "label", + "change_type": "update", + "id": 2008, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 1, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 1, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 4, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 2, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "rebel bases" + }, + { + "kind": "story", + "change_type": "update", + "id": 560, + "original_values": + { + "current_state": "delivered", + "accepted_at": null + }, + "new_values": + { + "current_state": "accepted", + "accepted_at": "2022-03-08T12:00:05Z" + }, + "name": "Destroy Alderaan", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 560, + "name": "Destroy Alderaan", + "story_type": "feature", + "url": "http://localhost/story/show/560", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_update_activity", + "guid": "99_43", + "project_version": 43, + "message": "Darth Vader edited this feature", + "highlight": "edited", + "changes": + [ + { + "kind": "label", + "change_type": "update", + "id": 2008, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 3, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "rebel bases" + }, + { + "kind": "label", + "change_type": "update", + "id": 2009, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 3, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 5, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "plans" + }, + { + "kind": "label", + "change_type": "update", + "id": 2015, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 3, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "diplomatic relations" + }, + { + "kind": "story", + "change_type": "update", + "id": 556, + "original_values": + { + "label_ids": + [ + 2015 + ], + "labels": + [ + "diplomatic relations" + ] + }, + "new_values": + { + "label_ids": + [ + 2009, + 2008 + ], + "labels": + [ + "plans", + "rebel bases" + ] + }, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 556, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "url": "http://localhost/story/show/556", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "98_2", + "project_version": 2, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": + [ + { + "kind": "story", + "change_type": "create", + "id": 568, + "new_values": + { + "id": 568, + "project_id": 98, + "name": "Midi-chlorians", + "description": "Without the midi-chlorians, life could not exist, and we would have no knowledge of the Force. They continually speak to us, telling us the will of the Force. When you learn to quiet your mind, you'll hear them speaking to you", + "story_type": "feature", + "current_state": "unstarted", + "estimate": 3, + "requested_by_id": 101, + "owned_by_id": 101, + "owner_ids": + [ + 101 + ], + "label_ids": + [ + ], + "follower_ids": + [ + ], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "blocked_story_ids": + [ + ], + "story_priority": "p3", + "labels": + [ + ] + }, + "name": "Midi-chlorians", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 568, + "name": "Midi-chlorians", + "story_type": "feature", + "url": "http://localhost/story/show/568", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 98, + "name": "Learn About the Force" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_11", + "project_version": 11, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": + [ + { + "kind": "story", + "change_type": "create", + "id": 558, + "new_values": + { + "id": 558, + "project_id": 99, + "name": "Contact Lando", + "story_type": "feature", + "current_state": "accepted", + "estimate": 1, + "accepted_at": "2022-03-08T12:00:05Z", + "requested_by_id": 101, + "owner_ids": + [ + ], + "label_ids": + [ + ], + "follower_ids": + [ + ], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 557, + "after_id": 553, + "blocked_story_ids": + [ + ], + "story_priority": "p3", + "labels": + [ + ] + }, + "name": "Contact Lando", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 558, + "name": "Contact Lando", + "story_type": "feature", + "url": "http://localhost/story/show/558", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_10", + "project_version": 10, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": + [ + { + "kind": "story", + "change_type": "create", + "id": 557, + "new_values": + { + "id": 557, + "project_id": 99, + "name": "Prepare carbonite freezing chamber", + "description": "Test first on Solo, we don't want the Emperor's prize damaged.", + "story_type": "feature", + "current_state": "unscheduled", + "requested_by_id": 101, + "owner_ids": + [ + ], + "label_ids": + [ + 2014 + ], + "follower_ids": + [ + ], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 556, + "after_id": 553, + "blocked_story_ids": + [ + ], + "story_priority": "p3", + "labels": + [ + "r&d" + ] + }, + "name": "Prepare carbonite freezing chamber", + "story_type": "feature", + "story_priority": "p3" + }, + { + "kind": "label", + "change_type": "update", + "id": 2014, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "r&d" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 557, + "name": "Prepare carbonite freezing chamber", + "story_type": "feature", + "url": "http://localhost/story/show/557", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_9", + "project_version": 9, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": + [ + { + "kind": "story", + "change_type": "create", + "id": 556, + "new_values": + { + "id": 556, + "project_id": 99, + "name": "Interrogate Leia Organa", + "description": "She is proving to be resistant to our mind probes", + "story_type": "feature", + "current_state": "unscheduled", + "requested_by_id": 101, + "owned_by_id": 101, + "owner_ids": + [ + 101, + 105 + ], + "label_ids": + [ + 2015 + ], + "follower_ids": + [ + ], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 555, + "after_id": 553, + "blocked_story_ids": + [ + ], + "story_priority": "p3", + "labels": + [ + "diplomatic relations" + ] + }, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "story_priority": "p3" + }, + { + "kind": "label", + "change_type": "update", + "id": 2015, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "diplomatic relations" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 556, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "url": "http://localhost/story/show/556", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_8", + "project_version": 8, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": + [ + { + "kind": "story", + "change_type": "create", + "id": 555, + "new_values": + { + "id": 555, + "project_id": 99, + "name": "Bring me the passengers", + "description": "ignore the droids", + "story_type": "feature", + "current_state": "unscheduled", + "requested_by_id": 101, + "owner_ids": + [ + ], + "label_ids": + [ + ], + "follower_ids": + [ + ], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 554, + "after_id": 553, + "blocked_story_ids": + [ + ], + "story_priority": "p3", + "labels": + [ + ] + }, + "name": "Bring me the passengers", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 555, + "name": "Bring me the passengers", + "story_type": "feature", + "url": "http://localhost/story/show/555", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_7", + "project_version": 7, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": + [ + { + "kind": "story", + "change_type": "create", + "id": 554, + "new_values": + { + "id": 554, + "project_id": 99, + "name": "Identify Bothan spies", + "description": "Infiltrate their spy network.", + "story_type": "feature", + "current_state": "unscheduled", + "requested_by_id": 101, + "owned_by_id": 106, + "owner_ids": + [ + 106 + ], + "label_ids": + [ + ], + "follower_ids": + [ + ], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 552, + "after_id": 553, + "blocked_story_ids": + [ + ], + "story_priority": "p3", + "labels": + [ + ] + }, + "name": "Identify Bothan spies", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 554, + "name": "Identify Bothan spies", + "story_type": "feature", + "url": "http://localhost/story/show/554", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_6", + "project_version": 6, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": + [ + { + "kind": "story", + "change_type": "create", + "id": 553, + "new_values": + { + "id": 553, + "project_id": 99, + "name": "Build protocol droid", + "description": "I want a friend", + "story_type": "feature", + "current_state": "accepted", + "estimate": 3, + "accepted_at": "2022-03-08T12:00:05Z", + "requested_by_id": 101, + "owned_by_id": 101, + "owner_ids": + [ + 101 + ], + "label_ids": + [ + 2014, + 2008 + ], + "follower_ids": + [ + ], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 552, + "blocked_story_ids": + [ + ], + "story_priority": "p3", + "labels": + [ + "r&d", + "rebel bases" + ] + }, + "name": "Build protocol droid", + "story_type": "feature", + "story_priority": "p3" + }, + { + "kind": "label", + "change_type": "update", + "id": 2008, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "rebel bases" + }, + { + "kind": "label", + "change_type": "update", + "id": 2014, + "original_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": + { + "counts": + { + "number_of_zero_point_stories_by_state": + { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": + { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": + { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "r&d" + } + ], + "primary_resources": + [ + { + "kind": "story", + "id": 553, + "name": "Build protocol droid", + "story_type": "feature", + "url": "http://localhost/story/show/553", + "story_priority": "p3" + } + ], + "secondary_resources": + [ + ], + "project": + { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + } +] \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/epics.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/epics.json new file mode 100644 index 0000000000000..65bb1873361bf --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/epics.json @@ -0,0 +1,93 @@ +[ + { + "id": 555, + "kind": "epic", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "project_id": 99, + "name": "Sanitation", + "url": "http://localhost/epic/show/555", + "label": + { + "id": 2017, + "project_id": 99, + "kind": "label", + "name": "sanitation", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + }, + { + "id": 8, + "kind": "epic", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "project_id": 99, + "name": "Maintenance", + "url": "http://localhost/epic/show/8", + "label": + { + "id": 2011, + "project_id": 99, + "kind": "label", + "name": "mnt", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + }, + { + "id": 7, + "kind": "epic", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "project_id": 99, + "name": "Turn Luke Skywalker", + "url": "http://localhost/epic/show/7", + "label": + { + "id": 2010, + "project_id": 99, + "kind": "label", + "name": "turning luke", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + }, + { + "id": 6, + "kind": "epic", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "project_id": 99, + "name": "Death Star Plans", + "url": "http://localhost/epic/show/6", + "label": + { + "id": 2009, + "project_id": 99, + "kind": "label", + "name": "plans", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + }, + { + "id": 5, + "kind": "epic", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "project_id": 99, + "name": "Rebel Home Worlds", + "description": "Identify the systems and eliminate the rebel scum.", + "url": "http://localhost/epic/show/5", + "label": + { + "id": 2008, + "project_id": 99, + "kind": "label", + "name": "rebel bases", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + } +] \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/labels.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/labels.json new file mode 100644 index 0000000000000..b40d7cfece51a --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/labels.json @@ -0,0 +1,82 @@ +[ + { + "kind": "label", + "id": 2015, + "project_id": 99, + "name": "diplomatic relations", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2013, + "project_id": 99, + "name": "fleet ops", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2011, + "project_id": 99, + "name": "mnt", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2012, + "project_id": 99, + "name": "personnel", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2009, + "project_id": 99, + "name": "plans", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2014, + "project_id": 99, + "name": "r&d", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2008, + "project_id": 99, + "name": "rebel bases", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2017, + "project_id": 99, + "name": "sanitation", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2016, + "project_id": 99, + "name": "security protocol", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2010, + "project_id": 99, + "name": "turning luke", + "created_at": 1646740800000, + "updated_at": 1646740800000 + } +] \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/project_memberships.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/project_memberships.json new file mode 100644 index 0000000000000..c77c7f75f6c83 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/project_memberships.json @@ -0,0 +1,156 @@ +[ + { + "kind": "project_membership", + "project_id": 99, + "id": 100, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "owner", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": false, + "will_receive_mention_notifications_or_emails": true, + "person": + { + "kind": "person", + "id": 100, + "name": "Emperor Palpatine", + "email": "emperor@galacticrepublic.gov", + "initials": "EP", + "username": "palpatine" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 101, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "member", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": true, + "will_receive_mention_notifications_or_emails": true, + "person": + { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "email": "vader@deathstar.mil", + "initials": "DV", + "username": "vader" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 102, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "owner", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": false, + "will_receive_mention_notifications_or_emails": true, + "person": + { + "kind": "person", + "id": 102, + "name": "Wilhuff Tarkin", + "email": "governor@eriadu.gov", + "initials": "WT", + "username": "tarkin" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 103, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "member", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": true, + "will_receive_mention_notifications_or_emails": true, + "person": + { + "kind": "person", + "id": 104, + "name": "Clone TK421", + "email": "tk421@deathstar.mil", + "initials": "TK421", + "username": "tk421" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 104, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "member", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": true, + "will_receive_mention_notifications_or_emails": true, + "person": + { + "kind": "person", + "id": 103, + "name": "Moradmin Bast", + "email": "bast@deathstar.mil", + "initials": "MB", + "username": "bast" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 105, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "member", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": true, + "will_receive_mention_notifications_or_emails": true, + "person": + { + "kind": "person", + "id": 105, + "name": "Robotic Maintenance Team 4", + "email": "I662@deathstar.mil", + "initials": "i662", + "username": "i662" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 107, + "last_viewed_at": "2022-03-08T12:00:05Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "viewer", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": false, + "will_receive_mention_notifications_or_emails": true, + "person": + { + "kind": "person", + "id": 107, + "name": "Bevel Lemelisk", + "email": "bevel@sith.mil", + "initials": "bl", + "username": "bevel" + } + } +] \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/projects.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/projects.json new file mode 100644 index 0000000000000..0102172ed15b4 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/projects.json @@ -0,0 +1,73 @@ +[ + { + "id": 98, + "kind": "project", + "name": "Learn About the Force", + "version": 2, + "iteration_length": 1, + "week_start_day": "Monday", + "point_scale": "0,1,2,3", + "point_scale_is_custom": false, + "bugs_and_chores_are_estimatable": false, + "automatic_planning": true, + "enable_tasks": true, + "time_zone": + { + "kind": "time_zone", + "olson_name": "America/Los_Angeles", + "offset": "-08:00" + }, + "velocity_averaged_over": 3, + "number_of_done_iterations_to_show": 4, + "has_google_domain": false, + "enable_incoming_emails": true, + "initial_velocity": 10, + "public": false, + "atom_enabled": true, + "project_type": "private", + "start_time": "2022-03-08T12:00:05Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:15Z", + "show_story_priority": true, + "account_id": 100, + "current_iteration_number": 1, + "enable_following": true + }, + { + "id": 99, + "kind": "project", + "name": "Death Star", + "version": 66, + "iteration_length": 1, + "week_start_day": "Monday", + "point_scale": "0,1,2,3", + "point_scale_is_custom": false, + "bugs_and_chores_are_estimatable": false, + "automatic_planning": true, + "enable_tasks": true, + "time_zone": + { + "kind": "time_zone", + "olson_name": "America/Los_Angeles", + "offset": "-08:00" + }, + "velocity_averaged_over": 3, + "number_of_done_iterations_to_show": 4, + "has_google_domain": false, + "description": "Expeditionary Battle Planetoid", + "profile_content": "This is a machine of war such as the universe has never known. It's colossal, the size of a class-four moon. And it possesses firepower unequaled in the history of warfare.", + "enable_incoming_emails": true, + "initial_velocity": 10, + "public": false, + "atom_enabled": true, + "project_type": "private", + "start_date": "2021-11-22", + "start_time": "2022-03-08T12:00:15Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:15Z", + "show_story_priority": true, + "account_id": 100, + "current_iteration_number": 15, + "enable_following": true + } +] \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/releases.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/releases.json new file mode 100644 index 0000000000000..8cca75d2b1cf8 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/releases.json @@ -0,0 +1,17 @@ +[ + { + "kind": "release", + "id": 552, + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "deadline": "2022-03-08T12:00:05Z", + "name": "Battlestation fully operational", + "description": "Everything is proceeding as I have foreseen.", + "current_state": "unstarted", + "url": "http://localhost/story/show/552", + "project_id": 99, + "labels": + [ + ] + } +] \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/stories.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/stories.json new file mode 100644 index 0000000000000..2529c19382e13 --- /dev/null +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/stories.json @@ -0,0 +1,71 @@ +[ + { + "id": 561, + "current_state": "unstarted", + "labels": + [ + ], + "tasks": + [ + ], + "comments": + [ + ] + }, + { + "id": 566, + "current_state": "unstarted", + "labels": + [ + { + "id": 2011, + "project_id": 99, + "kind": "label", + "name": "mnt", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + ], + "tasks": + [ + ], + "comments": + [ + ] + }, + { + "id": 552, + "current_state": "unstarted", + "labels": + [ + ], + "tasks": + [ + ], + "comments": + [ + ] + }, + { + "id": 555, + "current_state": "unstarted", + "labels": + [ + ], + "tasks": + [ + ], + "comments": + [ + { + "kind": "comment", + "id": 112, + "text": "I want them alive!", + "person_id": 101, + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "story_id": 555 + } + ] + } +] \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py index bb6a48c3b38fd..047e40d980bd3 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py @@ -2,20 +2,24 @@ # Copyright (c) 2021 Airbyte, Inc., all rights reserved. # +import responses from unittest.mock import MagicMock from source_pivotal_tracker.source import SourcePivotalTracker -def test_check_connection(mocker): +@responses.activate +def test_check_connection(config_pass, projects_response): source = SourcePivotalTracker() - logger_mock, config_mock = MagicMock(), MagicMock() - assert source.check_connection(logger_mock, config_mock) == (True, None) + logger_mock = MagicMock() + responses.add(responses.GET, "https://www.pivotaltracker.com/services/v5/projects", json=projects_response) + assert source.check_connection(logger_mock, config_pass) == (True, None) -def test_streams(mocker): +@responses.activate +def test_streams(config_pass, projects_response): source = SourcePivotalTracker() - config_mock = MagicMock() - streams = source.streams(config_mock) + responses.add(responses.GET, "https://www.pivotaltracker.com/services/v5/projects", json=projects_response) + streams = source.streams(config_pass) expected_streams_number = 7 assert len(streams) == expected_streams_number diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py index a32e6c134ca55..6c802afe0cf2a 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py @@ -2,82 +2,101 @@ # Copyright (c) 2021 Airbyte, Inc., all rights reserved. # -from http import HTTPStatus -from unittest.mock import MagicMock - -import pytest -from source_pivotal_tracker.source import PivotalTrackerStream - - -@pytest.fixture -def patch_base_class(mocker): - # Mock abstract methods to enable instantiating abstract class - mocker.patch.object(PivotalTrackerStream, "path", "v0/example_endpoint") - mocker.patch.object(PivotalTrackerStream, "primary_key", "test_primary_key") - mocker.patch.object(PivotalTrackerStream, "__abstractmethods__", set()) - - -def test_request_params(patch_base_class): - stream = PivotalTrackerStream() - # TODO: replace this with your input parameters - inputs = {"stream_slice": None, "stream_state": None, "next_page_token": None} - # TODO: replace this with your expected request parameters - expected_params = {} - assert stream.request_params(**inputs) == expected_params - - -def test_next_page_token(patch_base_class): - stream = PivotalTrackerStream() - # TODO: replace this with your input parameters - inputs = {"response": MagicMock()} - # TODO: replace this with your expected next page token - expected_token = None - assert stream.next_page_token(**inputs) == expected_token - - -def test_parse_response(patch_base_class): - stream = PivotalTrackerStream() - # TODO: replace this with your input parameters - inputs = {"response": MagicMock()} - # TODO: replace this with your expected parced object - expected_parsed_object = {} - assert next(stream.parse_response(**inputs)) == expected_parsed_object - - -def test_request_headers(patch_base_class): - stream = PivotalTrackerStream() - # TODO: replace this with your input parameters - inputs = {"stream_slice": None, "stream_state": None, "next_page_token": None} - # TODO: replace this with your expected request headers - expected_headers = {} - assert stream.request_headers(**inputs) == expected_headers - - -def test_http_method(patch_base_class): - stream = PivotalTrackerStream() - # TODO: replace this with your expected http request method - expected_method = "GET" - assert stream.http_method == expected_method - - -@pytest.mark.parametrize( - ("http_status", "should_retry"), - [ - (HTTPStatus.OK, False), - (HTTPStatus.BAD_REQUEST, False), - (HTTPStatus.TOO_MANY_REQUESTS, True), - (HTTPStatus.INTERNAL_SERVER_ERROR, True), - ], -) -def test_should_retry(patch_base_class, http_status, should_retry): - response_mock = MagicMock() - response_mock.status_code = http_status - stream = PivotalTrackerStream() - assert stream.should_retry(response_mock) == should_retry - - -def test_backoff_time(patch_base_class): - response_mock = MagicMock() - stream = PivotalTrackerStream() - expected_backoff_time = None - assert stream.backoff_time(response_mock) == expected_backoff_time +import responses +from airbyte_cdk.models import SyncMode +from source_pivotal_tracker.source import PivotalAuthenticator, Projects, ProjectMemberships, Activity, Labels, Releases, Epics, Stories + +auth = PivotalAuthenticator("goodtoken") +project_args = {"project_ids": [98, 99], "authenticator": auth} +stream_slice = {"project_id": 99} + + +@responses.activate +def test_projects_stream(projects_response): + responses.add( + responses.GET, + "https://www.pivotaltracker.com/services/v5/projects", + json=projects_response, + ) + stream = Projects(authenticator=auth) + records = [r for r in stream.read_records(SyncMode.full_refresh, None, None, None)] + assert len(records) == 2 + assert len(responses.calls) == 1 + + +@responses.activate +def test_project_memberships_stream(project_memberships_response): + responses.add( + responses.GET, + "https://www.pivotaltracker.com/services/v5/projects/99/memberships", + json=project_memberships_response, + ) + stream = ProjectMemberships(**project_args) + records = [r for r in stream.read_records(SyncMode.full_refresh, None, stream_slice, None)] + assert len(records) == 7 + assert len(responses.calls) == 1 + + +@responses.activate +def test_activity_stream(activity_response): + responses.add( + responses.GET, + "https://www.pivotaltracker.com/services/v5/projects/99/activity", + json=activity_response, + ) + stream = Activity(**project_args) + records = [r for r in stream.read_records(SyncMode.full_refresh, None, stream_slice, None)] + assert len(records) == 15 + assert len(responses.calls) == 1 + + +@responses.activate +def test_labels_stream(labels_response): + responses.add( + responses.GET, + "https://www.pivotaltracker.com/services/v5/projects/99/labels", + json=labels_response, + ) + stream = Labels(**project_args) + records = [r for r in stream.read_records(SyncMode.full_refresh, None, stream_slice, None)] + assert len(records) == 10 + assert len(responses.calls) == 1 + + +@responses.activate +def test_releases_stream(releases_response): + responses.add( + responses.GET, + "https://www.pivotaltracker.com/services/v5/projects/99/releases", + json=releases_response, + ) + stream = Releases(**project_args) + records = [r for r in stream.read_records(SyncMode.full_refresh, None, stream_slice, None)] + assert len(records) == 1 + assert len(responses.calls) == 1 + + +@responses.activate +def test_epics_stream(epics_response): + responses.add( + responses.GET, + "https://www.pivotaltracker.com/services/v5/projects/99/epics", + json=epics_response, + ) + stream = Epics(**project_args) + records = [r for r in stream.read_records(SyncMode.full_refresh, None, stream_slice, None)] + assert len(records) == 5 + assert len(responses.calls) == 1 + + +@responses.activate +def test_stories_stream(stories_response): + responses.add( + responses.GET, + "https://www.pivotaltracker.com/services/v5/projects/99/stories", + json=stories_response, + ) + stream = Stories(**project_args) + records = [r for r in stream.read_records(SyncMode.full_refresh, None, stream_slice, None)] + assert len(records) == 4 + assert len(responses.calls) == 1 From 08254a8cda6cf784faf0bde554d16cea97a3f6f0 Mon Sep 17 00:00:00 2001 From: marcosmarxm Date: Mon, 4 Apr 2022 17:01:55 -0700 Subject: [PATCH 16/20] small changes --- .../source-pivotal-tracker/acceptance-test-config.yml | 5 ----- .../source-pivotal-tracker/unit_tests/test_source.py | 2 +- .../source-pivotal-tracker/unit_tests/test_streams.py | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml b/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml index 8260b8aba2da7..d809d7b67525c 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-pivotal-tracker/acceptance-test-config.yml @@ -15,11 +15,6 @@ tests: - config_path: "secrets/config.json" configured_catalog_path: "integration_tests/configured_catalog.json" empty_streams: [] - expect_records: - path: "integration_tests/expected_records.txt" - extra_fields: no - exact_order: no - extra_records: yes full_refresh: - config_path: "secrets/config.json" configured_catalog_path: "integration_tests/configured_catalog.json" diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py index 047e40d980bd3..e17146de79739 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_source.py @@ -2,9 +2,9 @@ # Copyright (c) 2021 Airbyte, Inc., all rights reserved. # -import responses from unittest.mock import MagicMock +import responses from source_pivotal_tracker.source import SourcePivotalTracker diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py index 6c802afe0cf2a..417738c1f1693 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/test_streams.py @@ -4,7 +4,7 @@ import responses from airbyte_cdk.models import SyncMode -from source_pivotal_tracker.source import PivotalAuthenticator, Projects, ProjectMemberships, Activity, Labels, Releases, Epics, Stories +from source_pivotal_tracker.source import Activity, Epics, Labels, PivotalAuthenticator, ProjectMemberships, Projects, Releases, Stories auth = PivotalAuthenticator("goodtoken") project_args = {"project_ids": [98, 99], "authenticator": auth} From ca165edf5bcee776d5952d2473a5c834362a114c Mon Sep 17 00:00:00 2001 From: marcosmarxm Date: Mon, 4 Apr 2022 17:25:25 -0700 Subject: [PATCH 17/20] run format --- .../destination/gcs/GcsNameTransformer.java | 4 + .../destination/gcs/GcsStorageOperations.java | 5 +- .../gcs/GcsDestinationAcceptanceTest.java | 1 - .../s3/SerializedBufferFactory.java | 9 +- .../integration_tests/abnormal_state.json | 2 +- .../google_sheets_source/spec.json | 6 +- .../integration_tests/catalog.json | 670 +-- .../integration_tests/invalid_config.json | 4 +- .../integration_tests/sample_config.json | 4 +- .../schemas/activity.json | 4 +- .../source_pivotal_tracker/schemas/epics.json | 2 +- .../schemas/project_memberships.json | 2 +- .../schemas/projects.json | 10 +- .../schemas/releases.json | 2 +- .../schemas/shared/label.json | 2 +- .../schemas/shared/person.json | 2 +- .../schemas/stories.json | 2 +- .../unit_tests/responses/activity.json | 3866 ++++++++--------- .../unit_tests/responses/epics.json | 179 +- .../unit_tests/responses/labels.json | 162 +- .../responses/project_memberships.json | 303 +- .../unit_tests/responses/projects.json | 142 +- .../unit_tests/responses/releases.json | 30 +- .../unit_tests/responses/stories.json | 118 +- .../source_zendesk_support/streams.py | 19 +- .../unit_tests/test_futures.py | 9 +- .../unit_tests/unit_test.py | 5 +- 27 files changed, 2396 insertions(+), 3168 deletions(-) diff --git a/airbyte-integrations/connectors/destination-gcs/src/main/java/io/airbyte/integrations/destination/gcs/GcsNameTransformer.java b/airbyte-integrations/connectors/destination-gcs/src/main/java/io/airbyte/integrations/destination/gcs/GcsNameTransformer.java index 83361eac22f73..1072d52289377 100644 --- a/airbyte-integrations/connectors/destination-gcs/src/main/java/io/airbyte/integrations/destination/gcs/GcsNameTransformer.java +++ b/airbyte-integrations/connectors/destination-gcs/src/main/java/io/airbyte/integrations/destination/gcs/GcsNameTransformer.java @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2021 Airbyte, Inc., all rights reserved. + */ + package io.airbyte.integrations.destination.gcs; import io.airbyte.integrations.destination.s3.util.S3NameTransformer; diff --git a/airbyte-integrations/connectors/destination-gcs/src/main/java/io/airbyte/integrations/destination/gcs/GcsStorageOperations.java b/airbyte-integrations/connectors/destination-gcs/src/main/java/io/airbyte/integrations/destination/gcs/GcsStorageOperations.java index fab3e0ee5e803..aec00feb319d3 100644 --- a/airbyte-integrations/connectors/destination-gcs/src/main/java/io/airbyte/integrations/destination/gcs/GcsStorageOperations.java +++ b/airbyte-integrations/connectors/destination-gcs/src/main/java/io/airbyte/integrations/destination/gcs/GcsStorageOperations.java @@ -25,9 +25,8 @@ public GcsStorageOperations(final NamingConventionTransformer nameTransformer, } /** - * This method is overridden because GCS doesn't accept request to delete multiple objects. The - * only difference is that the AmazonS3#deleteObjects method is replaced with - * AmazonS3#deleteObject. + * This method is overridden because GCS doesn't accept request to delete multiple objects. The only + * difference is that the AmazonS3#deleteObjects method is replaced with AmazonS3#deleteObject. */ @Override public void cleanUpBucketObject(final String objectPath, final List stagedFiles) { diff --git a/airbyte-integrations/connectors/destination-gcs/src/test-integration/java/io/airbyte/integrations/destination/gcs/GcsDestinationAcceptanceTest.java b/airbyte-integrations/connectors/destination-gcs/src/test-integration/java/io/airbyte/integrations/destination/gcs/GcsDestinationAcceptanceTest.java index 5af9dfd110cd9..99d38e0929904 100644 --- a/airbyte-integrations/connectors/destination-gcs/src/test-integration/java/io/airbyte/integrations/destination/gcs/GcsDestinationAcceptanceTest.java +++ b/airbyte-integrations/connectors/destination-gcs/src/test-integration/java/io/airbyte/integrations/destination/gcs/GcsDestinationAcceptanceTest.java @@ -17,7 +17,6 @@ import io.airbyte.commons.json.Jsons; import io.airbyte.config.StandardCheckConnectionOutput.Status; import io.airbyte.integrations.destination.NamingConventionTransformer; -import io.airbyte.integrations.destination.s3.S3DestinationConstants; import io.airbyte.integrations.destination.s3.S3Format; import io.airbyte.integrations.destination.s3.S3FormatConfig; import io.airbyte.integrations.destination.s3.S3StorageOperations; diff --git a/airbyte-integrations/connectors/destination-s3/src/main/java/io/airbyte/integrations/destination/s3/SerializedBufferFactory.java b/airbyte-integrations/connectors/destination-s3/src/main/java/io/airbyte/integrations/destination/s3/SerializedBufferFactory.java index 05275cc8e2998..ac3b84f9f9876 100644 --- a/airbyte-integrations/connectors/destination-s3/src/main/java/io/airbyte/integrations/destination/s3/SerializedBufferFactory.java +++ b/airbyte-integrations/connectors/destination-s3/src/main/java/io/airbyte/integrations/destination/s3/SerializedBufferFactory.java @@ -49,15 +49,18 @@ public static CheckedBiFunction { - final Callable createStorageFunctionWithExtension = () -> createStorageFunctionWithoutExtension.apply(AvroSerializedBuffer.DEFAULT_SUFFIX); + final Callable createStorageFunctionWithExtension = + () -> createStorageFunctionWithoutExtension.apply(AvroSerializedBuffer.DEFAULT_SUFFIX); return AvroSerializedBuffer.createFunction((S3AvroFormatConfig) formatConfig, createStorageFunctionWithExtension); } case CSV -> { - final Callable createStorageFunctionWithExtension = () -> createStorageFunctionWithoutExtension.apply(CsvSerializedBuffer.CSV_GZ_SUFFIX); + final Callable createStorageFunctionWithExtension = + () -> createStorageFunctionWithoutExtension.apply(CsvSerializedBuffer.CSV_GZ_SUFFIX); return CsvSerializedBuffer.createFunction((S3CsvFormatConfig) formatConfig, createStorageFunctionWithExtension); } case JSONL -> { - final Callable createStorageFunctionWithExtension = () -> createStorageFunctionWithoutExtension.apply(JsonLSerializedBuffer.JSONL_GZ_SUFFIX); + final Callable createStorageFunctionWithExtension = + () -> createStorageFunctionWithoutExtension.apply(JsonLSerializedBuffer.JSONL_GZ_SUFFIX); return JsonLSerializedBuffer.createFunction((S3JsonlFormatConfig) formatConfig, createStorageFunctionWithExtension); } case PARQUET -> { diff --git a/airbyte-integrations/connectors/source-google-analytics-v4/integration_tests/abnormal_state.json b/airbyte-integrations/connectors/source-google-analytics-v4/integration_tests/abnormal_state.json index 8bbad318c4939..0cb746bf6ba36 100644 --- a/airbyte-integrations/connectors/source-google-analytics-v4/integration_tests/abnormal_state.json +++ b/airbyte-integrations/connectors/source-google-analytics-v4/integration_tests/abnormal_state.json @@ -10,5 +10,5 @@ "daily_active_users": { "ga_date": "2050-05-01" }, "devices": { "ga_date": "2050-05-01" }, "users_per_day": { "ga_date": "2050-05-01" }, - "new_users_per_day": {"ga_date": "2050-05-01"} + "new_users_per_day": { "ga_date": "2050-05-01" } } diff --git a/airbyte-integrations/connectors/source-google-sheets/google_sheets_source/spec.json b/airbyte-integrations/connectors/source-google-sheets/google_sheets_source/spec.json index a9e0d00657832..f89992b4143ed 100644 --- a/airbyte-integrations/connectors/source-google-sheets/google_sheets_source/spec.json +++ b/airbyte-integrations/connectors/source-google-sheets/google_sheets_source/spec.json @@ -4,14 +4,16 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Stripe Source Spec", "type": "object", - "required": ["spreadsheet_id","credentials"], + "required": ["spreadsheet_id", "credentials"], "additionalProperties": true, "properties": { "spreadsheet_id": { "type": "string", "title": "Spreadsheet Link", "description": "The link to your spreadsheet. See this guide for more details.", - "examples": ["https://docs.google.com/spreadsheets/d/1hLd9Qqti3UyLXZB2aFfUWDT7BG-arw2xy4HR3D-dwUb/edit"] + "examples": [ + "https://docs.google.com/spreadsheets/d/1hLd9Qqti3UyLXZB2aFfUWDT7BG-arw2xy4HR3D-dwUb/edit" + ] }, "credentials": { "type": "object", diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json index 42842051d4c1c..ada53b791a9dd 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/catalog.json @@ -7,186 +7,96 @@ "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "created_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "updated_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "project_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "name": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "account_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "version": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "iteration_length": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "current_iteration_number": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "week_start_day": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "point_scale": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "point_scale_is_custom": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "bugs_and_chores_are_estimatable": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "automatic_planning": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "enable_tasks": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "time_zone": { - "type": [ - "null", - "object" - ] + "type": ["null", "object"] }, "velocity_averaged_over": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "number_of_done_iterations_to_show": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "has_google_domain": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "enable_incoming_emails": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "initial_velocity": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "public": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "atom_enabled": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "enable_following": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "show_story_priority": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "project_type": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "start_time": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" } } }, - "supported_sync_modes": [ - "full_refresh" - ], - "source_defined_primary_key": [ - [ - "id" - ] - ] + "supported_sync_modes": ["full_refresh"], + "source_defined_primary_key": [["id"]] }, { "name": "stories", @@ -195,84 +105,45 @@ "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "created_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "updated_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "project_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "name": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "story_type": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "estimate": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "story_priority": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "current_state": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "requested_by_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "owned_by_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "url": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "owner_ids": { "type": "array", @@ -286,63 +157,36 @@ "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "created_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "updated_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "project_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "name": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] } } } }, "accepted_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" } } }, - "supported_sync_modes": [ - "full_refresh" - ], - "source_defined_primary_key": [ - [ - "id" - ] - ] + "supported_sync_modes": ["full_refresh"], + "source_defined_primary_key": [["id"]] }, { "name": "project_memberships", @@ -351,118 +195,64 @@ "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "created_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "updated_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "project_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "favorite": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "project_color": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "role": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "wants_comment_notification_emails": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "will_receive_mention_notifications_or_emails": { - "type": [ - "null", - "boolean" - ] + "type": ["null", "boolean"] }, "person": { "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "name": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "initials": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "email": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "username": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] } } } } }, - "supported_sync_modes": [ - "full_refresh" - ], - "source_defined_primary_key": [ - [ - "id" - ] - ] + "supported_sync_modes": ["full_refresh"], + "source_defined_primary_key": [["id"]] }, { "name": "labels", @@ -470,53 +260,29 @@ "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "created_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "updated_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "project_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "name": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] } } }, - "supported_sync_modes": [ - "full_refresh" - ], - "source_defined_primary_key": [ - [ - "id" - ] - ] + "supported_sync_modes": ["full_refresh"], + "source_defined_primary_key": [["id"]] }, { "name": "releases", @@ -525,60 +291,33 @@ "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "created_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "updated_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "project_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "name": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "current_state": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "url": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "deadline": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "labels": { @@ -587,63 +326,36 @@ "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "created_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "updated_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "project_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "name": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] } } } }, "accepted_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" } } }, - "supported_sync_modes": [ - "full_refresh" - ], - "source_defined_primary_key": [ - [ - "id" - ] - ] + "supported_sync_modes": ["full_refresh"], + "source_defined_primary_key": [["id"]] }, { "name": "epics", @@ -652,102 +364,57 @@ "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "created_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "updated_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "project_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "name": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "url": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "label": { "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "created_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "updated_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "project_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "name": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] } } } } }, - "supported_sync_modes": [ - "full_refresh" - ], - "source_defined_primary_key": [ - [ - "id" - ] - ] + "supported_sync_modes": ["full_refresh"], + "source_defined_primary_key": [["id"]] }, { "name": "activity", @@ -756,88 +423,49 @@ "type": "object", "properties": { "guid": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "project_id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "occured_at": { - "type": [ - "null", - "string" - ], + "type": ["null", "string"], "format": "date-time" }, "performed_by": { "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "name": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "initials": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "email": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "username": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] } } }, "message": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "highlight": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "project_version": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "changes": { "type": "array", @@ -845,71 +473,41 @@ "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "change_type": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] }, "original_values": { - "type": [ - "null", - "object" - ] + "type": ["null", "object"] }, "new_values": { - "type": [ - "null", - "object" - ] + "type": ["null", "object"] } } } }, "primary_resources": { - "type": [ - "null", - "array" - ], + "type": ["null", "array"], "items": { "type": "object", "properties": { "id": { - "type": [ - "null", - "integer" - ] + "type": ["null", "integer"] }, "kind": { - "type": [ - "null", - "string" - ] + "type": ["null", "string"] } } } } } }, - "supported_sync_modes": [ - "full_refresh" - ], - "source_defined_primary_key": [ - [ - "guid" - ] - ] + "supported_sync_modes": ["full_refresh"], + "source_defined_primary_key": [["guid"]] } ] } diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json index eb76800bde3ad..a02eb39320b1b 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/invalid_config.json @@ -1,3 +1,3 @@ -{ +{ "api_token": "5c054d0de3440452190fdc5d5a04d871" -} \ No newline at end of file +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json index 9761db59b865c..5703df17f8f60 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/integration_tests/sample_config.json @@ -1,3 +1,3 @@ -{ +{ "api_token": "" -} \ No newline at end of file +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/activity.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/activity.json index f5189758b86d3..95a0c31c2fd28 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/activity.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/activity.json @@ -25,7 +25,7 @@ "type": ["null", "string"] }, "project_version": { - "type": ["null", "integer"] + "type": ["null", "integer"] }, "changes": { "type": "array", @@ -65,4 +65,4 @@ } } } -} \ No newline at end of file +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/epics.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/epics.json index 6ad1f09e609f2..69ec47fd280bd 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/epics.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/epics.json @@ -29,4 +29,4 @@ "$ref": "label.json" } } -} \ No newline at end of file +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/project_memberships.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/project_memberships.json index 39adc63c16623..de2286f8573fc 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/project_memberships.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/project_memberships.json @@ -38,4 +38,4 @@ "$ref": "person.json" } } -} \ No newline at end of file +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json index dbdede7b14140..c837365ea995d 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/projects.json @@ -48,7 +48,7 @@ }, "automatic_planning": { "type": ["null", "boolean"] - }, + }, "enable_tasks": { "type": ["null", "boolean"] }, @@ -60,16 +60,16 @@ }, "number_of_done_iterations_to_show": { "type": ["null", "integer"] - }, + }, "has_google_domain": { "type": ["null", "boolean"] - }, + }, "enable_incoming_emails": { "type": ["null", "boolean"] }, "initial_velocity": { "type": ["null", "integer"] - }, + }, "public": { "type": ["null", "boolean"] }, @@ -90,4 +90,4 @@ "format": "date-time" } } -} \ No newline at end of file +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json index 710e059e46fd8..62e6760eac393 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/releases.json @@ -43,4 +43,4 @@ "format": "date-time" } } -} \ No newline at end of file +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json index f9237b06756ca..a7f9336b46021 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/label.json @@ -22,4 +22,4 @@ "type": ["null", "string"] } } -} \ No newline at end of file +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/person.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/person.json index 7b1cc94867552..60cee6d960366 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/person.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/shared/person.json @@ -20,4 +20,4 @@ "type": ["null", "string"] } } -} \ No newline at end of file +} diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json index c97c47dc4a98d..359201c1a98c3 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/source_pivotal_tracker/schemas/stories.json @@ -47,7 +47,7 @@ "type": "array", "items": { "type": "integer" - } + } }, "labels": { "type": "array", diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/activity.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/activity.json index ba05af336b702..5452ce58c7e89 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/activity.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/activity.json @@ -1,2104 +1,1764 @@ [ - { - "kind": "story_move_activity", - "guid": "99_65", - "project_version": 65, - "message": "Darth Vader moved 2 stories", - "highlight": "moved", - "changes": - [ - { - "kind": "story", - "change_type": "update", - "id": 562, - "original_values": - { - "before_id": 551 - }, - "new_values": - { - "before_id": 557 - }, - "name": "Garbage smashers on the detention level have malfunctioned", - "story_type": "bug", - "story_priority": "p3" - }, - { - "kind": "story", - "change_type": "update", - "id": 564, - "original_values": - { - "after_id": 557 - }, - "new_values": - { - "after_id": 565 - }, - "name": "Evacuate, in our moment of triumph", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 564, - "name": "Evacuate, in our moment of triumph", - "story_type": "feature", - "url": "http://localhost/story/show/564", - "story_priority": "p3" - }, - { - "kind": "story", - "id": 562, - "name": "Garbage smashers on the detention level have malfunctioned", - "story_type": "bug", - "url": "http://localhost/story/show/562", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_move_activity", - "guid": "99_64", - "project_version": 64, - "message": "Darth Vader moved 2 stories", - "highlight": "moved", - "changes": - [ - { - "kind": "story", - "change_type": "update", - "id": 562, - "original_values": - { - "before_id": 557 - }, - "new_values": - { - "before_id": 551 - }, - "name": "Garbage smashers on the detention level have malfunctioned", - "story_type": "bug", - "story_priority": "p3" - }, - { - "kind": "story", - "change_type": "update", - "id": 564, - "original_values": - { - "after_id": 565 - }, - "new_values": - { - "after_id": 557 - }, - "name": "Evacuate, in our moment of triumph", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 564, - "name": "Evacuate, in our moment of triumph", - "story_type": "feature", - "url": "http://localhost/story/show/564", - "story_priority": "p3" - }, - { - "kind": "story", - "id": 562, - "name": "Garbage smashers on the detention level have malfunctioned", - "story_type": "bug", - "url": "http://localhost/story/show/562", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "comment_create_activity", - "guid": "99_62", - "project_version": 62, - "message": "Darth Vader added comment: \"I want them alive!\"", - "highlight": "added comment:", - "changes": - [ - { - "kind": "comment", - "change_type": "create", - "id": 112, - "new_values": - { - "id": 112, - "story_id": 555, - "text": "I want them alive!", - "person_id": 101, - "created_at": "2022-03-08T12:00:05Z", - "updated_at": "2022-03-08T12:00:05Z", - "file_attachment_ids": - [ - ], - "google_attachment_ids": - [ - ], - "attachment_ids": - [ - ], - "file_attachments": - [ - ], - "google_attachments": - [ - ] - } - }, - { - "kind": "story", - "change_type": "update", - "id": 555, - "original_values": - { - "follower_ids": - [ - ] - }, - "new_values": - { - "follower_ids": - [ - 101 - ] - }, - "name": "Bring me the passengers", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 555, - "name": "Bring me the passengers", - "story_type": "feature", - "url": "http://localhost/story/show/555", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_update_activity", - "guid": "99_56", - "project_version": 56, - "message": "Darth Vader unstarted this feature", - "highlight": "unstarted", - "changes": - [ - { - "kind": "story", - "change_type": "update", - "id": 555, - "original_values": - { - "current_state": "unscheduled", - "before_id": 551, - "after_id": 557 - }, - "new_values": - { - "current_state": "unstarted", - "before_id": 567, - "after_id": 552 - }, - "name": "Bring me the passengers", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 555, - "name": "Bring me the passengers", - "story_type": "feature", - "url": "http://localhost/story/show/555", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_update_activity", - "guid": "99_54", - "project_version": 54, - "message": "Darth Vader rejected this feature", - "highlight": "rejected", - "changes": - [ - { - "kind": "label", - "change_type": "update", - "id": 2009, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 2, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 3, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 1, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 1, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 2, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 3, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 1, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 1, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "plans" - }, - { - "kind": "story", - "change_type": "update", - "id": 559, - "original_values": - { - "current_state": "delivered" - }, - "new_values": - { - "current_state": "rejected" - }, - "name": "All exhaust ports should be shielded", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 559, - "name": "All exhaust ports should be shielded", - "story_type": "feature", - "url": "http://localhost/story/show/559", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_update_activity", - "guid": "99_48", - "project_version": 48, - "message": "Darth Vader started this feature", - "highlight": "started", - "changes": - [ - { - "kind": "label", - "change_type": "update", - "id": 2008, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 4, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 2, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 2, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 4, - "started": 2, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 2, - "started": 1, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "rebel bases" - }, - { - "kind": "label", - "change_type": "update", - "id": 2009, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 5, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 2, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 2, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 3, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 1, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "plans" - }, - { - "kind": "story", - "change_type": "update", - "id": 556, - "original_values": - { - "current_state": "unscheduled", - "before_id": 555, - "after_id": 557 - }, - "new_values": - { - "current_state": "started", - "before_id": 567, - "after_id": 560 - }, - "name": "Interrogate Leia Organa", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 556, - "name": "Interrogate Leia Organa", - "story_type": "feature", - "url": "http://localhost/story/show/556", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_update_activity", - "guid": "99_47", - "project_version": 47, - "message": "Darth Vader accepted this feature", - "highlight": "accepted", - "changes": - [ - { - "kind": "label", - "change_type": "update", - "id": 2008, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 1, - "unscheduled": 2, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 1, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 4, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 2, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 2, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "rebel bases" - }, - { - "kind": "story", - "change_type": "update", - "id": 560, - "original_values": - { - "current_state": "delivered", - "accepted_at": null - }, - "new_values": - { - "current_state": "accepted", - "accepted_at": "2022-03-08T12:00:05Z" - }, - "name": "Destroy Alderaan", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 560, - "name": "Destroy Alderaan", - "story_type": "feature", - "url": "http://localhost/story/show/560", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_update_activity", - "guid": "99_43", - "project_version": 43, - "message": "Darth Vader edited this feature", - "highlight": "edited", - "changes": - [ - { - "kind": "label", - "change_type": "update", - "id": 2008, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 3, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 2, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "rebel bases" - }, - { - "kind": "label", - "change_type": "update", - "id": 2009, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 3, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 5, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 2, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "plans" - }, - { - "kind": "label", - "change_type": "update", - "id": 2015, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 3, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 2, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "diplomatic relations" - }, - { - "kind": "story", - "change_type": "update", - "id": 556, - "original_values": - { - "label_ids": - [ - 2015 - ], - "labels": - [ - "diplomatic relations" - ] - }, - "new_values": - { - "label_ids": - [ - 2009, - 2008 - ], - "labels": - [ - "plans", - "rebel bases" - ] - }, - "name": "Interrogate Leia Organa", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 556, - "name": "Interrogate Leia Organa", - "story_type": "feature", - "url": "http://localhost/story/show/556", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_create_activity", - "guid": "98_2", - "project_version": 2, - "message": "Darth Vader added this feature", - "highlight": "added", - "changes": - [ - { - "kind": "story", - "change_type": "create", - "id": 568, - "new_values": - { - "id": 568, - "project_id": 98, - "name": "Midi-chlorians", - "description": "Without the midi-chlorians, life could not exist, and we would have no knowledge of the Force. They continually speak to us, telling us the will of the Force. When you learn to quiet your mind, you'll hear them speaking to you", - "story_type": "feature", - "current_state": "unstarted", - "estimate": 3, - "requested_by_id": 101, - "owned_by_id": 101, - "owner_ids": - [ - 101 - ], - "label_ids": - [ - ], - "follower_ids": - [ - ], - "created_at": "2022-03-08T12:00:05Z", - "updated_at": "2022-03-08T12:00:05Z", - "blocked_story_ids": - [ - ], - "story_priority": "p3", - "labels": - [ - ] - }, - "name": "Midi-chlorians", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 568, - "name": "Midi-chlorians", - "story_type": "feature", - "url": "http://localhost/story/show/568", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 98, - "name": "Learn About the Force" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_create_activity", - "guid": "99_11", - "project_version": 11, - "message": "Darth Vader added this feature", - "highlight": "added", - "changes": - [ - { - "kind": "story", - "change_type": "create", - "id": 558, - "new_values": - { - "id": 558, - "project_id": 99, - "name": "Contact Lando", - "story_type": "feature", - "current_state": "accepted", - "estimate": 1, - "accepted_at": "2022-03-08T12:00:05Z", - "requested_by_id": 101, - "owner_ids": - [ - ], - "label_ids": - [ - ], - "follower_ids": - [ - ], - "created_at": "2022-03-08T12:00:05Z", - "updated_at": "2022-03-08T12:00:05Z", - "before_id": 557, - "after_id": 553, - "blocked_story_ids": - [ - ], - "story_priority": "p3", - "labels": - [ - ] - }, - "name": "Contact Lando", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 558, - "name": "Contact Lando", - "story_type": "feature", - "url": "http://localhost/story/show/558", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_create_activity", - "guid": "99_10", - "project_version": 10, - "message": "Darth Vader added this feature", - "highlight": "added", - "changes": - [ - { - "kind": "story", - "change_type": "create", - "id": 557, - "new_values": - { - "id": 557, - "project_id": 99, - "name": "Prepare carbonite freezing chamber", - "description": "Test first on Solo, we don't want the Emperor's prize damaged.", - "story_type": "feature", - "current_state": "unscheduled", - "requested_by_id": 101, - "owner_ids": - [ - ], - "label_ids": - [ - 2014 - ], - "follower_ids": - [ - ], - "created_at": "2022-03-08T12:00:05Z", - "updated_at": "2022-03-08T12:00:05Z", - "before_id": 556, - "after_id": 553, - "blocked_story_ids": - [ - ], - "story_priority": "p3", - "labels": - [ - "r&d" - ] - }, - "name": "Prepare carbonite freezing chamber", - "story_type": "feature", - "story_priority": "p3" - }, - { - "kind": "label", - "change_type": "update", - "id": 2014, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "r&d" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 557, - "name": "Prepare carbonite freezing chamber", - "story_type": "feature", - "url": "http://localhost/story/show/557", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_create_activity", - "guid": "99_9", - "project_version": 9, - "message": "Darth Vader added this feature", - "highlight": "added", - "changes": - [ - { - "kind": "story", - "change_type": "create", - "id": 556, - "new_values": - { - "id": 556, - "project_id": 99, - "name": "Interrogate Leia Organa", - "description": "She is proving to be resistant to our mind probes", - "story_type": "feature", - "current_state": "unscheduled", - "requested_by_id": 101, - "owned_by_id": 101, - "owner_ids": - [ - 101, - 105 - ], - "label_ids": - [ - 2015 - ], - "follower_ids": - [ - ], - "created_at": "2022-03-08T12:00:05Z", - "updated_at": "2022-03-08T12:00:05Z", - "before_id": 555, - "after_id": 553, - "blocked_story_ids": - [ - ], - "story_priority": "p3", - "labels": - [ - "diplomatic relations" - ] - }, - "name": "Interrogate Leia Organa", - "story_type": "feature", - "story_priority": "p3" - }, - { - "kind": "label", - "change_type": "update", - "id": 2015, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 1, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 2, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 2, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "diplomatic relations" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 556, - "name": "Interrogate Leia Organa", - "story_type": "feature", - "url": "http://localhost/story/show/556", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_create_activity", - "guid": "99_8", - "project_version": 8, - "message": "Darth Vader added this feature", - "highlight": "added", - "changes": - [ - { - "kind": "story", - "change_type": "create", - "id": 555, - "new_values": - { - "id": 555, - "project_id": 99, - "name": "Bring me the passengers", - "description": "ignore the droids", - "story_type": "feature", - "current_state": "unscheduled", - "requested_by_id": 101, - "owner_ids": - [ - ], - "label_ids": - [ - ], - "follower_ids": - [ - ], - "created_at": "2022-03-08T12:00:05Z", - "updated_at": "2022-03-08T12:00:05Z", - "before_id": 554, - "after_id": 553, - "blocked_story_ids": - [ - ], - "story_priority": "p3", - "labels": - [ - ] - }, - "name": "Bring me the passengers", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 555, - "name": "Bring me the passengers", - "story_type": "feature", - "url": "http://localhost/story/show/555", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_create_activity", - "guid": "99_7", - "project_version": 7, - "message": "Darth Vader added this feature", - "highlight": "added", - "changes": - [ - { - "kind": "story", - "change_type": "create", - "id": 554, - "new_values": - { - "id": 554, - "project_id": 99, - "name": "Identify Bothan spies", - "description": "Infiltrate their spy network.", - "story_type": "feature", - "current_state": "unscheduled", - "requested_by_id": 101, - "owned_by_id": 106, - "owner_ids": - [ - 106 - ], - "label_ids": - [ - ], - "follower_ids": - [ - ], - "created_at": "2022-03-08T12:00:05Z", - "updated_at": "2022-03-08T12:00:05Z", - "before_id": 552, - "after_id": 553, - "blocked_story_ids": - [ - ], - "story_priority": "p3", - "labels": - [ - ] - }, - "name": "Identify Bothan spies", - "story_type": "feature", - "story_priority": "p3" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 554, - "name": "Identify Bothan spies", - "story_type": "feature", - "url": "http://localhost/story/show/554", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - }, - { - "kind": "story_create_activity", - "guid": "99_6", - "project_version": 6, - "message": "Darth Vader added this feature", - "highlight": "added", - "changes": - [ - { - "kind": "story", - "change_type": "create", - "id": 553, - "new_values": - { - "id": 553, - "project_id": 99, - "name": "Build protocol droid", - "description": "I want a friend", - "story_type": "feature", - "current_state": "accepted", - "estimate": 3, - "accepted_at": "2022-03-08T12:00:05Z", - "requested_by_id": 101, - "owned_by_id": 101, - "owner_ids": - [ - 101 - ], - "label_ids": - [ - 2014, - 2008 - ], - "follower_ids": - [ - ], - "created_at": "2022-03-08T12:00:05Z", - "updated_at": "2022-03-08T12:00:05Z", - "before_id": 552, - "blocked_story_ids": - [ - ], - "story_priority": "p3", - "labels": - [ - "r&d", - "rebel bases" - ] - }, - "name": "Build protocol droid", - "story_type": "feature", - "story_priority": "p3" - }, - { - "kind": "label", - "change_type": "update", - "id": 2008, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "rebel bases" - }, - { - "kind": "label", - "change_type": "update", - "id": 2014, - "original_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "new_values": - { - "counts": - { - "number_of_zero_point_stories_by_state": - { - "accepted": 0, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "sum_of_story_estimates_by_state": - { - "accepted": 3, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "number_of_stories_by_state": - { - "accepted": 1, - "started": 0, - "finished": 0, - "unstarted": 0, - "planned": 0, - "delivered": 0, - "unscheduled": 0, - "rejected": 0, - "kind": "counts_by_story_state" - }, - "kind": "story_counts" - } - }, - "name": "r&d" - } - ], - "primary_resources": - [ - { - "kind": "story", - "id": 553, - "name": "Build protocol droid", - "story_type": "feature", - "url": "http://localhost/story/show/553", - "story_priority": "p3" - } - ], - "secondary_resources": - [ - ], - "project": - { - "kind": "project", - "id": 99, - "name": "Death Star" - }, - "performed_by": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "initials": "DV" - }, - "occurred_at": "2022-03-08T12:00:05Z" - } -] \ No newline at end of file + { + "kind": "story_move_activity", + "guid": "99_65", + "project_version": 65, + "message": "Darth Vader moved 2 stories", + "highlight": "moved", + "changes": [ + { + "kind": "story", + "change_type": "update", + "id": 562, + "original_values": { + "before_id": 551 + }, + "new_values": { + "before_id": 557 + }, + "name": "Garbage smashers on the detention level have malfunctioned", + "story_type": "bug", + "story_priority": "p3" + }, + { + "kind": "story", + "change_type": "update", + "id": 564, + "original_values": { + "after_id": 557 + }, + "new_values": { + "after_id": 565 + }, + "name": "Evacuate, in our moment of triumph", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 564, + "name": "Evacuate, in our moment of triumph", + "story_type": "feature", + "url": "http://localhost/story/show/564", + "story_priority": "p3" + }, + { + "kind": "story", + "id": 562, + "name": "Garbage smashers on the detention level have malfunctioned", + "story_type": "bug", + "url": "http://localhost/story/show/562", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_move_activity", + "guid": "99_64", + "project_version": 64, + "message": "Darth Vader moved 2 stories", + "highlight": "moved", + "changes": [ + { + "kind": "story", + "change_type": "update", + "id": 562, + "original_values": { + "before_id": 557 + }, + "new_values": { + "before_id": 551 + }, + "name": "Garbage smashers on the detention level have malfunctioned", + "story_type": "bug", + "story_priority": "p3" + }, + { + "kind": "story", + "change_type": "update", + "id": 564, + "original_values": { + "after_id": 565 + }, + "new_values": { + "after_id": 557 + }, + "name": "Evacuate, in our moment of triumph", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 564, + "name": "Evacuate, in our moment of triumph", + "story_type": "feature", + "url": "http://localhost/story/show/564", + "story_priority": "p3" + }, + { + "kind": "story", + "id": 562, + "name": "Garbage smashers on the detention level have malfunctioned", + "story_type": "bug", + "url": "http://localhost/story/show/562", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "comment_create_activity", + "guid": "99_62", + "project_version": 62, + "message": "Darth Vader added comment: \"I want them alive!\"", + "highlight": "added comment:", + "changes": [ + { + "kind": "comment", + "change_type": "create", + "id": 112, + "new_values": { + "id": 112, + "story_id": 555, + "text": "I want them alive!", + "person_id": 101, + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "file_attachment_ids": [], + "google_attachment_ids": [], + "attachment_ids": [], + "file_attachments": [], + "google_attachments": [] + } + }, + { + "kind": "story", + "change_type": "update", + "id": 555, + "original_values": { + "follower_ids": [] + }, + "new_values": { + "follower_ids": [101] + }, + "name": "Bring me the passengers", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 555, + "name": "Bring me the passengers", + "story_type": "feature", + "url": "http://localhost/story/show/555", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_update_activity", + "guid": "99_56", + "project_version": 56, + "message": "Darth Vader unstarted this feature", + "highlight": "unstarted", + "changes": [ + { + "kind": "story", + "change_type": "update", + "id": 555, + "original_values": { + "current_state": "unscheduled", + "before_id": 551, + "after_id": 557 + }, + "new_values": { + "current_state": "unstarted", + "before_id": 567, + "after_id": 552 + }, + "name": "Bring me the passengers", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 555, + "name": "Bring me the passengers", + "story_type": "feature", + "url": "http://localhost/story/show/555", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_update_activity", + "guid": "99_54", + "project_version": 54, + "message": "Darth Vader rejected this feature", + "highlight": "rejected", + "changes": [ + { + "kind": "label", + "change_type": "update", + "id": 2009, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 2, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 3, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 1, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 1, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 2, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 3, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 1, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 1, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "plans" + }, + { + "kind": "story", + "change_type": "update", + "id": 559, + "original_values": { + "current_state": "delivered" + }, + "new_values": { + "current_state": "rejected" + }, + "name": "All exhaust ports should be shielded", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 559, + "name": "All exhaust ports should be shielded", + "story_type": "feature", + "url": "http://localhost/story/show/559", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_update_activity", + "guid": "99_48", + "project_version": 48, + "message": "Darth Vader started this feature", + "highlight": "started", + "changes": [ + { + "kind": "label", + "change_type": "update", + "id": 2008, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 4, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 2, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 4, + "started": 2, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 2, + "started": 1, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "rebel bases" + }, + { + "kind": "label", + "change_type": "update", + "id": 2009, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 5, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 2, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 3, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 1, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "plans" + }, + { + "kind": "story", + "change_type": "update", + "id": 556, + "original_values": { + "current_state": "unscheduled", + "before_id": 555, + "after_id": 557 + }, + "new_values": { + "current_state": "started", + "before_id": 567, + "after_id": 560 + }, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 556, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "url": "http://localhost/story/show/556", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_update_activity", + "guid": "99_47", + "project_version": 47, + "message": "Darth Vader accepted this feature", + "highlight": "accepted", + "changes": [ + { + "kind": "label", + "change_type": "update", + "id": 2008, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 1, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 1, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 4, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 2, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "rebel bases" + }, + { + "kind": "story", + "change_type": "update", + "id": 560, + "original_values": { + "current_state": "delivered", + "accepted_at": null + }, + "new_values": { + "current_state": "accepted", + "accepted_at": "2022-03-08T12:00:05Z" + }, + "name": "Destroy Alderaan", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 560, + "name": "Destroy Alderaan", + "story_type": "feature", + "url": "http://localhost/story/show/560", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_update_activity", + "guid": "99_43", + "project_version": 43, + "message": "Darth Vader edited this feature", + "highlight": "edited", + "changes": [ + { + "kind": "label", + "change_type": "update", + "id": 2008, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 3, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "rebel bases" + }, + { + "kind": "label", + "change_type": "update", + "id": 2009, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 3, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 5, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "plans" + }, + { + "kind": "label", + "change_type": "update", + "id": 2015, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 3, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "diplomatic relations" + }, + { + "kind": "story", + "change_type": "update", + "id": 556, + "original_values": { + "label_ids": [2015], + "labels": ["diplomatic relations"] + }, + "new_values": { + "label_ids": [2009, 2008], + "labels": ["plans", "rebel bases"] + }, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 556, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "url": "http://localhost/story/show/556", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "98_2", + "project_version": 2, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": [ + { + "kind": "story", + "change_type": "create", + "id": 568, + "new_values": { + "id": 568, + "project_id": 98, + "name": "Midi-chlorians", + "description": "Without the midi-chlorians, life could not exist, and we would have no knowledge of the Force. They continually speak to us, telling us the will of the Force. When you learn to quiet your mind, you'll hear them speaking to you", + "story_type": "feature", + "current_state": "unstarted", + "estimate": 3, + "requested_by_id": 101, + "owned_by_id": 101, + "owner_ids": [101], + "label_ids": [], + "follower_ids": [], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "blocked_story_ids": [], + "story_priority": "p3", + "labels": [] + }, + "name": "Midi-chlorians", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 568, + "name": "Midi-chlorians", + "story_type": "feature", + "url": "http://localhost/story/show/568", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 98, + "name": "Learn About the Force" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_11", + "project_version": 11, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": [ + { + "kind": "story", + "change_type": "create", + "id": 558, + "new_values": { + "id": 558, + "project_id": 99, + "name": "Contact Lando", + "story_type": "feature", + "current_state": "accepted", + "estimate": 1, + "accepted_at": "2022-03-08T12:00:05Z", + "requested_by_id": 101, + "owner_ids": [], + "label_ids": [], + "follower_ids": [], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 557, + "after_id": 553, + "blocked_story_ids": [], + "story_priority": "p3", + "labels": [] + }, + "name": "Contact Lando", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 558, + "name": "Contact Lando", + "story_type": "feature", + "url": "http://localhost/story/show/558", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_10", + "project_version": 10, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": [ + { + "kind": "story", + "change_type": "create", + "id": 557, + "new_values": { + "id": 557, + "project_id": 99, + "name": "Prepare carbonite freezing chamber", + "description": "Test first on Solo, we don't want the Emperor's prize damaged.", + "story_type": "feature", + "current_state": "unscheduled", + "requested_by_id": 101, + "owner_ids": [], + "label_ids": [2014], + "follower_ids": [], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 556, + "after_id": 553, + "blocked_story_ids": [], + "story_priority": "p3", + "labels": ["r&d"] + }, + "name": "Prepare carbonite freezing chamber", + "story_type": "feature", + "story_priority": "p3" + }, + { + "kind": "label", + "change_type": "update", + "id": 2014, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "r&d" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 557, + "name": "Prepare carbonite freezing chamber", + "story_type": "feature", + "url": "http://localhost/story/show/557", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_9", + "project_version": 9, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": [ + { + "kind": "story", + "change_type": "create", + "id": 556, + "new_values": { + "id": 556, + "project_id": 99, + "name": "Interrogate Leia Organa", + "description": "She is proving to be resistant to our mind probes", + "story_type": "feature", + "current_state": "unscheduled", + "requested_by_id": 101, + "owned_by_id": 101, + "owner_ids": [101, 105], + "label_ids": [2015], + "follower_ids": [], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 555, + "after_id": 553, + "blocked_story_ids": [], + "story_priority": "p3", + "labels": ["diplomatic relations"] + }, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "story_priority": "p3" + }, + { + "kind": "label", + "change_type": "update", + "id": 2015, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 1, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 2, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "diplomatic relations" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 556, + "name": "Interrogate Leia Organa", + "story_type": "feature", + "url": "http://localhost/story/show/556", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_8", + "project_version": 8, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": [ + { + "kind": "story", + "change_type": "create", + "id": 555, + "new_values": { + "id": 555, + "project_id": 99, + "name": "Bring me the passengers", + "description": "ignore the droids", + "story_type": "feature", + "current_state": "unscheduled", + "requested_by_id": 101, + "owner_ids": [], + "label_ids": [], + "follower_ids": [], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 554, + "after_id": 553, + "blocked_story_ids": [], + "story_priority": "p3", + "labels": [] + }, + "name": "Bring me the passengers", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 555, + "name": "Bring me the passengers", + "story_type": "feature", + "url": "http://localhost/story/show/555", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_7", + "project_version": 7, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": [ + { + "kind": "story", + "change_type": "create", + "id": 554, + "new_values": { + "id": 554, + "project_id": 99, + "name": "Identify Bothan spies", + "description": "Infiltrate their spy network.", + "story_type": "feature", + "current_state": "unscheduled", + "requested_by_id": 101, + "owned_by_id": 106, + "owner_ids": [106], + "label_ids": [], + "follower_ids": [], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 552, + "after_id": 553, + "blocked_story_ids": [], + "story_priority": "p3", + "labels": [] + }, + "name": "Identify Bothan spies", + "story_type": "feature", + "story_priority": "p3" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 554, + "name": "Identify Bothan spies", + "story_type": "feature", + "url": "http://localhost/story/show/554", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + }, + { + "kind": "story_create_activity", + "guid": "99_6", + "project_version": 6, + "message": "Darth Vader added this feature", + "highlight": "added", + "changes": [ + { + "kind": "story", + "change_type": "create", + "id": 553, + "new_values": { + "id": 553, + "project_id": 99, + "name": "Build protocol droid", + "description": "I want a friend", + "story_type": "feature", + "current_state": "accepted", + "estimate": 3, + "accepted_at": "2022-03-08T12:00:05Z", + "requested_by_id": 101, + "owned_by_id": 101, + "owner_ids": [101], + "label_ids": [2014, 2008], + "follower_ids": [], + "created_at": "2022-03-08T12:00:05Z", + "updated_at": "2022-03-08T12:00:05Z", + "before_id": 552, + "blocked_story_ids": [], + "story_priority": "p3", + "labels": ["r&d", "rebel bases"] + }, + "name": "Build protocol droid", + "story_type": "feature", + "story_priority": "p3" + }, + { + "kind": "label", + "change_type": "update", + "id": 2008, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "rebel bases" + }, + { + "kind": "label", + "change_type": "update", + "id": 2014, + "original_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "new_values": { + "counts": { + "number_of_zero_point_stories_by_state": { + "accepted": 0, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "sum_of_story_estimates_by_state": { + "accepted": 3, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "number_of_stories_by_state": { + "accepted": 1, + "started": 0, + "finished": 0, + "unstarted": 0, + "planned": 0, + "delivered": 0, + "unscheduled": 0, + "rejected": 0, + "kind": "counts_by_story_state" + }, + "kind": "story_counts" + } + }, + "name": "r&d" + } + ], + "primary_resources": [ + { + "kind": "story", + "id": 553, + "name": "Build protocol droid", + "story_type": "feature", + "url": "http://localhost/story/show/553", + "story_priority": "p3" + } + ], + "secondary_resources": [], + "project": { + "kind": "project", + "id": 99, + "name": "Death Star" + }, + "performed_by": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "initials": "DV" + }, + "occurred_at": "2022-03-08T12:00:05Z" + } +] diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/epics.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/epics.json index 65bb1873361bf..9b446061ee56e 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/epics.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/epics.json @@ -1,93 +1,88 @@ [ - { - "id": 555, - "kind": "epic", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z", - "project_id": 99, - "name": "Sanitation", - "url": "http://localhost/epic/show/555", - "label": - { - "id": 2017, - "project_id": 99, - "kind": "label", - "name": "sanitation", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z" - } - }, - { - "id": 8, - "kind": "epic", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z", - "project_id": 99, - "name": "Maintenance", - "url": "http://localhost/epic/show/8", - "label": - { - "id": 2011, - "project_id": 99, - "kind": "label", - "name": "mnt", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z" - } - }, - { - "id": 7, - "kind": "epic", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z", - "project_id": 99, - "name": "Turn Luke Skywalker", - "url": "http://localhost/epic/show/7", - "label": - { - "id": 2010, - "project_id": 99, - "kind": "label", - "name": "turning luke", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z" - } - }, - { - "id": 6, - "kind": "epic", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z", - "project_id": 99, - "name": "Death Star Plans", - "url": "http://localhost/epic/show/6", - "label": - { - "id": 2009, - "project_id": 99, - "kind": "label", - "name": "plans", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z" - } - }, - { - "id": 5, - "kind": "epic", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z", - "project_id": 99, - "name": "Rebel Home Worlds", - "description": "Identify the systems and eliminate the rebel scum.", - "url": "http://localhost/epic/show/5", - "label": - { - "id": 2008, - "project_id": 99, - "kind": "label", - "name": "rebel bases", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z" - } - } -] \ No newline at end of file + { + "id": 555, + "kind": "epic", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "project_id": 99, + "name": "Sanitation", + "url": "http://localhost/epic/show/555", + "label": { + "id": 2017, + "project_id": 99, + "kind": "label", + "name": "sanitation", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + }, + { + "id": 8, + "kind": "epic", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "project_id": 99, + "name": "Maintenance", + "url": "http://localhost/epic/show/8", + "label": { + "id": 2011, + "project_id": 99, + "kind": "label", + "name": "mnt", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + }, + { + "id": 7, + "kind": "epic", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "project_id": 99, + "name": "Turn Luke Skywalker", + "url": "http://localhost/epic/show/7", + "label": { + "id": 2010, + "project_id": 99, + "kind": "label", + "name": "turning luke", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + }, + { + "id": 6, + "kind": "epic", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "project_id": 99, + "name": "Death Star Plans", + "url": "http://localhost/epic/show/6", + "label": { + "id": 2009, + "project_id": 99, + "kind": "label", + "name": "plans", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + }, + { + "id": 5, + "kind": "epic", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "project_id": 99, + "name": "Rebel Home Worlds", + "description": "Identify the systems and eliminate the rebel scum.", + "url": "http://localhost/epic/show/5", + "label": { + "id": 2008, + "project_id": 99, + "kind": "label", + "name": "rebel bases", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + } +] diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/labels.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/labels.json index b40d7cfece51a..89df254bea052 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/labels.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/labels.json @@ -1,82 +1,82 @@ [ - { - "kind": "label", - "id": 2015, - "project_id": 99, - "name": "diplomatic relations", - "created_at": 1646740800000, - "updated_at": 1646740800000 - }, - { - "kind": "label", - "id": 2013, - "project_id": 99, - "name": "fleet ops", - "created_at": 1646740800000, - "updated_at": 1646740800000 - }, - { - "kind": "label", - "id": 2011, - "project_id": 99, - "name": "mnt", - "created_at": 1646740800000, - "updated_at": 1646740800000 - }, - { - "kind": "label", - "id": 2012, - "project_id": 99, - "name": "personnel", - "created_at": 1646740800000, - "updated_at": 1646740800000 - }, - { - "kind": "label", - "id": 2009, - "project_id": 99, - "name": "plans", - "created_at": 1646740800000, - "updated_at": 1646740800000 - }, - { - "kind": "label", - "id": 2014, - "project_id": 99, - "name": "r&d", - "created_at": 1646740800000, - "updated_at": 1646740800000 - }, - { - "kind": "label", - "id": 2008, - "project_id": 99, - "name": "rebel bases", - "created_at": 1646740800000, - "updated_at": 1646740800000 - }, - { - "kind": "label", - "id": 2017, - "project_id": 99, - "name": "sanitation", - "created_at": 1646740800000, - "updated_at": 1646740800000 - }, - { - "kind": "label", - "id": 2016, - "project_id": 99, - "name": "security protocol", - "created_at": 1646740800000, - "updated_at": 1646740800000 - }, - { - "kind": "label", - "id": 2010, - "project_id": 99, - "name": "turning luke", - "created_at": 1646740800000, - "updated_at": 1646740800000 - } -] \ No newline at end of file + { + "kind": "label", + "id": 2015, + "project_id": 99, + "name": "diplomatic relations", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2013, + "project_id": 99, + "name": "fleet ops", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2011, + "project_id": 99, + "name": "mnt", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2012, + "project_id": 99, + "name": "personnel", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2009, + "project_id": 99, + "name": "plans", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2014, + "project_id": 99, + "name": "r&d", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2008, + "project_id": 99, + "name": "rebel bases", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2017, + "project_id": 99, + "name": "sanitation", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2016, + "project_id": 99, + "name": "security protocol", + "created_at": 1646740800000, + "updated_at": 1646740800000 + }, + { + "kind": "label", + "id": 2010, + "project_id": 99, + "name": "turning luke", + "created_at": 1646740800000, + "updated_at": 1646740800000 + } +] diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/project_memberships.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/project_memberships.json index c77c7f75f6c83..be178006538b3 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/project_memberships.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/project_memberships.json @@ -1,156 +1,149 @@ [ - { - "kind": "project_membership", - "project_id": 99, - "id": 100, - "last_viewed_at": "2022-03-08T12:00:10Z", - "created_at": "2022-03-08T12:00:10Z", - "updated_at": "2022-03-08T12:00:10Z", - "role": "owner", - "project_color": "8100ea", - "favorite": false, - "wants_comment_notification_emails": false, - "will_receive_mention_notifications_or_emails": true, - "person": - { - "kind": "person", - "id": 100, - "name": "Emperor Palpatine", - "email": "emperor@galacticrepublic.gov", - "initials": "EP", - "username": "palpatine" - } - }, - { - "kind": "project_membership", - "project_id": 99, - "id": 101, - "last_viewed_at": "2022-03-08T12:00:10Z", - "created_at": "2022-03-08T12:00:10Z", - "updated_at": "2022-03-08T12:00:10Z", - "role": "member", - "project_color": "8100ea", - "favorite": false, - "wants_comment_notification_emails": true, - "will_receive_mention_notifications_or_emails": true, - "person": - { - "kind": "person", - "id": 101, - "name": "Darth Vader", - "email": "vader@deathstar.mil", - "initials": "DV", - "username": "vader" - } - }, - { - "kind": "project_membership", - "project_id": 99, - "id": 102, - "last_viewed_at": "2022-03-08T12:00:10Z", - "created_at": "2022-03-08T12:00:10Z", - "updated_at": "2022-03-08T12:00:10Z", - "role": "owner", - "project_color": "8100ea", - "favorite": false, - "wants_comment_notification_emails": false, - "will_receive_mention_notifications_or_emails": true, - "person": - { - "kind": "person", - "id": 102, - "name": "Wilhuff Tarkin", - "email": "governor@eriadu.gov", - "initials": "WT", - "username": "tarkin" - } - }, - { - "kind": "project_membership", - "project_id": 99, - "id": 103, - "last_viewed_at": "2022-03-08T12:00:10Z", - "created_at": "2022-03-08T12:00:10Z", - "updated_at": "2022-03-08T12:00:10Z", - "role": "member", - "project_color": "8100ea", - "favorite": false, - "wants_comment_notification_emails": true, - "will_receive_mention_notifications_or_emails": true, - "person": - { - "kind": "person", - "id": 104, - "name": "Clone TK421", - "email": "tk421@deathstar.mil", - "initials": "TK421", - "username": "tk421" - } - }, - { - "kind": "project_membership", - "project_id": 99, - "id": 104, - "last_viewed_at": "2022-03-08T12:00:10Z", - "created_at": "2022-03-08T12:00:10Z", - "updated_at": "2022-03-08T12:00:10Z", - "role": "member", - "project_color": "8100ea", - "favorite": false, - "wants_comment_notification_emails": true, - "will_receive_mention_notifications_or_emails": true, - "person": - { - "kind": "person", - "id": 103, - "name": "Moradmin Bast", - "email": "bast@deathstar.mil", - "initials": "MB", - "username": "bast" - } - }, - { - "kind": "project_membership", - "project_id": 99, - "id": 105, - "last_viewed_at": "2022-03-08T12:00:10Z", - "created_at": "2022-03-08T12:00:10Z", - "updated_at": "2022-03-08T12:00:10Z", - "role": "member", - "project_color": "8100ea", - "favorite": false, - "wants_comment_notification_emails": true, - "will_receive_mention_notifications_or_emails": true, - "person": - { - "kind": "person", - "id": 105, - "name": "Robotic Maintenance Team 4", - "email": "I662@deathstar.mil", - "initials": "i662", - "username": "i662" - } - }, - { - "kind": "project_membership", - "project_id": 99, - "id": 107, - "last_viewed_at": "2022-03-08T12:00:05Z", - "created_at": "2022-03-08T12:00:10Z", - "updated_at": "2022-03-08T12:00:10Z", - "role": "viewer", - "project_color": "8100ea", - "favorite": false, - "wants_comment_notification_emails": false, - "will_receive_mention_notifications_or_emails": true, - "person": - { - "kind": "person", - "id": 107, - "name": "Bevel Lemelisk", - "email": "bevel@sith.mil", - "initials": "bl", - "username": "bevel" - } - } -] \ No newline at end of file + { + "kind": "project_membership", + "project_id": 99, + "id": 100, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "owner", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": false, + "will_receive_mention_notifications_or_emails": true, + "person": { + "kind": "person", + "id": 100, + "name": "Emperor Palpatine", + "email": "emperor@galacticrepublic.gov", + "initials": "EP", + "username": "palpatine" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 101, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "member", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": true, + "will_receive_mention_notifications_or_emails": true, + "person": { + "kind": "person", + "id": 101, + "name": "Darth Vader", + "email": "vader@deathstar.mil", + "initials": "DV", + "username": "vader" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 102, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "owner", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": false, + "will_receive_mention_notifications_or_emails": true, + "person": { + "kind": "person", + "id": 102, + "name": "Wilhuff Tarkin", + "email": "governor@eriadu.gov", + "initials": "WT", + "username": "tarkin" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 103, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "member", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": true, + "will_receive_mention_notifications_or_emails": true, + "person": { + "kind": "person", + "id": 104, + "name": "Clone TK421", + "email": "tk421@deathstar.mil", + "initials": "TK421", + "username": "tk421" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 104, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "member", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": true, + "will_receive_mention_notifications_or_emails": true, + "person": { + "kind": "person", + "id": 103, + "name": "Moradmin Bast", + "email": "bast@deathstar.mil", + "initials": "MB", + "username": "bast" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 105, + "last_viewed_at": "2022-03-08T12:00:10Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "member", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": true, + "will_receive_mention_notifications_or_emails": true, + "person": { + "kind": "person", + "id": 105, + "name": "Robotic Maintenance Team 4", + "email": "I662@deathstar.mil", + "initials": "i662", + "username": "i662" + } + }, + { + "kind": "project_membership", + "project_id": 99, + "id": 107, + "last_viewed_at": "2022-03-08T12:00:05Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:10Z", + "role": "viewer", + "project_color": "8100ea", + "favorite": false, + "wants_comment_notification_emails": false, + "will_receive_mention_notifications_or_emails": true, + "person": { + "kind": "person", + "id": 107, + "name": "Bevel Lemelisk", + "email": "bevel@sith.mil", + "initials": "bl", + "username": "bevel" + } + } +] diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/projects.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/projects.json index 0102172ed15b4..de302e91d383c 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/projects.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/projects.json @@ -1,73 +1,71 @@ [ - { - "id": 98, - "kind": "project", - "name": "Learn About the Force", - "version": 2, - "iteration_length": 1, - "week_start_day": "Monday", - "point_scale": "0,1,2,3", - "point_scale_is_custom": false, - "bugs_and_chores_are_estimatable": false, - "automatic_planning": true, - "enable_tasks": true, - "time_zone": - { - "kind": "time_zone", - "olson_name": "America/Los_Angeles", - "offset": "-08:00" - }, - "velocity_averaged_over": 3, - "number_of_done_iterations_to_show": 4, - "has_google_domain": false, - "enable_incoming_emails": true, - "initial_velocity": 10, - "public": false, - "atom_enabled": true, - "project_type": "private", - "start_time": "2022-03-08T12:00:05Z", - "created_at": "2022-03-08T12:00:10Z", - "updated_at": "2022-03-08T12:00:15Z", - "show_story_priority": true, - "account_id": 100, - "current_iteration_number": 1, - "enable_following": true - }, - { - "id": 99, - "kind": "project", - "name": "Death Star", - "version": 66, - "iteration_length": 1, - "week_start_day": "Monday", - "point_scale": "0,1,2,3", - "point_scale_is_custom": false, - "bugs_and_chores_are_estimatable": false, - "automatic_planning": true, - "enable_tasks": true, - "time_zone": - { - "kind": "time_zone", - "olson_name": "America/Los_Angeles", - "offset": "-08:00" - }, - "velocity_averaged_over": 3, - "number_of_done_iterations_to_show": 4, - "has_google_domain": false, - "description": "Expeditionary Battle Planetoid", - "profile_content": "This is a machine of war such as the universe has never known. It's colossal, the size of a class-four moon. And it possesses firepower unequaled in the history of warfare.", - "enable_incoming_emails": true, - "initial_velocity": 10, - "public": false, - "atom_enabled": true, - "project_type": "private", - "start_date": "2021-11-22", - "start_time": "2022-03-08T12:00:15Z", - "created_at": "2022-03-08T12:00:10Z", - "updated_at": "2022-03-08T12:00:15Z", - "show_story_priority": true, - "account_id": 100, - "current_iteration_number": 15, - "enable_following": true - } -] \ No newline at end of file + { + "id": 98, + "kind": "project", + "name": "Learn About the Force", + "version": 2, + "iteration_length": 1, + "week_start_day": "Monday", + "point_scale": "0,1,2,3", + "point_scale_is_custom": false, + "bugs_and_chores_are_estimatable": false, + "automatic_planning": true, + "enable_tasks": true, + "time_zone": { + "kind": "time_zone", + "olson_name": "America/Los_Angeles", + "offset": "-08:00" + }, + "velocity_averaged_over": 3, + "number_of_done_iterations_to_show": 4, + "has_google_domain": false, + "enable_incoming_emails": true, + "initial_velocity": 10, + "public": false, + "atom_enabled": true, + "project_type": "private", + "start_time": "2022-03-08T12:00:05Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:15Z", + "show_story_priority": true, + "account_id": 100, + "current_iteration_number": 1, + "enable_following": true + }, + { + "id": 99, + "kind": "project", + "name": "Death Star", + "version": 66, + "iteration_length": 1, + "week_start_day": "Monday", + "point_scale": "0,1,2,3", + "point_scale_is_custom": false, + "bugs_and_chores_are_estimatable": false, + "automatic_planning": true, + "enable_tasks": true, + "time_zone": { + "kind": "time_zone", + "olson_name": "America/Los_Angeles", + "offset": "-08:00" + }, + "velocity_averaged_over": 3, + "number_of_done_iterations_to_show": 4, + "has_google_domain": false, + "description": "Expeditionary Battle Planetoid", + "profile_content": "This is a machine of war such as the universe has never known. It's colossal, the size of a class-four moon. And it possesses firepower unequaled in the history of warfare.", + "enable_incoming_emails": true, + "initial_velocity": 10, + "public": false, + "atom_enabled": true, + "project_type": "private", + "start_date": "2021-11-22", + "start_time": "2022-03-08T12:00:15Z", + "created_at": "2022-03-08T12:00:10Z", + "updated_at": "2022-03-08T12:00:15Z", + "show_story_priority": true, + "account_id": 100, + "current_iteration_number": 15, + "enable_following": true + } +] diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/releases.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/releases.json index 8cca75d2b1cf8..708305643438b 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/releases.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/releases.json @@ -1,17 +1,15 @@ [ - { - "kind": "release", - "id": 552, - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z", - "deadline": "2022-03-08T12:00:05Z", - "name": "Battlestation fully operational", - "description": "Everything is proceeding as I have foreseen.", - "current_state": "unstarted", - "url": "http://localhost/story/show/552", - "project_id": 99, - "labels": - [ - ] - } -] \ No newline at end of file + { + "kind": "release", + "id": 552, + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "deadline": "2022-03-08T12:00:05Z", + "name": "Battlestation fully operational", + "description": "Everything is proceeding as I have foreseen.", + "current_state": "unstarted", + "url": "http://localhost/story/show/552", + "project_id": 99, + "labels": [] + } +] diff --git a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/stories.json b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/stories.json index 2529c19382e13..71f49a2040d85 100644 --- a/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/stories.json +++ b/airbyte-integrations/connectors/source-pivotal-tracker/unit_tests/responses/stories.json @@ -1,71 +1,49 @@ [ - { - "id": 561, - "current_state": "unstarted", - "labels": - [ - ], - "tasks": - [ - ], - "comments": - [ - ] - }, - { - "id": 566, - "current_state": "unstarted", - "labels": - [ - { - "id": 2011, - "project_id": 99, - "kind": "label", - "name": "mnt", - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z" - } - ], - "tasks": - [ - ], - "comments": - [ - ] - }, - { - "id": 552, - "current_state": "unstarted", - "labels": - [ - ], - "tasks": - [ - ], - "comments": - [ - ] - }, - { - "id": 555, - "current_state": "unstarted", - "labels": - [ - ], - "tasks": - [ - ], - "comments": - [ - { - "kind": "comment", - "id": 112, - "text": "I want them alive!", - "person_id": 101, - "created_at": "2022-03-08T12:00:00Z", - "updated_at": "2022-03-08T12:00:00Z", - "story_id": 555 - } - ] - } -] \ No newline at end of file + { + "id": 561, + "current_state": "unstarted", + "labels": [], + "tasks": [], + "comments": [] + }, + { + "id": 566, + "current_state": "unstarted", + "labels": [ + { + "id": 2011, + "project_id": 99, + "kind": "label", + "name": "mnt", + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z" + } + ], + "tasks": [], + "comments": [] + }, + { + "id": 552, + "current_state": "unstarted", + "labels": [], + "tasks": [], + "comments": [] + }, + { + "id": 555, + "current_state": "unstarted", + "labels": [], + "tasks": [], + "comments": [ + { + "kind": "comment", + "id": 112, + "text": "I want them alive!", + "person_id": 101, + "created_at": "2022-03-08T12:00:00Z", + "updated_at": "2022-03-08T12:00:00Z", + "story_id": 555 + } + ] + } +] diff --git a/airbyte-integrations/connectors/source-zendesk-support/source_zendesk_support/streams.py b/airbyte-integrations/connectors/source-zendesk-support/source_zendesk_support/streams.py index 34ee66d53ee70..a47621dca3d55 100644 --- a/airbyte-integrations/connectors/source-zendesk-support/source_zendesk_support/streams.py +++ b/airbyte-integrations/connectors/source-zendesk-support/source_zendesk_support/streams.py @@ -367,32 +367,32 @@ class SourceZendeskTicketExportStream(SourceZendeskSupportCursorPaginationStream @ param sideload_param : parameter variable to include various information to response more info: https://developer.zendesk.com/documentation/ticketing/using-the-zendesk-api/side_loading/#supported-endpoints """ - + cursor_field = "updated_at" response_list_name: str = "tickets" sideload_param: str = None - + @staticmethod def check_start_time_param(requested_start_time: int, value: int = 1): """ Requesting tickets in the future is not allowed, hits 400 - bad request. We get current UNIX timestamp minus `value` from now(), default = 1 (minute). - + Returns: either close to now UNIX timestamp or previously requested UNIX timestamp. - """ + """ now = calendar.timegm(pendulum.now().subtract(minutes=value).utctimetuple()) return now if requested_start_time > now else requested_start_time - + def path(self, **kwargs) -> str: return f"incremental/{self.response_list_name}.json" - + def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]: """ Returns next_page_token based on `end_of_stream` parameter inside of response """ next_page_token = super().next_page_token(response) return None if response.json().get(END_OF_STREAM_KEY, False) else next_page_token - + def request_params( self, stream_state: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None, **kwargs ) -> MutableMapping[str, Any]: @@ -402,7 +402,7 @@ def request_params( if self.sideload_param: params["include"] = self.sideload_param return params - + def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]: for record in response.json().get(self.response_list_name, []): yield record @@ -417,6 +417,7 @@ class SourceZendeskSupportTicketEventsExportStream(SourceZendeskTicketExportStre @ param list_entities_from_event : the list of nested child_events entities to include from parent record @ param event_type : specific event_type to check ["Audit", "Change", "Comment", etc] """ + cursor_field = "created_at" response_list_name: str = "ticket_events" response_target_entity: str = "child_events" @@ -427,7 +428,7 @@ class SourceZendeskSupportTicketEventsExportStream(SourceZendeskTicketExportStre def update_event_from_record(self) -> bool: """Returns True/False based on list_entities_from_event property""" return True if len(self.list_entities_from_event) > 0 else False - + def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]: for record in super().parse_response(response, **kwargs): for event in record.get(self.response_target_entity, []): diff --git a/airbyte-integrations/connectors/source-zendesk-support/unit_tests/test_futures.py b/airbyte-integrations/connectors/source-zendesk-support/unit_tests/test_futures.py index 50c334fefb688..fb86fe2cf2b3e 100644 --- a/airbyte-integrations/connectors/source-zendesk-support/unit_tests/test_futures.py +++ b/airbyte-integrations/connectors/source-zendesk-support/unit_tests/test_futures.py @@ -14,12 +14,11 @@ from source_zendesk_support.source import BasicApiTokenAuthenticator from source_zendesk_support.streams import Macros - STREAM_ARGS: dict = { - "subdomain": "fake-subdomain", - "start_date": "2021-01-27T00:00:00Z", - "authenticator": BasicApiTokenAuthenticator("test@airbyte.io", "api_token"), - } + "subdomain": "fake-subdomain", + "start_date": "2021-01-27T00:00:00Z", + "authenticator": BasicApiTokenAuthenticator("test@airbyte.io", "api_token"), +} @pytest.mark.parametrize( diff --git a/airbyte-integrations/connectors/source-zendesk-support/unit_tests/unit_test.py b/airbyte-integrations/connectors/source-zendesk-support/unit_tests/unit_test.py index 0a98d9ebeb72c..42f6034da5193 100644 --- a/airbyte-integrations/connectors/source-zendesk-support/unit_tests/unit_test.py +++ b/airbyte-integrations/connectors/source-zendesk-support/unit_tests/unit_test.py @@ -14,8 +14,8 @@ DATETIME_FORMAT, END_OF_STREAM_KEY, BaseSourceZendeskSupportStream, - TicketComments, SourceZendeskTicketExportStream, + TicketComments, ) # config @@ -83,7 +83,8 @@ def test_str2unixtime(): expected = calendar.timegm(DATETIME_FROM_STR.utctimetuple()) output = BaseSourceZendeskSupportStream.str2unixtime(DATETIME_STR) assert output == expected - + + def test_check_start_time_param(): expected = 1626936955 start_time = calendar.timegm(pendulum.parse(DATETIME_STR).utctimetuple()) From 3df9cd4a90930643c6d81ee8dbeaf78cfeb2cbae Mon Sep 17 00:00:00 2001 From: marcosmarxm Date: Mon, 4 Apr 2022 17:36:15 -0700 Subject: [PATCH 18/20] add doc --- .../resources/seed/source_definitions.yaml | 6 +++ docs/integrations/sources/pivotal-tracker.md | 53 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 docs/integrations/sources/pivotal-tracker.md diff --git a/airbyte-config/init/src/main/resources/seed/source_definitions.yaml b/airbyte-config/init/src/main/resources/seed/source_definitions.yaml index 7e704567cedd1..a20ac25c4681f 100644 --- a/airbyte-config/init/src/main/resources/seed/source_definitions.yaml +++ b/airbyte-config/init/src/main/resources/seed/source_definitions.yaml @@ -589,6 +589,12 @@ documentationUrl: https://docs.airbyte.io/integrations/sources/pipedrive icon: pipedrive.svg sourceType: api +- name: Pivotal Tracker + sourceDefinitionId: d8286229-c680-4063-8c59-23b9b391c700 + dockerRepository: airbyte/source-pivotal-tracker + dockerImageTag: 0.1.0 + documentationUrl: https://docs.airbyte.io/integrations/sources/pivotal-tracker + sourceType: api - name: Plaid sourceDefinitionId: ed799e2b-2158-4c66-8da4-b40fe63bc72a dockerRepository: airbyte/source-plaid diff --git a/docs/integrations/sources/pivotal-tracker.md b/docs/integrations/sources/pivotal-tracker.md new file mode 100644 index 0000000000000..3b823e8c4754f --- /dev/null +++ b/docs/integrations/sources/pivotal-tracker.md @@ -0,0 +1,53 @@ +# Pivotal Tracker + +## Overview + +The Pivotal Tracker source supports Full Refresh syncs. It supports pulling from : +* Activity +* Epics +* Labels +* Project Membership +* Projects +* Releases +* Stories + +### Output schema + +Output streams: + +* [Activity](https://www.pivotaltracker.com/help/api/rest/v5#Activity) +* [Epics](https://www.pivotaltracker.com/help/api/rest/v5#Epics) +* [Labels](https://www.pivotaltracker.com/help/api/rest/v5#Labels) +* [Project Membership](https://www.pivotaltracker.com/help/api/rest/v5#Project_Memberships) +* [Projects](https://www.pivotaltracker.com/help/api/rest/v5#Projects) +* [Releases](https://www.pivotaltracker.com/help/api/rest/v5#Releases) +* [Stories](https://www.pivotaltracker.com/help/api/rest/v5#Stories) + +### Features + +| Feature | Supported? | +| :--- | :--- | +| Full Refresh Sync | Yes | +| Incremental - Append Sync | Coming soon | +| Replicate Incremental Deletes | Coming soon | +| SSL connection | Yes | +| Namespaces | No | + +### Performance considerations + +The Pivotal Trakcer connector should not run into Stripe API limitations under normal usage. Please [create an issue](https://github.com/airbytehq/airbyte/issues) if you see any rate limit issues that are not automatically retried successfully. + +## Getting started + +### Requirements + +* Pivotal Trakcer API Token + +### Setup guide to create the API Token + +Access your profile [here](https://www.pivotaltracker.com/profile) go down and click in **Create New Token**. +Use this to pull data from Pivotal Tracker. + +| Version | Date | Pull Request | Subject | +| :--- | :--- | :--- | :--- | +| 0.1.0 | 2022-04-04 | [11060](https://github.com/airbytehq/airbyte/pull/11060) | Initial Release | From 14e1f1e18c4755230ffab5bac2c4c5caaee95399 Mon Sep 17 00:00:00 2001 From: marcosmarxm Date: Mon, 4 Apr 2022 17:36:48 -0700 Subject: [PATCH 19/20] change uuid value --- .../init/src/main/resources/seed/source_definitions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airbyte-config/init/src/main/resources/seed/source_definitions.yaml b/airbyte-config/init/src/main/resources/seed/source_definitions.yaml index a20ac25c4681f..12e55df6ad1b4 100644 --- a/airbyte-config/init/src/main/resources/seed/source_definitions.yaml +++ b/airbyte-config/init/src/main/resources/seed/source_definitions.yaml @@ -590,7 +590,7 @@ icon: pipedrive.svg sourceType: api - name: Pivotal Tracker - sourceDefinitionId: d8286229-c680-4063-8c59-23b9b391c700 + sourceDefinitionId: d60f5393-f99e-4310-8d05-b1876820f40e dockerRepository: airbyte/source-pivotal-tracker dockerImageTag: 0.1.0 documentationUrl: https://docs.airbyte.io/integrations/sources/pivotal-tracker From bfba2dbe8b43c3473733f562900b1118b33f9bf6 Mon Sep 17 00:00:00 2001 From: marcosmarxm Date: Mon, 4 Apr 2022 17:54:54 -0700 Subject: [PATCH 20/20] run seed config --- .../src/main/resources/seed/source_specs.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/airbyte-config/init/src/main/resources/seed/source_specs.yaml b/airbyte-config/init/src/main/resources/seed/source_specs.yaml index 88de4a68c5fbf..5637553766493 100644 --- a/airbyte-config/init/src/main/resources/seed/source_specs.yaml +++ b/airbyte-config/init/src/main/resources/seed/source_specs.yaml @@ -6327,6 +6327,25 @@ - - "client_secret" oauthFlowOutputParameters: - - "refresh_token" +- dockerImage: "airbyte/source-pivotal-tracker:0.1.0" + spec: + documentationUrl: "https://docsurl.com" + connectionSpecification: + $schema: "http://json-schema.org/draft-07/schema#" + title: "Pivotal Tracker Spec" + type: "object" + required: + - "api_token" + additionalProperties: false + properties: + api_token: + type: "string" + description: "Pivotal Tracker API token" + examples: + - "5c054d0de3440452190fdc5d5a04d871" + supportsNormalization: false + supportsDBT: false + supported_destination_sync_modes: [] - dockerImage: "airbyte/source-plaid:0.3.1" spec: documentationUrl: "https://plaid.com/docs/api/"