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

Enhancement: clients can use REST url for all methods #219

Open
wants to merge 23 commits into
base: master
Choose a base branch
from

Conversation

davidalexandru7013
Copy link

@davidalexandru7013 davidalexandru7013 commented Aug 13, 2024

Description of changes

In this modification, I created the opportunity for users to decide between using v1 api or rest api route in the SnykClient. Previously, only the get method let user to use the rest api. Patch method was created for use cases in which the verb PATCH is necessary, thing that didn't exist previously.

Technical details

  • added patch method in the SnykClient class.
  • updated old tests and added new test cases for the new modifications.
  • redirect delete project from v1 to rest in the client.
  • updated rest api version, because some endpoints were not available in the old version.

Context

These changes were made to enable users use the rest api, because v1 is approaching its end of life. It provides more flexibility to the developers who wants to interact with different instances of Snyk APIs.

How to test

This modification should not impact the existing functionality of the library. Everything should work as intended.

Example Usage

Update an existing project

client = SnykClient(token=api_key)
client.patch(
    f"orgs/org_id/projects/project_id",
    body={
        "data": {
            "attributes": {
                "business_criticality": ["critical", "medium", "low"],
                "tags": [
                    {"key": "keytest1", "value": "valuetest1"},
                    {"key": "keytest2", "value": "valuetest2"},
                ],
                "environment": ["backend", "frontend"],
                "lifecycle": ["development"],
            },
            "id": "project_id",
            "relationships": {},
            "type": "project",
        }
    },
)

snyk/client.py Outdated
fkwargs={"json": body, "headers": {**self.api_post_headers, **headers}},
fkwargs={
"json": body,
"headers": {**self.api_post_headers, **headers},

Choose a reason for hiding this comment

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

feels a bit confusing to user the api_post_headers in the put. Maybe use the api_headers or create a similar api_put_headers?

Copy link
Author

Choose a reason for hiding this comment

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

This line was not changed from the initial version, it is marked as modified, because I've added params and the linter splits them on multiple lines.

body: Dict[str, Any],
headers: Dict[str, str] = {},
params: Dict[str, Any] = {},
) -> requests.Response:

Choose a reason for hiding this comment

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

I'm not sure if we should keep consistency here with a rest parameter or not. I know it would be useless, and need to be set to true. It's just triggering my OCD having this lack of uniformity of methods 🙈

Copy link
Author

Choose a reason for hiding this comment

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

According to the documentation(https://snyk.docs.apiary.io/#reference) there is no endpoint which makes use of patch verb in V1.

@@ -329,3 +327,140 @@ def test_rest_limit_deduplication(self, requests_mock, rest_client):
)
params = {"limit": 10}
rest_client.get(f"orgs/{REST_ORG}/projects?limit=100", params)

def test_patch_update_project_should_return_new_project(

Choose a reason for hiding this comment

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

maybe a typo? patch should not return a "new" project

assert response.status_code == 200
assert response_data == project

def test_token_added_to_patch_headers(self, client):
Copy link

@iulia-ionce iulia-ionce Aug 13, 2024

Choose a reason for hiding this comment

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

sorry, don't know enough python 😅 how does this test work? What does it test?

Copy link
Author

Choose a reason for hiding this comment

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

It makes sure that there is a value in Authorization header and nobody deletes the line which add the value, by mistake.

Choose a reason for hiding this comment

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

client is a fixture. See the function client() in the top of the class.
When you add an argument to the test it runs the function and uses the returned value as parameter.

Copy link

@iulia-ionce iulia-ionce left a comment

Choose a reason for hiding this comment

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

Nice job! Just left a few comments (some just asking for clarification 😄 )

snyk/client.py Outdated
@@ -40,13 +42,18 @@ def __init__(
"Authorization": "token %s" % self.api_token,
"User-Agent": user_agent,
}
self.api_post_headers = self.api_headers
self.api_post_headers = copy.deepcopy(self.api_headers)

Choose a reason for hiding this comment

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

Suggested change
self.api_post_headers = copy.deepcopy(self.api_headers)
self.api_post_headers = dict(self.api_headers)

Why do we need to copy them?

Copy link
Author

Choose a reason for hiding this comment

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

Since both api post headers and patch post headers have different content-type, I wanted a way to avoid side-effects(since simple assigning creates a shallow copy and dictionaries are mutable in python) when changing content-type. The solution given by you might be better rather than creating a deep-copy.

Choose a reason for hiding this comment

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

So my suggestion is to set those headers explicitly in the specific function.
Otherwise it's confusing.

Copy link
Author

@davidalexandru7013 davidalexandru7013 Aug 26, 2024

Choose a reason for hiding this comment

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

This might introduce duplicate code, since both V1 and REST API are using the same headers.

Choose a reason for hiding this comment

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

Again, it's a suggestion only. In my opinion it is better to duplicate code and make them explicit rather than start looking at who modified what header. Moreover, if you do that you will be able to get rid of the api_post_headers property.

snyk/client.py Outdated
self.api_post_headers["Content-Type"] = "application/json"
self.api_patch_headers = copy.deepcopy(self.api_headers)

Choose a reason for hiding this comment

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

Suggested change
self.api_patch_headers = copy.deepcopy(self.api_headers)
self.api_patch_headers = dict(self.api_headers)

Same here. Why do we need to duplicate them?

Copy link
Author

Choose a reason for hiding this comment

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

The same reason as stated above.

snyk/client.py Outdated
def post(
self,
path: str,
body: Any,

Choose a reason for hiding this comment

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

If this is a dict, so can use Dict. If it's either a dict or a str you can do something like Union[Dict, str].

snyk/client.py Outdated
self,
path: str,
body: Any,
headers: dict = {},

Choose a reason for hiding this comment

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

What version of Python do you use? The type dict is allowed only from 3.9. I saw in the pyproject.toml file that the allowed versions of Python are from version 3.7 and up. If this is still the case I suggest using Dict instead.

Copy link
Author

Choose a reason for hiding this comment

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

This was previously in the project, I can change it, but I need to make sure every instance gets changed.

Choose a reason for hiding this comment

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

I'm not suggesting to upgrade. But if we still want to support Python 3.7 we need to change line 96 to be something like:

headers: Dict = {}. # note the capital D.

snyk/client.py Outdated
logger.debug(f"POST: {url}")

if use_rest and "version" not in params:
params["version"] = self.version if self.version else "2024-06-21"

Choose a reason for hiding this comment

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

Suggested change
params["version"] = self.version if self.version else "2024-06-21"
params["version"] = self.version or "2024-06-21"

Choose a reason for hiding this comment

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

Also, any chance we define the default version in a constant?

snyk/client.py Outdated
logger.debug(f"PATCH: {url}")

if "version" not in params:
params["version"] = self.version if self.version else "2024-06-21"

Choose a reason for hiding this comment

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

Suggested change
params["version"] = self.version if self.version else "2024-06-21"
params["version"] = self.version or "2024-06-21"

Any chance to define the default version in a constant?

Copy link

@pablo-snyk pablo-snyk left a comment

Choose a reason for hiding this comment

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

The last commit to package retry's repo was in 2016.
I suggest replace it.

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