Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐙 octavia-cli: generate open api client #9277

Merged
merged 20 commits into from
Jan 17, 2022

Conversation

alafanechere
Copy link
Contributor

@alafanechere alafanechere commented Jan 4, 2022

What

We need an API client to make octavia-cli talk with Airbyte's API.
Airbyte API's uses Open API specs, making it easy to autogenerate a python client with openapitools/openapi-generator-cli.

How

  1. Create a script to generate the API client using the openapi-generator-cli docker image from airbyte-api/src/main/openapi/config.yaml.
  2. Call this script during the build in octavia-cli/build.gradle.
  3. Make small changes to airbyte-api/src/main/openapi/config.yaml
  4. Run the client generation -> creates a new python module in octavia-cli/airbyte_api_client
  5. Use the client in octavia command group to retrieve the Airbyte workspace's id
  6. Unit test the behavior of step 5.

Now running octavia --airbyte-url https://demo.airbyte.io list outputs:

🐙 - Octavia is targetting your Airbyte instance running at https://demo.airbyte.io on workspace b734c3d7-ece6-47e0-8f07-c4be707fbcfa
Error: The list command is not yet implemented.

Recommended reading order

  1. octavia-cli/bin/generate-api-client.sh + octavia-cli/build.gradle > Run the API client generation on build.
  2. airbyte-api/src/main/openapi/config.yaml declare some workspace-related field as nullable to make the generated client work.
  3. octavia-cli/setup.py: Add airbyte_api_client as an octavia-cli dependency
  4. octavia-cli/octavia_cli/entrypoint.py: Make use of the client in the octavia command group to retrieve the workspace id and store it into the CLI context.
  5. octavia-cli/unit_tests/test_entrypoint.py: Unit test the octavia command group .

🚨 User Impact 🚨

No impact as the

Pre-merge Checklist

Expand the relevant checklist and delete the others.

New Connector

Community member or Airbyter

  • Community member? Grant edit access to maintainers (instructions)
  • Secrets in the connector's spec are annotated with airbyte_secret
  • Unit & integration tests added and passing. Community members, please provide proof of success locally e.g: screenshot or copy-paste unit, integration, and acceptance test output. To run acceptance tests for a Python connector, follow instructions in the README. For java connectors run ./gradlew :airbyte-integrations:connectors:<name>:integrationTest.
  • Code reviews completed
  • Documentation updated
    • Connector's README.md
    • Connector's bootstrap.md. See description and examples
    • docs/SUMMARY.md
    • docs/integrations/<source or destination>/<name>.md including changelog. See changelog example
    • docs/integrations/README.md
    • airbyte-integrations/builds.md
  • PR name follows PR naming conventions

Airbyter

If this is a community PR, the Airbyte engineer reviewing this PR is responsible for the below items.

  • Create a non-forked branch based on this PR and test the below items on it
  • Build is successful
  • Credentials added to Github CI. Instructions.
  • /test connector=connectors/<name> command is passing.
  • New Connector version released on Dockerhub by running the /publish command described here
  • After the connector is published, connector added to connector index as described here
  • Seed specs have been re-generated by building the platform and committing the changes to the seed spec files, as described here

Updating a connector

Community member or Airbyter

  • Grant edit access to maintainers (instructions)
  • Secrets in the connector's spec are annotated with airbyte_secret
  • Unit & integration tests added and passing. Community members, please provide proof of success locally e.g: screenshot or copy-paste unit, integration, and acceptance test output. To run acceptance tests for a Python connector, follow instructions in the README. For java connectors run ./gradlew :airbyte-integrations:connectors:<name>:integrationTest.
  • Code reviews completed
  • Documentation updated
    • Connector's README.md
    • Connector's bootstrap.md. See description and examples
    • Changelog updated in docs/integrations/<source or destination>/<name>.md including changelog. See changelog example
  • PR name follows PR naming conventions

Airbyter

If this is a community PR, the Airbyte engineer reviewing this PR is responsible for the below items.

  • Create a non-forked branch based on this PR and test the below items on it
  • Build is successful
  • Credentials added to Github CI. Instructions.
  • /test connector=connectors/<name> command is passing.
  • New Connector version released on Dockerhub by running the /publish command described here
  • After the new connector version is published, connector version bumped in the seed directory as described here
  • Seed specs have been re-generated by building the platform and committing the changes to the seed spec files, as described here

Connector Generator

  • Issue acceptance criteria met
  • PR name follows PR naming conventions
  • If adding a new generator, add it to the list of scaffold modules being tested
  • The generator test modules (all connectors with -scaffold in their name) have been updated with the latest scaffold by running ./gradlew :airbyte-integrations:connector-templates:generator:testScaffoldTemplates then checking in your changes
  • Documentation which references the generator is updated as needed.


This change is Reviewable

@alafanechere alafanechere temporarily deployed to more-secrets January 4, 2022 11:09 Inactive
@github-actions github-actions bot added area/api Related to the api area/platform issues related to the platform labels Jan 4, 2022
@alafanechere alafanechere removed the area/api Related to the api label Jan 4, 2022
Base automatically changed from augustin/octavia-cli/bootstrap-repo to master January 5, 2022 20:19
@alafanechere alafanechere changed the base branch from master to 0.29.12-alpha January 6, 2022 08:14
@alafanechere alafanechere changed the base branch from 0.29.12-alpha to master January 6, 2022 08:14
@alafanechere alafanechere force-pushed the augustin/octavia-cli/open-api-client branch from d0c8f67 to 2afe6c2 Compare January 6, 2022 08:19
@github-actions github-actions bot added the area/api Related to the api label Jan 6, 2022
@alafanechere alafanechere temporarily deployed to more-secrets January 6, 2022 08:21 Inactive
@alafanechere alafanechere temporarily deployed to more-secrets January 6, 2022 08:26 Inactive
@alafanechere alafanechere temporarily deployed to more-secrets January 6, 2022 09:12 Inactive
@alafanechere alafanechere force-pushed the augustin/octavia-cli/open-api-client branch from 9cb23df to 0d5ad55 Compare January 6, 2022 09:15
@alafanechere alafanechere temporarily deployed to more-secrets January 6, 2022 09:17 Inactive
@alafanechere alafanechere temporarily deployed to more-secrets January 6, 2022 09:20 Inactive
@alafanechere alafanechere temporarily deployed to more-secrets January 6, 2022 09:25 Inactive
@alafanechere alafanechere temporarily deployed to more-secrets January 6, 2022 09:28 Inactive
@alafanechere alafanechere marked this pull request as ready for review January 6, 2022 09:33
@alafanechere alafanechere added area/octavia-cli and removed area/platform issues related to the platform labels Jan 6, 2022
Copy link
Contributor

@cgardens cgardens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only blocking because i don't understand the chnages in airbyte-api/src/main/openapi/config.yaml. otherwise it looks good! nice use of the docker open api generator container!

@@ -1944,8 +1944,10 @@ components:
$ref: "#/components/schemas/Notification"
firstCompletedSync:
type: boolean
nullable: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are you trying to achieve here? because these fields are not in the required list above they should already be nullable and should not require the extra nullable field.

Copy link
Contributor Author

@alafanechere alafanechere Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I called the workspace endpoint with the generated API client it raises an error during serialization of the response because it expected this field to be not nullable. When I make this change in config.yaml the problem is bypassed. I'm going to check if I can find another solution by tweaking some generator options, otherwise, I'll have to make these kinds of changes in config.yaml for other endpoints that will be used in the future, which is not ideal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. that seems like kinda big deal. swagger generation is always a little jenky around the edges, but nullable fields is a pretty core feature, so it seems like a really bad sign if the generator can't handle it because it means there will be other problems.

one thing to test is instead of using the nullable field if instead setting type: [boolean, null] works. I don't like this as much as the pattern we already use but if we needed to switch to this pattern it may not be the end of the world.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found this issue OpenAPITools/openapi-generator#4816 on OpenApiTools repo, this is for their C# generator but it looks like the exact same problem I have with the Python one. I'll continue to investigate and try type: [boolean, null]. Let me know if you think that I should rather implement the client myself.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we definitely don't want to implement it ourselves. my guess is the C# bug is language specific (since C# is actually a typed language)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried [boolean, null] but this is not a valid spec according to open-api-generator.
I think I found a workaround. I can disable the type validation of the response. It also disables deserialization to a model and leads us to manipulate the raw response as a python dict. It's not the most elegant solution but I prefer this rather than editing the API spec. Related commit: a586719
I also reverted my changes on airbyte-api's config.yaml.

.pre-commit-config.yaml Show resolved Hide resolved
octavia-cli/README.md Show resolved Hide resolved
@@ -7,3 +7,12 @@ airbytePython {
moduleDirectory 'octavia_cli'
}

task generateApiClient(type: Exec) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the caching work properly on this task? the expectation should be that if airbyte-api/src/main/openapi/config.yaml does not change that this task does not need to run again. if that's not set up properly it will require re-executing this task and every tasks that depends on them (which is most of them).

see UP-TO-DATE in https://docs.gradle.org/current/userguide/more_about_tasks.html

Copy link
Contributor Author

@alafanechere alafanechere Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this suggestion! I realized I could use a grade plugin already used for java API client generation: org.openapitools.generator.gradle.plugin.tasks.GenerateTask. It supports incremental builds, but I can't figure why it's not working in my context.
I did the following in b285449:

  • update octavia-cli/build.gradle to use the GenerateTask.
  • exclude the generated client from the license headers checks.
  • remove the script I wrote to generate the client with the docker image.

Still, I don't get why I can't see the UP-TO-DATE flag after consequent builds.

Copy link
Contributor Author

@alafanechere alafanechere Jan 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I finally made the FROM-CACHE and UP-TO-DATE mechanisms work. The solution was to use the buildDir as output dir. Using other paths made Gradle re-run the task on each run. The generated client is now stored in octavia-cli/build/airbyte_api_client. Related commit: e95abc9

I confirm that changes in airbyte-api/src/main/openapi/config.yaml will trigger a regeneration of the client on SUB_BUILD=OCTAVIA_CLI ./gradlew build.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like you to present this very briefly in the dev meeting. This is something we often get wrong when using gradle, so it would be good to have a 5 minute presentation to share the knowledge with the team.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok! To be honest, I'm not understanding why using input paths for tasks that are not buildDir does not lead to UP-TO-DATE but it could indeed be interesting to share this finding and to open this discussion in the dev meeting.

def test_octavia():
@click.command()
@click.pass_context
def dumb(ctx):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😂

@github-actions github-actions bot added the area/platform issues related to the platform label Jan 11, 2022
@alafanechere alafanechere temporarily deployed to more-secrets January 11, 2022 10:29 Inactive
@alafanechere alafanechere temporarily deployed to more-secrets January 12, 2022 10:10 Inactive
@alafanechere alafanechere temporarily deployed to more-secrets January 12, 2022 10:18 Inactive
@github-actions github-actions bot removed area/platform issues related to the platform area/api Related to the api labels Jan 13, 2022
@alafanechere alafanechere temporarily deployed to more-secrets January 13, 2022 08:51 Inactive
Copy link
Contributor

@cgardens cgardens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good! don't love that we can't used the typed objects in python, but i agree with you, i can't think of another workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants