diff --git a/docs/api_reference.rst b/docs/api_reference.rst index b9181186..74064439 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -13,6 +13,7 @@ API Reference api_reference/dataframe api_reference/spec api_reference/file + api_reference/feeds Indices and tables ------------------ diff --git a/docs/api_reference/feeds.rst b/docs/api_reference/feeds.rst new file mode 100644 index 00000000..9bc2e27a --- /dev/null +++ b/docs/api_reference/feeds.rst @@ -0,0 +1,18 @@ +.. _api_tag_page: + +nisystemlink.clients.feeds +====================== + +.. autoclass:: nisystemlink.clients.feeds.FeedsClient + :exclude-members: __init__ + + .. automethod:: __init__ + .. automethod:: create_feed + .. automethod:: query_feeds + .. automethod:: upload_package + .. automethod:: upload_package_content + .. automethod:: delete_feed + +.. automodule:: nisystemlink.clients.feeds.models + :members: + :imported-members: \ No newline at end of file diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 0efe9358..41a13fe0 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -158,7 +158,6 @@ Update and Delete Specifications :language: python :linenos: - File API ------- @@ -183,4 +182,42 @@ Get the metadata of a File using its Id and download it. .. literalinclude:: ../examples/file/download_file.py :language: python - :linenos: \ No newline at end of file + :linenos: + +Feeds API +------- + +Overview +~~~~~~~~ + +The :class:`.FeedsClient` class is the primary entry point of the Feeds API. + +When constructing a :class:`.FeedsClient`, you can pass an +:class:`.HttpConfiguration` (like one retrieved from the +:class:`.HttpConfigurationManager`), or let :class:`.FeedsClient` use the +default connection. The default connection depends on your environment. + +With a :class:`.FeedsClient` object, you can: + +* Get the list of feeds, create feed, upload package to feed and delete feed. + +Examples +~~~~~~~~ + +Create a new feed. + +.. literalinclude:: ../examples/feeds/create_feed.py + :language: python + :linenos: + +Query feeds and upload a package to feed. + +.. literalinclude:: ../examples/feeds/query_and_upload_feeds.py + :language: python + :linenos: + +Delete a feed. + +.. literalinclude:: ../examples/feeds/delete_feed.py + :language: python + :linenos: diff --git a/examples/feeds/create_feed.py b/examples/feeds/create_feed.py new file mode 100644 index 00000000..5eaaf78b --- /dev/null +++ b/examples/feeds/create_feed.py @@ -0,0 +1,40 @@ +"""Functionality of creating feeds APIs.""" + +from nisystemlink.clients.core import ApiException, HttpConfiguration +from nisystemlink.clients.feeds._feeds_client import FeedsClient +from nisystemlink.clients.feeds.models import ( + CreateFeedRequest, + Platform, +) + +# Update the constants. +FEED_NAME = "" +FEED_DESCRIPTION = "" +PLATFORM = Platform.WINDOWS +WORKSPACE_ID = ( + None # None uses Default workspace. Replace with Systemlink workspace id. +) + +server_url = "" # SystemLink API URL +server_api_key = "" # SystemLink API key + +# Provide the valid API key and API URL for client initialization. +client = FeedsClient(HttpConfiguration(api_key=server_api_key, server_uri=server_url)) + +# Creating Feeds. +try: + feed_request = CreateFeedRequest( + name=FEED_NAME, + description=FEED_DESCRIPTION, + platform=PLATFORM, + workspace=WORKSPACE_ID, + ) + feed_details = client.create_feed(feed=feed_request) + + print("Feed created Successfully.") + print(f"Created feed details: {feed_details}") + +except ApiException as exp: + print(exp) +except Exception as exp: + print(exp) diff --git a/examples/feeds/delete_feed.py b/examples/feeds/delete_feed.py new file mode 100644 index 00000000..030c416a --- /dev/null +++ b/examples/feeds/delete_feed.py @@ -0,0 +1,36 @@ +"""Functionality of deleting feed API.""" + +from nisystemlink.clients.core import ApiException, HttpConfiguration +from nisystemlink.clients.feeds._feeds_client import FeedsClient +from nisystemlink.clients.feeds.models import Platform +from nisystemlink.clients.feeds.utilities._get_feed_details import get_feed_by_name + +# Update the constants. +FEED_NAME = "" +PLATFORM = Platform.WINDOWS +WORKSPACE_ID = ( + None # None uses Default workspace. Replace with Systemlink workspace id. +) + +server_url = "" # SystemLink API URL +server_api_key = "" # SystemLink API key + +# Provide the valid API key and API URL for client initialization. +client = FeedsClient(HttpConfiguration(api_key=server_api_key, server_uri=server_url)) + +# Deleting Feed. +try: + # Get ID of the Feed to delete by name + feeds = client.query_feeds(platform=PLATFORM, workspace=WORKSPACE_ID) + feed = get_feed_by_name(feeds=feeds, name=FEED_NAME) + feed_id = feed.id if feed else None + + # Delete the Feed by ID + if feed_id: + client.delete_feed(id=feed_id) + print("Feed deleted successfully.") + +except ApiException as exp: + print(exp) +except Exception as exp: + print(exp) diff --git a/examples/feeds/query_and_upload_feeds.py b/examples/feeds/query_and_upload_feeds.py new file mode 100644 index 00000000..d69fa22a --- /dev/null +++ b/examples/feeds/query_and_upload_feeds.py @@ -0,0 +1,44 @@ +"""Functionality of uploading & querying feeds APIs.""" + +from nisystemlink.clients.core import ApiException, HttpConfiguration +from nisystemlink.clients.feeds._feeds_client import FeedsClient +from nisystemlink.clients.feeds.models import Platform +from nisystemlink.clients.feeds.utilities._get_feed_details import get_feed_by_name + +# Update the constants. +FEED_NAME = "" +PLATFORM = None +FEED_DESCRIPTION = "" +PLATFORM = Platform.WINDOWS +WORKSPACE_ID = ( + None # None uses Default workspace. Replace with Systemlink workspace id. +) +PACKAGE_PATH = "" + +server_url = "" # SystemLink API URL +server_api_key = "" # SystemLink API key + +# Provide the valid API key and API URL for client initialization. +client = FeedsClient(HttpConfiguration(api_key=server_api_key, server_uri=server_url)) + +# To upload a package to feed. +try: + # Get ID of the Feed to upload by name + feeds = client.query_feeds(platform=PLATFORM, workspace=WORKSPACE_ID) + feed = get_feed_by_name(feeds=feeds, name=FEED_NAME) + feed_id = feed.id if feed else None + + # Upload the package to Feed by ID + if feed_id: + client.upload_package( + feed_id=feed_id, + overwrite=True, + package_file_path=PACKAGE_PATH, + ) + print("Package uploaded sucessfully.") + +except ApiException as exp: + print(exp) + +except Exception as exp: + print(exp) diff --git a/nisystemlink/clients/feeds/__init__.py b/nisystemlink/clients/feeds/__init__.py new file mode 100644 index 00000000..c5da55bd --- /dev/null +++ b/nisystemlink/clients/feeds/__init__.py @@ -0,0 +1,3 @@ +from ._feeds_client import FeedsClient + +# flake8: noqa diff --git a/nisystemlink/clients/feeds/_feeds_client.py b/nisystemlink/clients/feeds/_feeds_client.py new file mode 100644 index 00000000..df4d6ac5 --- /dev/null +++ b/nisystemlink/clients/feeds/_feeds_client.py @@ -0,0 +1,190 @@ +"""Implementation of SystemLink Feeds Client.""" + +from typing import BinaryIO, 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 uplink import Part, Path, Query + +from . import models + + +class FeedsClient(BaseClient): + 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 ` + is used to obtain the configuration. + + Raises: + ApiException: if unable to communicate with the Feeds Service. + """ + if configuration is None: + configuration = core.HttpConfigurationManager.get_configuration() + + super().__init__(configuration, base_path="/nifeed/v1/") + + @post("feeds") + def create_feed(self, feed: models.CreateFeedRequest) -> models.Feed: + """Create a new feed with the provided feed details. + + Args: + feeds (models.CreateFeedsRequest): Request to create the feed. + + Returns: + models.Feed: Details of the created feed. + + Raises: + ApiException: if unable to communicate with the Feeds Service. + """ + ... + + @get("feeds", args=[Query, Query]) + def __query_feeds( + self, + platform: Optional[str] = None, + workspace: Optional[str] = None, + ) -> models.QueryFeedsResponse: + """Lists available feeds for the Platform `platform` under the Workspace `workspace`. + + Args: + platform (Optional[str]): Information about system platform. Defaults to None. + workspace (Optional[str]): Workspace id. Defaults to None. + + Returns: + models.QueryFeedsResponse: List of feeds. + + Raises: + ApiException: if unable to communicate with the Feeds Service. + """ + ... + + def query_feeds( + self, + platform: Optional[models.Platform] = None, + workspace: Optional[str] = None, + ) -> List[models.Feed]: + """Lists available feeds for the Platform `platform` under the Workspace `workspace`. + + Args: + platform (Optional[models.Platform]): Information about system platform. \ +Defaults to None. + workspace (Optional[str]): Workspace id. Defaults to None. + + Returns: + List[models.Feed]: List of feeds. + + Raises: + ApiException: if unable to communicate with the Feeds Service. + """ + platform_by_str = platform.value if platform is not None else None + response = self.__query_feeds( + platform=platform_by_str, + workspace=workspace, + ).feeds + + return response + + @post( + "feeds/{feedId}/packages", + args=[Path(name="feedId"), Query(name="ShouldOverwrite")], + ) + def __upload_package( + self, + feed_id: str, + package: Part, + overwrite: Query = False, + ) -> models.Package: + """Upload package to SystemLink feed. + + Args: + feed_id (str): ID of the feed. + package (Part): Package file to be uploaded. + overwrite (Query): Set to True, to overwrite the package if it already exists.\ +Defaults to False. + + Returns: + models.Package: Uploaded package information. + + Raises: + ApiException: if unable to communicate with the Feeds Service. + """ + ... + + def upload_package( + self, + feed_id: str, + package_file_path: str, + overwrite: bool = False, + ) -> models.Package: + """Upload package to SystemLink feed. + + Args: + feed_id (str): ID of the feed. + package_file_path (str): File path of the package to be uploaded. + overwrite (bool): Set to True, to overwrite the package if it already exists.\ +Defaults to False. + + Returns: + models.Package: Uploaded package information. + + Raises: + ApiException: if unable to communicate with the Feeds Service. + """ + response = self.__upload_package( + feed_id=feed_id, + overwrite=overwrite, + package=open(package_file_path, "rb"), + ) + + return response + + def upload_package_content( + self, + feed_id: str, + package: BinaryIO, + overwrite: bool = False, + ) -> models.Package: + """Upload package to SystemLink feed. + + Args: + feed_id (str): ID of the feed. + package (BinaryIO): Package file to be uploaded. + overwrite (bool): Set to True, to overwrite the package if it already exists.\ +Defaults to False. + + Returns: + models.Package: Uploaded package information. + + Raises: + ApiException: if unable to communicate with the Feeds Service. + """ + response = self.__upload_package( + feed_id=feed_id, + overwrite=overwrite, + package=package, + ) + + return response + + @delete( + "feeds/{feedId}", + args=[Path(name="feedId")], + ) + def delete_feed(self, id: str) -> None: + """Delete feed and its packages. + + Args: + id (str): ID of the feed to be deleted. + + Returns: + None. + + Raises: + ApiException: if unable to communicate with the Feeds Service. + """ + ... diff --git a/nisystemlink/clients/feeds/models/__init__.py b/nisystemlink/clients/feeds/models/__init__.py new file mode 100644 index 00000000..84c1df08 --- /dev/null +++ b/nisystemlink/clients/feeds/models/__init__.py @@ -0,0 +1,7 @@ +from ._create_feed import CreateFeedRequest +from ._feed import Feed +from ._package import Package +from ._platform import Platform +from ._query_feeds import QueryFeedsResponse + +# flake8: noqa diff --git a/nisystemlink/clients/feeds/models/_create_feed.py b/nisystemlink/clients/feeds/models/_create_feed.py new file mode 100644 index 00000000..3551b4c0 --- /dev/null +++ b/nisystemlink/clients/feeds/models/_create_feed.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from typing import Optional + +from nisystemlink.clients.core._uplink._json_model import JsonModel + +from ._platform import Platform + + +class CreateFeedRequest(JsonModel): + """Create Feed Request.""" + + name: str + """The name of the feed.""" + description: Optional[str] = None + """The description of the feed.""" + platform: Platform + """The platform of the feed, the following package extensions are available: .nipkg for + windows feeds, .ipk and .deb for ni-linux-rt feeds.""" + workspace: Optional[str] = None + """The ID of the workspace this feed belongs to. If the workspace is not defined, + the default workspace is used.""" diff --git a/nisystemlink/clients/feeds/models/_feed.py b/nisystemlink/clients/feeds/models/_feed.py new file mode 100644 index 00000000..e894db56 --- /dev/null +++ b/nisystemlink/clients/feeds/models/_feed.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +from datetime import datetime +from typing import List, Optional + +from nisystemlink.clients.core._uplink._json_model import JsonModel + +from ._platform import Platform + + +class Feed(JsonModel): + """Feed model.""" + + id: Optional[str] = None + """The auto-generated ID of the feed.""" + name: Optional[str] = None + """The name of the feed.""" + description: Optional[str] = None + """The description of the feed.""" + platform: Optional[Platform] = None + """The platform of the feed, the following package extensions are available: .nipkg for + windows feeds, .ipk and .deb for ni-linux-rt feeds. + """ + workspace: Optional[str] = None + """The ID of the workspace this feed belongs to.""" + updated_at: Optional[datetime] = None + """The date of the latest feed update""" + created_at: Optional[datetime] = None + """The date when the feed was created at.""" + package_sources: Optional[List[str]] = None + """The package sources list of the feed.""" + deleted: Optional[bool] = None + """Whether the feed deletion was requested.""" diff --git a/nisystemlink/clients/feeds/models/_package.py b/nisystemlink/clients/feeds/models/_package.py new file mode 100644 index 00000000..c119de1a --- /dev/null +++ b/nisystemlink/clients/feeds/models/_package.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +from datetime import datetime +from typing import Dict, List, Optional + +from nisystemlink.clients.core._uplink._json_model import JsonModel + + +class PackageMetadata(JsonModel): + """Package Meta data.""" + + package_name: Optional[str] = None + """The name of the package.""" + version: Optional[str] = None + """The version number of the package.""" + architecture: Optional[str] = None + """The architecture of the package.""" + breaks: Optional[List[str]] = None + """Information about other packages this package breaks.""" + conflicts: Optional[List[str]] = None + """Information about other packages this package conflicts with.""" + depends: Optional[List[str]] = None + """Information about other packages this package depends on.""" + description: Optional[str] = None + """The description of the package.""" + enhances: Optional[List[str]] = None + """Information about other packages this package enchances.""" + essential: Optional[bool] = None + """True if the package is essential.""" + file_name: Optional[str] = None + """The file name of the package. Depending on the selected platform, + the following package extensions are available: + .nipkg for windows feeds, .ipk and .deb for ni-linux-rt feeds.""" + homepage: Optional[str] = None + """The website of the maintainers for the package.""" + installed_size: Optional[int] = None + """The size of the package after install.""" + maintainer: Optional[str] = None + """The maintainer of the package (name and email address).""" + predepends: Optional[List[str]] = None + """Information about other packages this package predepends.""" + priority: Optional[int] = None + """The priority of the package.""" + provides: Optional[List[str]] = None + """Information about other packages that this package provides.""" + recommends: Optional[List[str]] = None + """Information about other packages this package recommends.""" + release_notes: Optional[str] = None + """The release notes of the package.""" + replaces: Optional[List[str]] = None + """Information about other packages this package replaces.""" + section: Optional[str] = None + """The application area of the package.""" + size: Optional[int] = None + """The size (in bytes) of the package.""" + source: Optional[str] = None + """The source of the package.""" + suggests: Optional[List[str]] = None + """Information about other packages this package suggests.""" + tags: Optional[str] = None + """The tags of the package.""" + attributes: Optional[Dict[str, str]] = None + """The attributes of the package.""" + + +class Package(JsonModel): + """Package model.""" + + id: Optional[str] = None + """Gets or sets the ID of this package. This is used to reference this package in the service.""" + file_name: Optional[str] = None + """The name of the file in this package.""" + feed_id: Optional[str] = None + """The ID of the feed this package is associated with.""" + workspace: Optional[str] = None + """The ID of the workspace this package belongs to. + The workspace of a package is the workspace of feed this package is associated with.""" + updated_at: Optional[datetime] = None + """The date of the latest package update.""" + created_at: Optional[datetime] = None + """The date when the package was created at.""" + metadata: Optional[PackageMetadata] = None + """Package meta data.""" diff --git a/nisystemlink/clients/feeds/models/_platform.py b/nisystemlink/clients/feeds/models/_platform.py new file mode 100644 index 00000000..d1a68a82 --- /dev/null +++ b/nisystemlink/clients/feeds/models/_platform.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from enum import Enum + + +class Platform(Enum): + """Platform.""" + + WINDOWS = "WINDOWS" + NI_LINUX_RT = "NI_LINUX_RT" diff --git a/nisystemlink/clients/feeds/models/_query_feeds.py b/nisystemlink/clients/feeds/models/_query_feeds.py new file mode 100644 index 00000000..1b5c51a7 --- /dev/null +++ b/nisystemlink/clients/feeds/models/_query_feeds.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from typing import List + +from nisystemlink.clients.core._uplink._json_model import JsonModel + +from ._feed import Feed + + +class QueryFeedsResponse(JsonModel): + """Query Feeds response.""" + + feeds: List[Feed] + """A collection of feeds""" diff --git a/nisystemlink/clients/feeds/utilities/__init__.py b/nisystemlink/clients/feeds/utilities/__init__.py new file mode 100644 index 00000000..0d07a5c4 --- /dev/null +++ b/nisystemlink/clients/feeds/utilities/__init__.py @@ -0,0 +1,3 @@ +from ._get_feed_details import get_feed_by_name + +# flake8: noqa diff --git a/nisystemlink/clients/feeds/utilities/_get_feed_details.py b/nisystemlink/clients/feeds/utilities/_get_feed_details.py new file mode 100644 index 00000000..c25dcddd --- /dev/null +++ b/nisystemlink/clients/feeds/utilities/_get_feed_details.py @@ -0,0 +1,24 @@ +"""Utilities for getting feed details.""" + +from typing import List, Optional + +from nisystemlink.clients.feeds.models import Feed + + +def get_feed_by_name( + feeds: List[Feed], + name: str, +) -> Optional[Feed]: + """Get feed information from the list of feeds using `name`. + + Args: + feeds (List[Feed]): List of feeds. + name (str): Feed name. + + Returns: + Optional[Feed]: Feed information. + """ + for feed in feeds: + if feed.name == name and feed.id: + return feed + return None diff --git a/tests/integration/feeds/test_feeds_client.py b/tests/integration/feeds/test_feeds_client.py new file mode 100644 index 00000000..4639a083 --- /dev/null +++ b/tests/integration/feeds/test_feeds_client.py @@ -0,0 +1,274 @@ +"""Integration tests for FeedsClient.""" + +from pathlib import Path +from random import randint +from typing import BinaryIO, Callable + +import pytest +from nisystemlink.clients.core import ApiException +from nisystemlink.clients.feeds import FeedsClient +from nisystemlink.clients.feeds.models import CreateFeedRequest, Platform + + +FEED_DESCRIPTION = "Sample feed for uploading packages" +PACKAGE_PATH = str( + Path(__file__).parent.resolve() + / "test_files" + / "sample-measurement_0.5.0_windows_x64.nipkg" +) + + +@pytest.fixture(scope="class") +def client(enterprise_config) -> FeedsClient: + """Fixture to create a FeedsClient instance.""" + return FeedsClient(enterprise_config) + + +@pytest.fixture(scope="class") +def create_feed(client: FeedsClient): + """Fixture to return a object that creates feed.""" + feed_ids = [] + + def _create_feed(feed): + response = client.create_feed(feed) + feed_ids.append(response.id) + return response + + yield _create_feed + + # deleting the created feeds. + for feed_id in feed_ids: + client.delete_feed(id=feed_id) + + +@pytest.fixture(scope="class") +def create_feed_request(): + """Fixture to create a request body of create feed API.""" + + def _create_feed_request(feed_name: str, description: str, platform: Platform): + feed_request = CreateFeedRequest( + name=feed_name, + platform=platform, + description=description, + ) + return feed_request + + yield _create_feed_request + + +@pytest.fixture(scope="class") +def binary_pkg_file_data() -> BinaryIO: + """Fixture to return package file in binary format.""" + package_data = open(PACKAGE_PATH, "rb") + return package_data + + +@pytest.fixture(scope="class") +def invalid_id() -> str: + """Generate a invalid id.""" + id = f"Invalid-id-{randint(1000, 9999)}" + return id + + +@pytest.fixture(scope="class") +def get_feed_name(): + """Generate a feed name.""" + name = "testfeed_" + feed_count = 0 + + def _get_feed_name(): + nonlocal feed_count + feed_count += 1 + feed_name = name + str(feed_count) + return feed_name + + yield _get_feed_name + + +@pytest.mark.enterprise +@pytest.mark.integration +class TestFeedsClient: + def test__create_feed_windows_platform__succeeds( + self, + create_feed: Callable, + create_feed_request: Callable, + get_feed_name: Callable, + ): + """Test the case of a completely successful create feed API for Windows platform.""" + feed_name = get_feed_name() + request_body = create_feed_request( + feed_name=feed_name, + description=FEED_DESCRIPTION, + platform=Platform.WINDOWS, + ) + response = create_feed(request_body) + + assert response.id is not None + assert response.workspace is not None + assert response.name == feed_name + assert response.platform == Platform.WINDOWS + assert response.description == FEED_DESCRIPTION + + def test__create_feed_linux_platform__succeeds( + self, + create_feed: Callable, + create_feed_request: Callable, + get_feed_name: Callable, + ): + """Test the case of a completely successful create feed API for Linux platform.""" + feed_name = get_feed_name() + request_body = create_feed_request( + feed_name=feed_name, + description=FEED_DESCRIPTION, + platform=Platform.NI_LINUX_RT, + ) + response = create_feed(request_body) + + assert response.id is not None + assert response.workspace is not None + assert response.name == feed_name + assert response.platform == Platform.NI_LINUX_RT + assert response.description == FEED_DESCRIPTION + + def test__query_feeds_windows_platform__succeeds( + self, + client: FeedsClient, + create_feed: Callable, + create_feed_request: Callable, + get_feed_name: Callable, + ): + """Test the case for querying available feeds for Windows platform.""" + create_feed_request_body = create_feed_request( + feed_name=get_feed_name(), + description=FEED_DESCRIPTION, + platform=Platform.WINDOWS, + ) + create_feed_resp = create_feed(create_feed_request_body) + assert create_feed_resp.id is not None + + query_feed_resp = client.query_feeds(platform=Platform.WINDOWS) + assert query_feed_resp is not None + + def test__query_feeds_linux_platform__succeeds( + self, + client: FeedsClient, + create_feed: Callable, + create_feed_request: Callable, + get_feed_name: Callable, + ): + """Test the case for querying available feeds for Linux platform.""" + create_feed_request_body = create_feed_request( + feed_name=get_feed_name(), + description=FEED_DESCRIPTION, + platform=Platform.NI_LINUX_RT, + ) + create_feed_resp = create_feed(create_feed_request_body) + assert create_feed_resp.id is not None + + query_feed_resp = client.query_feeds(platform=Platform.NI_LINUX_RT) + assert query_feed_resp is not None + + def test__query_feeds__invalid_workspace_raises( + self, + client: FeedsClient, + invalid_id: str, + ): + """Test the case of query feeds API with invalid workspace id.""" + with pytest.raises(ApiException, match="UnauthorizedWorkspaceError"): + client.query_feeds(workspace=invalid_id) + + def test__upload_package__succeeds( + self, + client: FeedsClient, + create_feed: Callable, + create_feed_request: Callable, + get_feed_name: Callable, + ): + """Test the case of upload package to feed.""" + create_feed_request_body = create_feed_request( + feed_name=get_feed_name(), + description=FEED_DESCRIPTION, + platform=Platform.WINDOWS, + ) + create_feed_resp = create_feed(create_feed_request_body) + assert create_feed_resp.id is not None + + upload_pacakge_rsp = client.upload_package( + package_file_path=PACKAGE_PATH, + feed_id=create_feed_resp.id, + overwrite=True, + ) + assert upload_pacakge_rsp is not None + + def test__upload_package_content__succeeds( + self, + client: FeedsClient, + create_feed: Callable, + create_feed_request: Callable, + binary_pkg_file_data: BinaryIO, + get_feed_name: Callable, + ): + """Test the case of upload package content to feed.""" + create_feed_request_body = create_feed_request( + feed_name=get_feed_name(), + description=FEED_DESCRIPTION, + platform=Platform.WINDOWS, + ) + create_feed_resp = create_feed(create_feed_request_body) + assert create_feed_resp.id is not None + + upload_pacakge_rsp = client.upload_package_content( + package=binary_pkg_file_data, + feed_id=create_feed_resp.id, + overwrite=True, + ) + assert upload_pacakge_rsp is not None + + def test__upload_package_content__invalid_feed_id_raises( + self, + client: FeedsClient, + binary_pkg_file_data: BinaryIO, + invalid_id: str, + ): + """Test the case of uploading package to Invalid feed.""" + with pytest.raises(ApiException, match="FeedNotFoundError"): + client.upload_package_content( + package=binary_pkg_file_data, + feed_id=invalid_id, + ) + + def test__delete_windows_feed__succeeds( + self, + client: FeedsClient, + create_feed_request: Callable, + get_feed_name: Callable, + ): + """Test the case of deleting Windows feed.""" + create_feed_request_body = create_feed_request( + feed_name=get_feed_name(), + description=FEED_DESCRIPTION, + platform=Platform.WINDOWS, + ) + create_feed_resp = client.create_feed(create_feed_request_body) + assert create_feed_resp.id is not None + + delete_feed_resp = client.delete_feed(id=create_feed_resp.id) + assert delete_feed_resp is None + + def test__delete__linux_feed__succeeds( + self, + client: FeedsClient, + create_feed_request: Callable, + get_feed_name: Callable, + ): + """Test the case of deleting Linux feed.""" + create_feed_request_body = create_feed_request( + feed_name=get_feed_name(), + description=FEED_DESCRIPTION, + platform=Platform.NI_LINUX_RT, + ) + create_feed_resp = client.create_feed(create_feed_request_body) + assert create_feed_resp.id is not None + + delete_feed_resp = client.delete_feed(id=create_feed_resp.id) + assert delete_feed_resp is None diff --git a/tests/integration/feeds/test_files/sample-measurement_0.5.0_windows_x64.nipkg b/tests/integration/feeds/test_files/sample-measurement_0.5.0_windows_x64.nipkg new file mode 100644 index 00000000..a1ad596f Binary files /dev/null and b/tests/integration/feeds/test_files/sample-measurement_0.5.0_windows_x64.nipkg differ