def test_urilist_to_uri(): uri = URI('/api/v1/') id_list = [ '/api/v1/ns/model:sdf:', '/api/v1/ns/model:234:', '/api/v1/ns/model:rfv:' ] assert uri.uriListToMultiURI(id_list) == '/api/v1/ns/model:sdf:234:rfv:' id_list = [ '/api/v1/ns/model:sdf:', '/api/v1/ns/model:234:www:', '/api/v1/ns/model:rfv:' ] assert uri.uriListToMultiURI( id_list) == '/api/v1/ns/model:sdf:234:www:rfv:' id_list = ['/api/v1/ns/model:234:www:'] assert uri.uriListToMultiURI(id_list) == '/api/v1/ns/model:234:www:' id_list = [ '/api/v1/ns/model:sdf:', '/api/v1/ns/model:234:www', '/api/v1/ns/model:rfv:' ] with pytest.raises(ValueError): uri.uriListToMultiURI(id_list) id_list = ['/api/v1/ns/model'] uri.uriListToMultiURI(id_list) == [] id_list = [ '/api/v1/ns/model:sdf:', '/api/v1/ns/model', '/api/v1/ns/model:rfv:' ] uri.uriListToMultiURI(id_list) == '/api/v1/ns/model:sdf:rfv:' assert uri.uriListToMultiURI([]) == []
def test_extract_ids(): uri = URI( '/api/v1/' ) id_list = [ '/api/v1/ns/model:sdf:', '/api/v1/ns/model:234:', '/api/v1/ns/model:rfv:' ] assert uri.extractIds( id_list ) == [ 'sdf', '234', 'rfv' ] id_list = [ '/api/v1/ns/model:sdf:', '/api/v1/ns/model:234:www:', '/api/v1/ns/model:rfv:' ] assert uri.extractIds( id_list ) == [ 'sdf', '234', 'www', 'rfv' ] id_list = [ '/api/v1/ns/model:234:www:' ] assert uri.extractIds( id_list ) == [ '234', 'www' ] id_list = [ '/api/v1/ns/model:sdf:', '/api/v1/ns/model:234:www', '/api/v1/ns/model:rfv:' ] with pytest.raises( ValueError ): uri.extractIds( id_list ) id_list = [ '/api/v1/ns/model:sdf' ] with pytest.raises( ValueError ): uri.extractIds( id_list ) id_list = [ '/api/v1/ns/model' ] with pytest.raises( ValueError ): uri.extractIds( id_list ) id_list = [ '/api/v1/ns/model:sdf:', '/api/v1/ns/model', '/api/v1/ns/model:rfv:' ] with pytest.raises( ValueError ): uri.extractIds( id_list )
def __init__( self, host, root_path, proxy=None, verify_ssl=True, retry_event=None ): # retry_event should be an Event Object, use to cancel retry loops, if the event get's set the retry loop will throw the most recent Exception it ignored super().__init__() self.retry_event = retry_event if not host.startswith(('http:', 'https:')): raise ValueError('hostname must start with http(s):') if host[-1] == '/': raise ValueError('hostname must not end with "/"') self.proxy = proxy self.host = host logging.debug( 'cinp: new client host: "{0}", root_path: "{1}", via: "{2}"'. format(self.host, root_path, self.proxy)) self.uri = URI(root_path) self.opener = request.OpenerDirector() if self.proxy: # not doing 'is not None', so empty strings don't try and proxy # have a proxy option to take it from the envrionment vars self.opener.add_handler( request.ProxyHandler({ 'http': self.proxy, 'https': self.proxy })) else: self.opener.add_handler(request.ProxyHandler({})) self.opener.add_handler(request.HTTPHandler()) if hasattr(http.client, 'HTTPSConnection'): if not verify_ssl: self.opener.add_handler( request.HTTPSHandler( context=ssl._create_unverified_context())) else: self.opener.add_handler(request.HTTPSHandler()) self.opener.add_handler(request.UnknownHandler()) self.opener.addheaders = [ ('User-Agent', 'python CInP client {0}'.format(__CLIENT_VERSION__)), ('Accepts', 'application/json'), ('Accept-Charset', 'utf-8'), ('CInP-Version', __CINP_VERSION__) ]
def uploadFile(self, uri, filepath, filename=None, cb=None, timeout=30): """ filepath can be a string of the path name or a file object. If a file object either specify the filename or make sure your file object exposes the attribute 'name'. Also if file object, must be opened in binary mode, ie: 'rb' NOTE: this is not a CInP function, but a conviance function for uploading large files. """ uri_parser = URI('/') try: # TODO: There has to be a better way to validate this uri (namespace, model, action, id_list, _) = uri_parser.split(uri) except ValueError as e: raise InvalidRequest(str(e)) if action is not None or id_list is not None: raise InvalidRequest( 'file upload target can\'t be an action nor have ids') if isinstance(filepath, str): if filename is None: filename = os.path.basename(filepath) file_reader = _readerWrapper(open(filepath, 'rb'), cb) else: if filename is None: filename = os.path.basename(filepath.name) file_reader = _readerWrapper(filepath, cb) header_map = { 'Content-Disposition': 'inline: filename="{0}"'.format(filename), 'Content-Length': len(file_reader) } (http_code, data, _) = self._request('UPLOAD', uri_parser.build(namespace, model), data=file_reader, header_map=header_map, timeout=timeout) if http_code != 202: logging.warning( 'cinp: Unexpected HTTP Code "{0}" for File Upload'.format( http_code)) raise ResponseError( 'Unexpected HTTP Code "{0}" for File Upload'.format(http_code)) return data['uri']
def test_getElement(): uri = URI( root_path='/api/' ) root_ns = Namespace( name=None, version='0.0', root_path='/api/', converter=None ) ns2 = Namespace( name='ns2', version='0.1', converter=None ) ns3 = Namespace( name='ns3', version='0.2', converter=None ) ns2_2 = Namespace( name='ns2_2', version='0.1', converter=None ) root_ns.addElement( ns2 ) root_ns.addElement( ns3 ) ns2.addElement( ns2_2 ) mdl1 = Model( name='mdl1', field_list={}, transaction_class=TestTransaction ) mdl2 = Model( name='mdl2', field_list={}, transaction_class=TestTransaction ) root_ns.addElement( mdl1 ) ns2_2.addElement( mdl2 ) act1 = Action( name='act1', return_paramater=Paramater( type='String' ), paramater_list=[ Paramater( name='bob', type='Float' ) ], static=False, func=fake_func ) act2 = Action( name='act2', return_paramater=Paramater( type='String' ), paramater_list=[ Paramater( name='stuff', type='Boolean' ) ], func=fake_func ) mdl1.addAction( act1 ) mdl2.addAction( act2 ) assert root_ns.getElement( uri.split( '/api/', root_optional=True ) ) == root_ns assert root_ns.getElement( uri.split( '/api/ns2/', root_optional=True ) ) == ns2 assert root_ns.getElement( uri.split( '/api/ns3/', root_optional=True ) ) == ns3 assert root_ns.getElement( uri.split( '/api/ns2/ns2_2/', root_optional=True ) ) == ns2_2 assert root_ns.getElement( uri.split( '/', root_optional=True ) ) == root_ns assert root_ns.getElement( uri.split( '/ns2/', root_optional=True ) ) == ns2 assert root_ns.getElement( uri.split( '/ns3/', root_optional=True ) ) == ns3 assert root_ns.getElement( uri.split( '/ns2/ns2_2/', root_optional=True ) ) == ns2_2 assert root_ns.getElement( uri.split( '/api/mdl1', root_optional=True ) ) == mdl1 assert root_ns.getElement( uri.split( '/api/ns2/ns2_2/mdl2', root_optional=True ) ) == mdl2 assert root_ns.getElement( uri.split( '/mdl1', root_optional=True ) ) == mdl1 assert root_ns.getElement( uri.split( '/ns2/ns2_2/mdl2', root_optional=True ) ) == mdl2 assert root_ns.getElement( uri.split( '/api/mdl1(act1)', root_optional=True ) ) == act1 assert root_ns.getElement( uri.split( '/api/ns2/ns2_2/mdl2(act2)', root_optional=True ) ) == act2 assert root_ns.getElement( uri.split( '/mdl1(act1)', root_optional=True ) ) == act1 assert root_ns.getElement( uri.split( '/ns2/ns2_2/mdl2(act2)', root_optional=True ) ) == act2 with pytest.raises( ValueError ): root_ns.getElement( '/api/' ) assert root_ns.getElement( uri.split( '/api/nsX/' ) ) is None assert root_ns.getElement( uri.split( '/api/ns2/mdlX' ) ) is None assert root_ns.getElement( uri.split( '/api/mdl1(actX)' ) ) is None
def test_splituri_builduri(): # TODO: test invlid URIs, mabey remove some tests from client_test that are just checking the URI uri = URI( '/api/v1/' ) ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/' ) assert ns == [] assert model is None assert id_list is None assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/' ns = None assert uri.build( ns, model, action, id_list ) == '/api/v1/' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/' ) assert ns == [ 'ns' ] assert model is None assert id_list is None assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/' ns = 'ns' assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/model' ) assert ns == [ 'ns' ] assert model == 'model' assert id_list is None assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/model' id_list = [] assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/model' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/ns2/' ) assert ns == [ 'ns', 'ns2' ] assert model is None assert id_list is None assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/ns2/' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/ns2/model' ) assert ns == [ 'ns', 'ns2' ] assert model == 'model' assert id_list is None assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/ns2/model' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/model::' ) assert ns == [ 'ns' ] assert model == 'model' assert id_list == [ '' ] assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/model::' id_list = '' assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/model::' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/model:ghj:' ) assert ns == [ 'ns' ] assert model == 'model' assert id_list == [ 'ghj' ] assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/model:ghj:' id_list = 'ghj' assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/model:ghj:' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/model:ghj:dsf:sfe:' ) assert ns == [ 'ns' ] assert model == 'model' assert id_list == [ 'ghj', 'dsf', 'sfe' ] assert action is None assert multi is True assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/model:ghj:dsf:sfe:' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/model(action)' ) assert ns == [ 'ns' ] assert model == 'model' assert id_list is None assert action == 'action' assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/model(action)' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/model:sdf:(action)' ) assert ns == [ 'ns' ] assert model == 'model' assert id_list == [ 'sdf' ] assert action == 'action' assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/model:sdf:(action)' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/model:sdf:eed:(action)' ) assert ns == [ 'ns' ] assert model == 'model' assert id_list == [ 'sdf', 'eed' ] assert action == 'action' assert multi is True assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/model:sdf:eed:(action)' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/', root_optional=True ) assert ns == [] assert model is None assert id_list is None assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/' assert uri.build( ns, model, action, id_list, in_root=False ) == '/' with pytest.raises( ValueError ): ( ns, model, action, id_list, multi ) = uri.split( '/', root_optional=False ) ( ns, model, action, id_list, multi ) = uri.split( '/', root_optional=True ) assert ns == [] assert model is None assert id_list is None assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/' assert uri.build( ns, model, action, id_list, in_root=False ) == '/' ( ns, model, action, id_list, multi ) = uri.split( '/api/v1/ns/', root_optional=True ) assert ns == [ 'ns' ] assert model is None assert id_list is None assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/' assert uri.build( ns, model, action, id_list, in_root=False ) == '/ns/' with pytest.raises( ValueError ): ( ns, model, action, id_list, multi ) = uri.split( '/ns/', root_optional=False ) ( ns, model, action, id_list, multi ) = uri.split( '/ns/', root_optional=True ) assert ns == [ 'ns' ] assert model is None assert id_list is None assert action is None assert multi is False assert uri.build( ns, model, action, id_list ) == '/api/v1/ns/' assert uri.build( ns, model, action, id_list, in_root=False ) == '/ns/'
def test_not_allowed_verbs(): server = Server( root_path='/api/', root_version='0.0', debug=True ) ns1 = Namespace( name='ns1', version='0.1', converter=Converter( URI( '/api/' ) ) ) ns1.checkAuth = lambda user, verb, id_list: True field_list = [] field_list.append( Field( name='field1', type='String', length=50 ) ) model1 = Model( name='model1', field_list=field_list, not_allowed_verb_list=[], transaction_class=TestTransaction ) model2 = Model( name='model2', field_list=field_list, not_allowed_verb_list=[ 'GET', 'LIST', 'CALL', 'CREATE', 'UPDATE', 'DELETE', 'DESCRIBE' ], transaction_class=TestTransaction ) model1.checkAuth = lambda user, verb, id_list: True model2.checkAuth = lambda user, verb, id_list: True action1 = Action( name='act', return_paramater=Paramater( type='String' ), func=fake_func ) action2 = Action( name='act', return_paramater=Paramater( type='String' ), func=fake_func ) action1.checkAuth = lambda user, verb, id_list: True action2.checkAuth = lambda user, verb, id_list: True model1.addAction( action1 ) model2.addAction( action2 ) ns1.addElement( model1 ) ns1.addElement( model2 ) server.registerNamespace( '/', ns1 ) with pytest.raises( ValueError ): Model( name='modelX', field_list=[], not_allowed_verb_list=[ 'OPTIONS' ], transaction_class=TestTransaction ) with pytest.raises( ValueError ): Model( name='modelX', field_list=[], not_allowed_verb_list=[ 'ASDF' ], transaction_class=TestTransaction ) req = Request( 'OPTIONS', '/api/ns1/model1', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 200 req = Request( 'OPTIONS', '/api/ns1/model2', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 200 req = Request( 'DESCRIBE', '/api/ns1/model1', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 200 req = Request( 'DESCRIBE', '/api/ns1/model2', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 403 req = Request( 'GET', '/api/ns1/model1:asd:', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 200 req = Request( 'GET', '/api/ns1/model2:asd:', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 403 req = Request( 'LIST', '/api/ns1/model1', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 200 req = Request( 'LIST', '/api/ns1/model2', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 403 req = Request( 'CREATE', '/api/ns1/model1', { 'CINP-VERSION': __CINP_VERSION__ } ) req.data = { 'field1': 'stuff' } res = server.handle( req ) assert res.http_code == 201 req = Request( 'CREATE', '/api/ns1/model2', { 'CINP-VERSION': __CINP_VERSION__ } ) req.data = { 'field1': 'stuff' } res = server.handle( req ) assert res.http_code == 403 req = Request( 'UPDATE', '/api/ns1/model1:sdf:', { 'CINP-VERSION': __CINP_VERSION__ } ) req.data = { 'field1': 'stuff' } res = server.handle( req ) assert res.http_code == 200 req = Request( 'UPDATE', '/api/ns1/model2:sdf:', { 'CINP-VERSION': __CINP_VERSION__ } ) req.data = { 'field1': 'stuff' } res = server.handle( req ) assert res.http_code == 403 req = Request( 'DELETE', '/api/ns1/model1:asd:', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 200 req = Request( 'DELETE', '/api/ns1/model2:asd:', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 403 req = Request( 'CALL', '/api/ns1/model1(act)', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 200 req = Request( 'CALL', '/api/ns1/model2(act)', { 'CINP-VERSION': __CINP_VERSION__ } ) res = server.handle( req ) assert res.http_code == 403
def getFile(self, uri, target_dir='/tmp', file_object=None, cb=None, timeout=30, chunk_size=(4096 * 1024)): """ if file_object is defined: The file contense are written to it and the filename as specified by the server is returned, None is returned if not filename is detected. The file_object is not closed. file_object must be opened with the 'b' attribute. Otherwise a file is created in target_dir, and the full path is returned. If the filename is not specified by the server, and a random filename is chosen. WARNING: there isn't checking done to make sure the target file does not allready exist, there is a possibility it could clober something that allready exists. we do make sure the filename fits a regex pattern that prevents it from escaping the target_dir. The "filename" as sent by the server is the "model" of the uri. make sure target_dir exists before calling getFile """ uri_parser = URI('/') try: # TODO: There has to be a better way to validate this uri (_, filename, _, _, _) = uri_parser.split(uri) except ValueError as e: raise InvalidRequest(str(e)) # Due to the return value we have to do our own request, this is pretty much a stright GET url = '{0}{1}'.format(self.host, uri) req = request.Request(url) req.get_method = lambda: 'GET' try: resp = self.opener.open(req, timeout=timeout) except request.HTTPError as e: raise ResponseError('HTTPError "{0}"'.format(e)) except request.URLError as e: if isinstance(e.reason, socket.timeout): raise Timeout( 'Request Timeout after {0} seconds'.format(timeout)) raise ResponseError('URLError "{0}" for "{1}" via "{2}"'.format( e, url, self.proxy)) http_code = resp.code if http_code != 200: logging.warning( 'cinp: Unexpected HTTP Code "{0}" for File Get'.format( http_code)) raise ResponseError( 'Unexpected HTTP Code "{0}" for File Get'.format(http_code)) try: size = resp.headers['Content-Length'] except KeyError: size = 0 if file_object is not None: file_writer = file_object else: if filename is None: file_writer = NamedTemporaryFile(dir=target_dir, mode='wb') filename = file_writer.name else: filename = os.path.join(target_dir, filename) file_writer = open(filename, 'wb') buff = resp.read(chunk_size) while buff: file_writer.write(buff) if cb: cb(file_writer.tell(), size) buff = resp.read(chunk_size) resp.close() if file_object is not None: return filename else: file_writer.close() return filename