Skip to content

Commit 35d32b5

Browse files
authored
feat: Add minimal client implementation for Feeds API (#73)
1 parent 2c7e37c commit 35d32b5

18 files changed

+842
-2
lines changed

docs/api_reference.rst

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ API Reference
1414
api_reference/dataframe
1515
api_reference/spec
1616
api_reference/file
17+
api_reference/feeds
1718

1819
Indices and tables
1920
------------------

docs/api_reference/feeds.rst

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.. _api_tag_page:
2+
3+
nisystemlink.clients.feeds
4+
======================
5+
6+
.. autoclass:: nisystemlink.clients.feeds.FeedsClient
7+
:exclude-members: __init__
8+
9+
.. automethod:: __init__
10+
.. automethod:: create_feed
11+
.. automethod:: query_feeds
12+
.. automethod:: upload_package
13+
.. automethod:: upload_package_content
14+
.. automethod:: delete_feed
15+
16+
.. automodule:: nisystemlink.clients.feeds.models
17+
:members:
18+
:imported-members:

docs/getting_started.rst

+39-2
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ Update and Delete Specifications
186186
:language: python
187187
:linenos:
188188

189-
190189
File API
191190
-------
192191

@@ -211,4 +210,42 @@ Get the metadata of a File using its Id and download it.
211210

212211
.. literalinclude:: ../examples/file/download_file.py
213212
:language: python
214-
:linenos:
213+
:linenos:
214+
215+
Feeds API
216+
-------
217+
218+
Overview
219+
~~~~~~~~
220+
221+
The :class:`.FeedsClient` class is the primary entry point of the Feeds API.
222+
223+
When constructing a :class:`.FeedsClient`, you can pass an
224+
:class:`.HttpConfiguration` (like one retrieved from the
225+
:class:`.HttpConfigurationManager`), or let :class:`.FeedsClient` use the
226+
default connection. The default connection depends on your environment.
227+
228+
With a :class:`.FeedsClient` object, you can:
229+
230+
* Get the list of feeds, create feed, upload package to feed and delete feed.
231+
232+
Examples
233+
~~~~~~~~
234+
235+
Create a new feed.
236+
237+
.. literalinclude:: ../examples/feeds/create_feed.py
238+
:language: python
239+
:linenos:
240+
241+
Query feeds and upload a package to feed.
242+
243+
.. literalinclude:: ../examples/feeds/query_and_upload_feeds.py
244+
:language: python
245+
:linenos:
246+
247+
Delete a feed.
248+
249+
.. literalinclude:: ../examples/feeds/delete_feed.py
250+
:language: python
251+
:linenos:

examples/feeds/create_feed.py

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Functionality of creating feeds APIs."""
2+
3+
from nisystemlink.clients.core import ApiException, HttpConfiguration
4+
from nisystemlink.clients.feeds._feeds_client import FeedsClient
5+
from nisystemlink.clients.feeds.models import (
6+
CreateFeedRequest,
7+
Platform,
8+
)
9+
10+
# Update the constants.
11+
FEED_NAME = ""
12+
FEED_DESCRIPTION = ""
13+
PLATFORM = Platform.WINDOWS
14+
WORKSPACE_ID = (
15+
None # None uses Default workspace. Replace with Systemlink workspace id.
16+
)
17+
18+
server_url = "" # SystemLink API URL
19+
server_api_key = "" # SystemLink API key
20+
21+
# Provide the valid API key and API URL for client initialization.
22+
client = FeedsClient(HttpConfiguration(api_key=server_api_key, server_uri=server_url))
23+
24+
# Creating Feeds.
25+
try:
26+
feed_request = CreateFeedRequest(
27+
name=FEED_NAME,
28+
description=FEED_DESCRIPTION,
29+
platform=PLATFORM,
30+
workspace=WORKSPACE_ID,
31+
)
32+
feed_details = client.create_feed(feed=feed_request)
33+
34+
print("Feed created Successfully.")
35+
print(f"Created feed details: {feed_details}")
36+
37+
except ApiException as exp:
38+
print(exp)
39+
except Exception as exp:
40+
print(exp)

examples/feeds/delete_feed.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Functionality of deleting feed API."""
2+
3+
from nisystemlink.clients.core import ApiException, HttpConfiguration
4+
from nisystemlink.clients.feeds._feeds_client import FeedsClient
5+
from nisystemlink.clients.feeds.models import Platform
6+
from nisystemlink.clients.feeds.utilities._get_feed_details import get_feed_by_name
7+
8+
# Update the constants.
9+
FEED_NAME = ""
10+
PLATFORM = Platform.WINDOWS
11+
WORKSPACE_ID = (
12+
None # None uses Default workspace. Replace with Systemlink workspace id.
13+
)
14+
15+
server_url = "" # SystemLink API URL
16+
server_api_key = "" # SystemLink API key
17+
18+
# Provide the valid API key and API URL for client initialization.
19+
client = FeedsClient(HttpConfiguration(api_key=server_api_key, server_uri=server_url))
20+
21+
# Deleting Feed.
22+
try:
23+
# Get ID of the Feed to delete by name
24+
feeds = client.query_feeds(platform=PLATFORM, workspace=WORKSPACE_ID)
25+
feed = get_feed_by_name(feeds=feeds, name=FEED_NAME)
26+
feed_id = feed.id if feed else None
27+
28+
# Delete the Feed by ID
29+
if feed_id:
30+
client.delete_feed(id=feed_id)
31+
print("Feed deleted successfully.")
32+
33+
except ApiException as exp:
34+
print(exp)
35+
except Exception as exp:
36+
print(exp)
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Functionality of uploading & querying feeds APIs."""
2+
3+
from nisystemlink.clients.core import ApiException, HttpConfiguration
4+
from nisystemlink.clients.feeds._feeds_client import FeedsClient
5+
from nisystemlink.clients.feeds.models import Platform
6+
from nisystemlink.clients.feeds.utilities._get_feed_details import get_feed_by_name
7+
8+
# Update the constants.
9+
FEED_NAME = ""
10+
PLATFORM = None
11+
FEED_DESCRIPTION = ""
12+
PLATFORM = Platform.WINDOWS
13+
WORKSPACE_ID = (
14+
None # None uses Default workspace. Replace with Systemlink workspace id.
15+
)
16+
PACKAGE_PATH = ""
17+
18+
server_url = "" # SystemLink API URL
19+
server_api_key = "" # SystemLink API key
20+
21+
# Provide the valid API key and API URL for client initialization.
22+
client = FeedsClient(HttpConfiguration(api_key=server_api_key, server_uri=server_url))
23+
24+
# To upload a package to feed.
25+
try:
26+
# Get ID of the Feed to upload by name
27+
feeds = client.query_feeds(platform=PLATFORM, workspace=WORKSPACE_ID)
28+
feed = get_feed_by_name(feeds=feeds, name=FEED_NAME)
29+
feed_id = feed.id if feed else None
30+
31+
# Upload the package to Feed by ID
32+
if feed_id:
33+
client.upload_package(
34+
feed_id=feed_id,
35+
overwrite=True,
36+
package_file_path=PACKAGE_PATH,
37+
)
38+
print("Package uploaded sucessfully.")
39+
40+
except ApiException as exp:
41+
print(exp)
42+
43+
except Exception as exp:
44+
print(exp)
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from ._feeds_client import FeedsClient
2+
3+
# flake8: noqa
+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
"""Implementation of SystemLink Feeds Client."""
2+
3+
from typing import BinaryIO, List, Optional
4+
5+
from nisystemlink.clients import core
6+
from nisystemlink.clients.core._uplink._base_client import BaseClient
7+
from nisystemlink.clients.core._uplink._methods import delete, get, post
8+
from uplink import Part, Path, Query
9+
10+
from . import models
11+
12+
13+
class FeedsClient(BaseClient):
14+
def __init__(self, configuration: Optional[core.HttpConfiguration] = None):
15+
"""Initialize an instance.
16+
17+
Args:
18+
configuration: Defines the web server to connect to and information about
19+
how to connect. If not provided, the
20+
:class:`HttpConfigurationManager <nisystemlink.clients.core.HttpConfigurationManager>`
21+
is used to obtain the configuration.
22+
23+
Raises:
24+
ApiException: if unable to communicate with the Feeds Service.
25+
"""
26+
if configuration is None:
27+
configuration = core.HttpConfigurationManager.get_configuration()
28+
29+
super().__init__(configuration, base_path="/nifeed/v1/")
30+
31+
@post("feeds")
32+
def create_feed(self, feed: models.CreateFeedRequest) -> models.Feed:
33+
"""Create a new feed with the provided feed details.
34+
35+
Args:
36+
feeds (models.CreateFeedsRequest): Request to create the feed.
37+
38+
Returns:
39+
models.Feed: Details of the created feed.
40+
41+
Raises:
42+
ApiException: if unable to communicate with the Feeds Service.
43+
"""
44+
...
45+
46+
@get("feeds", args=[Query, Query])
47+
def __query_feeds(
48+
self,
49+
platform: Optional[str] = None,
50+
workspace: Optional[str] = None,
51+
) -> models.QueryFeedsResponse:
52+
"""Lists available feeds for the Platform `platform` under the Workspace `workspace`.
53+
54+
Args:
55+
platform (Optional[str]): Information about system platform. Defaults to None.
56+
workspace (Optional[str]): Workspace id. Defaults to None.
57+
58+
Returns:
59+
models.QueryFeedsResponse: List of feeds.
60+
61+
Raises:
62+
ApiException: if unable to communicate with the Feeds Service.
63+
"""
64+
...
65+
66+
def query_feeds(
67+
self,
68+
platform: Optional[models.Platform] = None,
69+
workspace: Optional[str] = None,
70+
) -> List[models.Feed]:
71+
"""Lists available feeds for the Platform `platform` under the Workspace `workspace`.
72+
73+
Args:
74+
platform (Optional[models.Platform]): Information about system platform.
75+
Defaults to None.
76+
workspace (Optional[str]): Workspace id. Defaults to None.
77+
78+
Returns:
79+
List[models.Feed]: List of feeds.
80+
81+
Raises:
82+
ApiException: if unable to communicate with the Feeds Service.
83+
"""
84+
platform_by_str = platform.value if platform is not None else None
85+
response = self.__query_feeds(
86+
platform=platform_by_str,
87+
workspace=workspace,
88+
).feeds
89+
90+
return response
91+
92+
@post(
93+
"feeds/{feedId}/packages",
94+
args=[Path(name="feedId"), Part(), Query(name="shouldOverwrite")],
95+
)
96+
def __upload_package(
97+
self,
98+
feed_id: str,
99+
package: Part,
100+
overwrite: Query = False,
101+
) -> models.Package:
102+
"""Upload package to SystemLink feed.
103+
104+
Args:
105+
feed_id (str): ID of the feed.
106+
package (Part): Package file to be uploaded.
107+
overwrite (Query): Set to True, to overwrite the package if it already exists.
108+
Defaults to False.
109+
110+
Returns:
111+
models.Package: Uploaded package information.
112+
113+
Raises:
114+
ApiException: if unable to communicate with the Feeds Service.
115+
"""
116+
...
117+
118+
def upload_package(
119+
self,
120+
feed_id: str,
121+
package_file_path: str,
122+
overwrite: bool = False,
123+
) -> models.Package:
124+
"""Upload package to SystemLink feed.
125+
126+
Args:
127+
feed_id (str): ID of the feed.
128+
package_file_path (str): File path of the package to be uploaded.
129+
overwrite (bool): Set to True, to overwrite the package if it already exists.
130+
Defaults to False.
131+
132+
Returns:
133+
models.Package: Uploaded package information.
134+
135+
Raises:
136+
ApiException: if unable to communicate with the Feeds Service.
137+
OSError: if the file does not exist or cannot be opened.
138+
"""
139+
response = self.__upload_package(
140+
feed_id=feed_id,
141+
overwrite=overwrite,
142+
package=open(package_file_path, "rb"),
143+
)
144+
145+
return response
146+
147+
def upload_package_content(
148+
self,
149+
feed_id: str,
150+
package: BinaryIO,
151+
overwrite: bool = False,
152+
) -> models.Package:
153+
"""Upload package to SystemLink feed.
154+
155+
Args:
156+
feed_id (str): ID of the feed.
157+
package (BinaryIO): Package file to be uploaded.
158+
overwrite (bool): Set to True, to overwrite the package if it already exists.
159+
Defaults to False.
160+
161+
Returns:
162+
models.Package: Uploaded package information.
163+
164+
Raises:
165+
ApiException: if unable to communicate with the Feeds Service.
166+
"""
167+
response = self.__upload_package(
168+
feed_id=feed_id,
169+
overwrite=overwrite,
170+
package=package,
171+
)
172+
173+
return response
174+
175+
@delete(
176+
"feeds/{feedId}",
177+
args=[Path(name="feedId")],
178+
)
179+
def delete_feed(self, id: str) -> None:
180+
"""Delete feed and its packages.
181+
182+
Args:
183+
id (str): ID of the feed to be deleted.
184+
185+
Returns:
186+
None.
187+
188+
Raises:
189+
ApiException: if unable to communicate with the Feeds Service.
190+
"""
191+
...

0 commit comments

Comments
 (0)