11from json .decoder import JSONDecodeError
2- from keyword import iskeyword
32
43import requests
54from requests .exceptions import RequestException
65
76from cnct .client .constants import CONNECT_ENDPOINT_URL , CONNECT_SPECS_URL
8- from cnct .client .exceptions import ClientError , NotFoundError
7+ from cnct .client .exceptions import ClientError
98from cnct .client .models import Collection , NS
109from cnct .client .utils import get_headers
11- from cnct .help import DefaultFormatter
12- from cnct .specs . parser import parse
10+ from cnct .client . help_formatter import DefaultFormatter
11+ from cnct .client . openapi import OpenAPISpecs
1312
1413
1514class ConnectClient :
@@ -19,11 +18,12 @@ class ConnectClient:
1918 def __init__ (
2019 self ,
2120 api_key ,
22- endpoint = CONNECT_ENDPOINT_URL ,
23- specs_location = CONNECT_SPECS_URL ,
24- default_headers = {},
21+ endpoint = None ,
22+ use_specs = True ,
23+ specs_location = None ,
24+ validate_using_specs = True ,
25+ default_headers = None ,
2526 default_limit = 100 ,
26- help_formatter = DefaultFormatter (),
2727 ):
2828 """
2929 Create a new instance of the ConnectClient.
@@ -40,65 +40,40 @@ def __init__(
4040 if default_headers and 'Authorization' in default_headers :
4141 raise ValueError ('`default_headers` cannot contains `Authorization`' )
4242
43- self .endpoint = endpoint
43+ self .endpoint = endpoint or CONNECT_ENDPOINT_URL
4444 self .api_key = api_key
45- self .default_headers = default_headers
46- self .specs_location = specs_location
47- self .specs = parse (self .specs_location ) if self .specs_location else None
45+ self .default_headers = default_headers or {}
46+ self ._use_specs = use_specs
47+ self ._validate_using_specs = validate_using_specs
48+ self .specs_location = specs_location or CONNECT_SPECS_URL
49+ self .specs = None
50+ if self ._use_specs :
51+ self .specs = OpenAPISpecs (self .specs_location )
4852 self .response = None
49- self ._help_formatter = help_formatter
53+ self ._help_formatter = DefaultFormatter ( self . specs )
5054
5155 def __getattr__ (self , name ):
5256 """
53- Returns a NS or a Collection object called ``name``.
57+ Returns a collection object called ``name``.
5458
55- :param name: The name of the NS or Collection to retrieve.
59+ :param name: The name of the collection to retrieve.
5660 :type name: str
57- :raises AttributeError: If OpenAPI specs are not avaliable.
58- :raises AttributeError: If the name does not exist.
59- :return: a Collection or a NS called ``name``.
60- :rtype: Collection, NS
61+ :return: a collection called ``name``.
62+ :rtype: Collection
6163 """
62- if not self .specs :
63- raise AttributeError (
64- 'No specs available. Use `ns` '
65- 'or `collection` methods instead.'
66- )
6764 if '_' in name :
6865 name = name .replace ('_' , '-' )
69- if name in self .specs .namespaces :
70- return self .ns (name )
71- if name in self .specs .collections :
72- return self .collection (name )
73- raise AttributeError ('Unable to resolve {}.' .format (name ))
74-
75- def __dir__ (self ):
76- """
77- Return a list of attributes defined for this ConnectClient instance.
78- The returned list includes the names of the root namespaces and collections.
66+ return self .collection (name )
7967
80- :return: List of attributes.
81- :rtype: list
82- """
83- if not self .specs :
84- return super ().__dir__ ()
85- ns = list (self .specs .namespaces .keys ())
86- cl = list (self .specs .collections .keys ())
87- additional_names = []
88- for name in cl + ns :
89- if '-' in name :
90- name = name .replace ('-' , '_' )
91- if name .isidentifier () and not iskeyword (name ):
92- additional_names .append (name )
93- return sorted (super ().__dir__ () + additional_names )
68+ def __call__ (self , name ):
69+ return self .ns (name )
9470
9571 def ns (self , name ):
9672 """
9773 Returns the namespace called ``name``.
9874
9975 :param name: The name of the namespace to access.
10076 :type name: str
101- :raises NotFoundError: If a namespace with name ``name`` does not exist.
10277 :return: The namespace called ``name``.
10378 :rtype: NS
10479 """
@@ -108,19 +83,14 @@ def ns(self, name):
10883 if not name :
10984 raise ValueError ('`name` must not be blank.' )
11085
111- if not self .specs :
112- return NS (self , f'{ self .endpoint } /{ name } ' ,)
113- if name in self .specs .namespaces :
114- return NS (self , f'{ self .endpoint } /{ name } ' , self .specs .namespaces [name ])
115- raise NotFoundError (f'The namespace { name } does not exist.' )
86+ return NS (self , name )
11687
11788 def collection (self , name ):
11889 """
11990 Returns the collection called ``name``.
12091
12192 :param name: The name of the collection to access.
12293 :type name: str
123- :raises NotFoundError: If a collection with name ``name`` does not exist.
12494 :return: The collection called ``name``.
12595 :rtype: Collection
12696 """
@@ -130,18 +100,10 @@ def collection(self, name):
130100 if not name :
131101 raise ValueError ('`name` must not be blank.' )
132102
133- if not self .specs :
134- return Collection (
135- self ,
136- f'{ self .endpoint } /{ name } ' ,
137- )
138- if name in self .specs .collections :
139- return Collection (
140- self ,
141- f'{ self .endpoint } /{ name } ' ,
142- self .specs .collections [name ],
143- )
144- raise NotFoundError (f'The collection { name } does not exist.' )
103+ return Collection (
104+ self ,
105+ name ,
106+ )
145107
146108 def get (self , url , ** kwargs ):
147109 return self .execute ('get' , url , ** kwargs )
@@ -165,7 +127,17 @@ def update(self, url, payload=None, **kwargs):
165127 def delete (self , url , ** kwargs ):
166128 return self .execute ('delete' , url , ** kwargs )
167129
168- def execute (self , method , url , ** kwargs ):
130+ def execute (self , method , path , ** kwargs ):
131+ if (
132+ self ._use_specs
133+ and self ._validate_using_specs
134+ and not self .specs .exists (method , path )
135+ ):
136+ # TODO more info, specs version, method etc
137+ raise ClientError (f'The path `{ path } ` does not exist.' )
138+
139+ url = f'{ self .endpoint } /{ path } '
140+
169141 kwargs = kwargs or {}
170142 if 'headers' in kwargs :
171143 kwargs ['headers' ].update (get_headers (self .api_key ))
@@ -191,8 +163,12 @@ def execute(self, method, url, **kwargs):
191163 status_code = self .response .status_code if self .response is not None else None
192164 raise ClientError (status_code = status_code , ** api_error ) from re
193165
166+ def print_help (self , obj ):
167+ print ()
168+ print (self ._help_formatter .format (obj ))
169+
194170 def help (self ):
195- self ._help_formatter . print_help (self . specs )
171+ self .print_help (None )
196172 return self
197173
198174 def _execute_http_call (self , method , url , kwargs ):
0 commit comments