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

Snow 1369491 snow object create command #1058

Merged
merged 30 commits into from
Jun 19, 2024

Conversation

sfc-gh-pczajka
Copy link
Collaborator

@sfc-gh-pczajka sfc-gh-pczajka commented May 10, 2024

Pre-review checklist

  • I've confirmed that instructions included in README.md are still correct after my changes in the codebase.
  • I've added or updated automated unit tests to verify correctness of my new code.
  • I've added or updated integration tests to verify correctness of my new code.
  • I've confirmed that my changes are working by executing CLI's commands manually.
  • I've confirmed that my changes are up-to-date with the target branch.
  • I've described my changes in the release notes.
  • I've described my changes in the section below.

Changes description

Add hidden "create" command, which is a thin layer over Snowflake REST API CREATE commands. Supported objects:
https://docs.snowflake.com/LIMITEDACCESS/rest-api/reference/index.html

@sfc-gh-pczajka sfc-gh-pczajka requested a review from a team as a code owner May 10, 2024 14:01
@sfc-gh-pczajka sfc-gh-pczajka marked this pull request as draft May 10, 2024 14:02
show_default=False,
)
ObjectDefinitionArgument = typer.Argument(
help="""Object definition in JSON format, for example \'{"name": "my_database", "owner": "owner_role"}\',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this a valid example?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've checked - "owner" is not settable field, it is ignored by request (that's why my previous check passed). I've changed it to "comment"

@sfc-gh-pczajka sfc-gh-pczajka marked this pull request as ready for review May 22, 2024 14:21
@sfc-gh-pczajka sfc-gh-pczajka requested a review from a team as a code owner May 22, 2024 14:21
@sfc-gh-pczajka sfc-gh-pczajka marked this pull request as draft May 22, 2024 14:26
@sfc-gh-pczajka sfc-gh-pczajka marked this pull request as ready for review May 22, 2024 16:25
@@ -118,3 +121,55 @@ def test_show_drop_image_repository(runner, test_database, snowflake_session):
)
assert result_drop.exit_code == 0, result_drop.output
assert f"{repo_name} successfully dropped" in result_drop.output


@pytest.mark.parametrize(
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

table, task, warehouse work when called manually, integration test role probably don't have enough permissions

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should grant create for them and test it, but this will requires extending of our cleanup function. Can be done in separate task.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

\'{"name": "my_db", "comment": "created with Snowflake CLI"}\', or provided as a list of key=value pairs,
for example: name=my_db 'comment=created with Snowflake CLI'.

Check https://docs.snowflake.com/LIMITEDACCESS/rest-api/reference/ for the full list of available parameters
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do not link to private docs, we should not merge the changes as long as APIs are not in PuPr.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I left TODO to be filled, and changed base branch from main to object-create, which will collect PRs and merge them together into main when APIs will gave gone PuPr.

Copy link
Collaborator

@sfc-gh-turbaszek sfc-gh-turbaszek left a comment

Choose a reason for hiding this comment

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

Private feature atm.

@sfc-gh-pczajka sfc-gh-pczajka changed the base branch from main to object-create May 23, 2024 13:44
ObjectAttributesArgument = typer.Argument(
None,
help="""Object attributes provided as a list of key=value pairs,
for example name=my_db 'comment=created with Snowflake CLI'.
Copy link
Collaborator

Choose a reason for hiding this comment

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

why one has quotes and other does not?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If the value contains spaces, it should be quoted. I just checked, comment='created with Snowflake CLI' works as well, changed.

"""Create an object of a given type. List of supported objects
and parameters: https://docs.snowflake.com/LIMITEDACCESS/rest-api/reference/"""
if object_attributes and object_json:
raise ClickException(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Loose though - I think we have a few cases like this, maybe we should have a ticket for introducing a common error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Comment on lines 154 to 156
object_data = {
v.key: v.value for v in parse_key_value_variables(object_attributes)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any support for nested attirbutes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nice catch, fixed

no_retry=True,
)

def _url_exists(self, url):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add type hints

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 113 to 116
if object_type.endswith("y"):
plural_object_type = object_type[:-1].lower() + "ies"
else:
plural_object_type = object_type.lower() + "s"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's separate this to separate util method

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

else:
plural_object_type = object_type.lower() + "s"

url_prefix = "/api/v2"
Copy link
Collaborator

@sfc-gh-turbaszek sfc-gh-turbaszek Jun 13, 2024

Choose a reason for hiding this comment

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

Make this a global constant , check for other usage in code base

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed, this is the first use


url = f"{url_prefix}/{plural_object_type}/"
if self._url_exists(url):
return url
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add comments explaining why this pre-check

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added a docstring and refactored so it is more clear


schema = self._conn.schema
url = f"{url_prefix}/databases/{db}/schemas/{schema}/{plural_object_type}/"
if self._url_exists(url):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider implementing an RestAPI class that would encapsulate the communication logic, we don't have to overload the object manger.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

if not response:
raise ClickException(
dedent(
""" An unexpected error occurred while creating the object. Try again with --debug for more info.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Won't this be duplicated in the output due to global handling?

Copy link
Collaborator Author

@sfc-gh-pczajka sfc-gh-pczajka Jun 13, 2024

Choose a reason for hiding this comment

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

No, python connector surpresses the exception. I've added a "workaround", but I think it's worth to discuss with connector team to change that behaviour. IMO its worth to show info what exactly went wrong to the user

)
return response["status"]
except BadRequest:
raise ClickException("Incorrect object definition.")
Copy link
Collaborator

@sfc-gh-turbaszek sfc-gh-turbaszek Jun 13, 2024

Choose a reason for hiding this comment

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

Is there any information from the error that we can expose to the users? What exactly was wrong?

Imho we should show the original error but in more human-friendly way, for example not using json 😄

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, the only information we get is "409 bad request", which, according to documentation, means malformatted payload (for example misspelled argument)

("image-repository", {"name": "test_image_repo"}),
],
)
@pytest.mark.integration
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add integration tests for expected errors so we have a check that nothing changes on server side

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added!

@sfc-gh-turbaszek sfc-gh-turbaszek merged commit 79d8003 into object-create Jun 19, 2024
24 checks passed
@sfc-gh-turbaszek sfc-gh-turbaszek deleted the SNOW-1369491-create-database branch June 19, 2024 12:57
sfc-gh-pczajka added a commit that referenced this pull request Jun 28, 2024
* update release-notes

* Snow 1369491 snow object create command (#1058)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants