-
Notifications
You must be signed in to change notification settings - Fork 31
feat: Add client for SystemLink results API #82
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
Merged
priyadarshini-ni
merged 25 commits into
ni:master
from
Madhan-Reddy-ni:users/Peram/niresults/add-results-client
Mar 5, 2025
Merged
Changes from 15 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
36cb9f4
First Pass
Madhan-Reddy-ni dbb9055
Added the examples
Madhan-Reddy-ni c31dfc5
Removed the unnecessary changes
Madhan-Reddy-ni 5c6139f
Fix: PR Comments
Madhan-Reddy-ni a87f71d
Fix: Linting
Madhan-Reddy-ni f8f03b1
Merge branch 'master' into users/Peram/niresults/add-results-client
3124c3b
fix: resolve PR comments
5eb11bb
fix: rename file, add model
bb46743
add retry for 503, 504
a34a907
add workspace update parameter
9ac98fc
fix: lint
a70692a
remove arg for update-results
9004087
remove is_finalized
1a4abca
fix: lint
cc4dc76
fix: add updateResultRequest
cf89b0b
move the results client into testmonitor client
19a57bd
update doc
0338102
add mapping for model in test
e17912f
fix: resolve comments
06ba5e4
fix: add model for create request and partial response
e40ac37
move result example to testmonitor folder
bbf44ba
fix: nit changes, added standard status types
2b07a13
fix: type error
3b31618
fix: move status to a separate file
13894e6
add class to init
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| .. _api_tag_page: | ||
|
|
||
| nisystemlink.clients.result | ||
| ====================== | ||
|
|
||
| .. autoclass:: nisystemlink.clients.result.ResultClient | ||
| :exclude-members: __init__ | ||
|
|
||
| .. automethod:: __init__ | ||
| .. automethod:: create_results | ||
| .. automethod:: get_results | ||
| .. automethod:: query_results | ||
| .. automethod:: query_result_values | ||
| .. automethod:: update_results | ||
| .. automethod:: delete_result | ||
| .. automethod:: delete_results | ||
|
|
||
| .. automodule:: nisystemlink.clients.result.models | ||
| :members: | ||
| :imported-members: | ||
|
|
||
| .. automodule:: nisystemlink.clients.result.utilities | ||
| :members: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| from nisystemlink.clients.core import HttpConfiguration | ||
rbell517 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| from nisystemlink.clients.result import ResultClient | ||
| from nisystemlink.clients.result.models import ( | ||
| QueryResultsRequest, | ||
| QueryResultValuesRequest, | ||
| Result, | ||
| ResultField, | ||
| StatusObject, | ||
| StatusType, | ||
| ) | ||
|
|
||
| program_name = "Example Name" | ||
| host_name = "Example Host" | ||
| status_type = StatusType.PASSED | ||
|
|
||
|
|
||
| def create_some_results(): | ||
| """Create two example results on your server.""" | ||
| new_results = [ | ||
| Result( | ||
| part_number="Example 123 AA", | ||
| program_name=program_name, | ||
| host_name=host_name, | ||
| status=StatusObject(status_type=StatusType.PASSED), | ||
| keywords=["original keyword"], | ||
| properties={"original property key": "yes"}, | ||
| ), | ||
| Result( | ||
| part_number="Example 123 AA1", | ||
| program_name=program_name, | ||
| host_name=host_name, | ||
| status=StatusObject(status_type=StatusType.CUSTOM, status_name="Custom"), | ||
| keywords=["original keyword"], | ||
| properties={"original property key": "original"}, | ||
| ), | ||
| ] | ||
| create_response = client.create_results(new_results) | ||
| return create_response | ||
|
|
||
|
|
||
| # Setup the server configuration to point to your instance of SystemLink Enterprise | ||
| server_configuration = HttpConfiguration( | ||
| server_uri="https://yourserver.yourcompany.com", | ||
| api_key="YourAPIKeyGeneratedFromSystemLink", | ||
| ) | ||
| client = ResultClient(configuration=server_configuration) | ||
|
|
||
| create_response = create_some_results() | ||
|
|
||
| # Get all the results using the continuation token in batches of 100 at a time. | ||
| response = client.get_results_paged(take=100, return_count=True) | ||
| all_results = response.results | ||
| while response.continuation_token: | ||
| response = client.get_results_paged( | ||
| take=100, continuation_token=response.continuation_token, return_count=True | ||
| ) | ||
| all_results.extend(response.results) | ||
|
|
||
| # use get for first result created | ||
| created_result = client.get_result(create_response.results[0].id) | ||
|
|
||
| # Query results without continuation | ||
| query_request = QueryResultsRequest( | ||
| filter=f'status.statusType="{status_type}"', return_count=True | ||
| ) | ||
| response = client.query_results_paged(query_request) | ||
|
|
||
| # Update the first result that you just created and replace the keywords | ||
| updated_result = create_response.results[0] | ||
| updated_result.keywords = ["new keyword"] | ||
| updated_result.properties = {"new property key": "new value"} | ||
| update_response = client.update_results([create_response.results[0]], replace=True) | ||
|
|
||
| # Query for just the ids of results that match the family | ||
| values_query = QueryResultValuesRequest( | ||
| filter=f'programName="{program_name}"', field=ResultField.ID | ||
| ) | ||
| values_response = client.query_result_values(query=values_query) | ||
|
|
||
| # delete each created result individually by id | ||
| for result in create_response.results: | ||
| client.delete_result(result.id) | ||
|
|
||
| # Create some more and delete them with a single call to delete. | ||
| create_response = create_some_results() | ||
| client.delete_results([result.id for result in create_response.results]) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| from ._result_client import ResultClient | ||
|
|
||
| # flake8: noqa |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| """Implementations of the Result Client.""" | ||
|
|
||
| from typing import List, Optional | ||
|
|
||
| from nisystemlink.clients import core | ||
| from nisystemlink.clients.core._uplink._base_client import BaseClient | ||
| from nisystemlink.clients.core._uplink._methods import delete, get, post | ||
| from nisystemlink.clients.result.models import ( | ||
| Result, | ||
| UpdateResultRequest, | ||
| ) | ||
| from uplink import Field, Query, retry, returns | ||
|
|
||
| from . import models | ||
|
|
||
|
|
||
| @retry(when=retry.when.status([429, 503, 504]), stop=retry.stop.after_attempt(5)) | ||
| class ResultClient(BaseClient): | ||
priyadarshini-ni marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| def __init__(self, configuration: Optional[core.HttpConfiguration] = None): | ||
| """Initialize an instance. | ||
|
|
||
| Args: | ||
| configuration: Defines the web server to connect to and information about | ||
| how to connect. If not provided, the | ||
| :class:`HttpConfigurationManager <nisystemlink.clients.core.HttpConfigurationManager>` | ||
| is used to obtain the configuration. | ||
|
|
||
| Raises: | ||
| ApiException: if unable to communicate with the Result Service. | ||
| """ | ||
| if configuration is None: | ||
| configuration = core.HttpConfigurationManager.get_configuration() | ||
| super().__init__(configuration, base_path="/nitestmonitor/v2/") | ||
|
|
||
| @post("results", args=[Field("results")]) | ||
| def create_results(self, results: List[Result]) -> models.ResultsPartialSuccess: | ||
| """Creates one or more results and returns errors for failed creations. | ||
|
|
||
| Args: | ||
| results: A list of results to attempt to create. | ||
|
|
||
| Returns: A list of created results, results that failed to create, and errors for | ||
| failures. | ||
|
|
||
| Raises: | ||
| ApiException: if unable to communicate with the ``/nitestmonitor`` service of provided invalid | ||
| arguments. | ||
| """ | ||
| ... | ||
|
|
||
| @get( | ||
| "results", | ||
| args=[Query("continuationToken"), Query("take"), Query("returnCount")], | ||
| ) | ||
| def get_results_paged( | ||
| self, | ||
| continuation_token: Optional[str] = None, | ||
| take: Optional[int] = None, | ||
| return_count: Optional[bool] = None, | ||
| ) -> models.PagedResults: | ||
| """Reads a list of results. | ||
|
|
||
| Args: | ||
| continuation_token: The token used to paginate results. | ||
| take: The number of results to get in this request. | ||
| return_count: Whether or not to return the total number of results available. | ||
|
|
||
| Returns: | ||
| A list of results. | ||
|
|
||
| Raises: | ||
| ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
| or provided an invalid argument. | ||
| """ | ||
| ... | ||
|
|
||
| @get("results/{id}") | ||
| def get_result(self, id: str) -> models.Result: | ||
| """Retrieves a single result by id. | ||
|
|
||
| Args: | ||
| id (str): Unique ID of a result. | ||
|
|
||
| Returns: | ||
| The single result matching `id` | ||
|
|
||
| Raises: | ||
| ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
| or provided an invalid argument. | ||
| """ | ||
| ... | ||
|
|
||
| @post("query-results") | ||
| def query_results_paged( | ||
rbell517 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| self, query: models.QueryResultsRequest | ||
| ) -> models.PagedResults: | ||
| """Queries for results that match the filter. | ||
|
|
||
| Args: | ||
| query : The query contains a DynamicLINQ query string in addition to other details | ||
| about how to filter and return the list of results. | ||
|
|
||
| Returns: | ||
| A paged list of results with a continuation token to get the next page. | ||
|
|
||
| Raises: | ||
| ApiException: if unable to communicate with the ``/nitestmonitor`` Service or provided invalid | ||
| arguments. | ||
| """ | ||
| ... | ||
|
|
||
| @returns.json # type: ignore | ||
| @post("query-result-values") | ||
| def query_result_values(self, query: models.QueryResultValuesRequest) -> List[str]: | ||
| """Queries for results that match the query and returns a list of the requested field. | ||
|
|
||
| Args: | ||
| query : The query for the fields. | ||
|
|
||
| Returns: | ||
| A list of the values of the queried field. | ||
|
|
||
| Raises: | ||
| ApiException: if unable to communicate with the ``/nitestmonitor`` Service or provided | ||
| invalid arguments. | ||
| """ | ||
| ... | ||
|
|
||
| @post("update-results", args=[Field("results"), Field("replace")]) | ||
| def __update_results( | ||
| self, results: List[UpdateResultRequest], replace: bool = False | ||
| ) -> models.ResultsPartialSuccess: | ||
| """Updates a list of results with optional field replacement. | ||
|
|
||
| Args: | ||
| `results`: A list of results to update. Results are matched for update by id. | ||
| `replace`: Replace the existing fields instead of merging them. Defaults to `False`. | ||
| If this is `True`, then `keywords` and `properties` for the result will be | ||
| replaced by what is in the `results` provided in this request. | ||
| If this is `False`, then the `keywords` and `properties` in this request will | ||
| merge with what is already present in the server resource. | ||
|
|
||
| Returns: A list of updates results, results that failed to update, and errors for | ||
| failures. | ||
|
|
||
| Raises: | ||
| ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
| or provided an invalid argument. | ||
| """ | ||
| ... | ||
|
|
||
| def update_results( | ||
| self, | ||
| results: List[UpdateResultRequest], | ||
| replace: bool = False, | ||
| allow_workspace_update: bool = False, | ||
| ) -> models.ResultsPartialSuccess: | ||
| """Updates a list of results with optional field replacement. | ||
|
|
||
| Args: | ||
| `results`: A list of results to update. Results are matched for update by id. | ||
| `replace`: Replace the existing fields instead of merging them. Defaults to `False`. | ||
| If this is `True`, then `keywords` and `properties` for the result will be | ||
| replaced by what is in the `results` provided in this request. | ||
| If this is `False`, then the `keywords` and `properties` in this request will | ||
| merge with what is already present in the server resource. | ||
| `allow_workspace_update`: If this is set to `False`, the `workspace` field will be set to None | ||
| before updating. | ||
|
|
||
| Returns: A list of updates results, results that failed to update, and errors for | ||
| failures. | ||
|
|
||
| Raises: | ||
| ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
| or provided an invalid argument. | ||
| """ | ||
| if not allow_workspace_update: | ||
rbell517 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| for result in results: | ||
| result.workspace = None | ||
|
|
||
| return self.__update_results(results, replace) | ||
|
|
||
| @delete("results/{id}") | ||
| def delete_result(self, id: str) -> None: | ||
| """Deletes a single result by id. | ||
|
|
||
| Args: | ||
| id (str): Unique ID of a result. | ||
|
|
||
| Raises: | ||
| ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
| or provided an invalid argument. | ||
| """ | ||
| ... | ||
|
|
||
| @post("delete-results", args=[Field("ids")]) | ||
| def delete_results( | ||
| self, ids: List[str] | ||
| ) -> Optional[models.DeleteResultsPartialSuccess]: | ||
| """Deletes multiple results. | ||
|
|
||
| Args: | ||
| ids (List[str]): List of unique IDs of results. | ||
|
|
||
| Returns: | ||
| A partial success if any results failed to delete, or None if all | ||
| results were deleted successfully. | ||
|
|
||
| Raises: | ||
| ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
| or provided an invalid argument. | ||
| """ | ||
| ... | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| from ._result import Result, StatusObject, StatusType | ||
| from ._results_partial_success import ResultsPartialSuccess | ||
| from ._delete_results_partial_success import DeleteResultsPartialSuccess | ||
| from ._paged_results import PagedResults | ||
| from ._update_result_request import UpdateResultRequest | ||
| from ._query_results_request import ( | ||
| QueryResultsRequest, | ||
| ResultField, | ||
| QueryResultValuesRequest, | ||
| ) | ||
|
|
||
| # flake8: noqa |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.