def test_build_commands_names_dict_no_duplicate_names(self): """ Given - dictionary containing names of requests from a collection When - There are no requests with names which have the same kebab case Then - returns names' dictionary with an entry for each request's name kebab case and the original name """ requests = [{"name": "Test Number One"}, {"name": "Test number two"}] names_dict = build_commands_names_dict(requests) assert len(names_dict[tools.to_kebab_case("Test Number One")]) == 1 assert len(names_dict[tools.to_kebab_case("Test number two")]) == 1 assert len(names_dict) == 2
def build_commands_names_dict(items: list) -> dict: names_dict = defaultdict(list) for item in items: request_name = item.get('name', None) if request_name: command_name = tools.to_kebab_case(request_name) names_dict[command_name].append(request_name) return names_dict
def test_build_commands_names_dict_none_names(self): """ Given - dictionary containing names of requests from a collection When - There's a request with no name key Then - returns names' dictionary with an entry for each request's name kebab case and the original name and just them """ requests = [{ "None": None }, { "name": "Test number one" }, { "name": "Test number two" }] names_dict = build_commands_names_dict(requests) assert len(names_dict[tools.to_kebab_case("Test Number One")]) == 1 assert len(names_dict[tools.to_kebab_case("Test number two")]) == 1 assert len(names_dict) == 2
def test_build_commands_names_dict_duplicate_names(self): """ Given - dictionary containing names of requests from a collection When - There are requests with names which have the same kebab case Then - returns names' dictionary with an entry of the matching kebab-case which has a list with the problematic names """ requests = [{ "name": "Test Number One" }, { "name": "Test number one" }, { "name": "Test number two" }] names_dict = build_commands_names_dict(requests) assert len(names_dict[tools.to_kebab_case("Test Number One")]) == 2 assert len(names_dict[tools.to_kebab_case("Test number two")]) == 1 assert len(names_dict) == 2
def convert_request_to_command(item): logger.debug(f'converting request to command: {item.get("name")}') command_name = tools.to_kebab_case(item.get('name')) context_prefix = tools.to_pascal_case(item.get('name')) request = item.get('request', {}) logger.debug(f'converting postman headers of request: {item.get("name")}') headers = postman_headers_to_conf_headers(request.get('header'), skip_authorization_header=True) url_path = '' args = [] outputs = [] returns_file = False logger.debug(f'creating url arguments of request: {item.get("name")}') request_url_object = item.get('request').get('url') if not request_url_object: logger.error( f'failed to get item.request.url.path object of request {item.get("name")}. ' f'Go to Postman, Save the request and try again with the updated collection.' ) return None url_path = '/'.join(request_url_object.get('path')).replace('{{', '{').replace( '}}', '}') for url_path_item in request_url_object.get('path'): if re.match(r'\{\{.*\}\}', url_path_item): arg = IntegrationGeneratorArg(name=url_path_item.replace( '{{', '').replace('}}', ''), description='', in_='url') args.append(arg) logger.debug(f'creating query arguments of request: {item.get("name")}') for q in request_url_object.get('query', []): arg = IntegrationGeneratorArg(name=q.get('key'), description='', in_='query') args.append(arg) logger.debug( f'creating arguments which will be passed to the request body of request: {item.get("name")}' ) request_body = request.get('body') body_format = None if request_body: if request_body.get('mode') == 'raw': try: body_obj = json.loads(request_body.get('raw')) body_format = create_body_format(body_obj) for key, value in flatten_json(body_obj).items(): path_split = key.split('.') json_path = path_split[:-1] arg_name = path_split[-1] arg = IntegrationGeneratorArg(name=arg_name, description='', in_='body', in_object=json_path) args.append(arg) except Exception: logger.exception( f'Failed to parse {item.get("name")} request body as JSON.' ) if not item.get('response') or item.get('response') == 0: logger.error( f'[{item.get("name")}] request is missing response. Make sure to save at least one successful ' f'response in Postman') else: response = item.get('response')[0] try: if response.get('_postman_previewlanguage') == 'json': body = json.loads(response.get('body')) for key, value in flatten_json(body).items(): output = IntegrationGeneratorOutput( name=key, description='', type_=determine_type(value)) outputs.append(output) elif response.get('_postman_previewlanguage') == 'raw': returns_file = True except ValueError: logger.exception( f'Failed to parse to JSON response body of {item.get("name")} request.' ) command = IntegrationGeneratorCommand( name=command_name, url_path=url_path, http_method=request.get('method'), headers=headers, description=request.get('description'), arguments=args, outputs=outputs, context_path=context_prefix, root_object='', unique_key='', returns_file=returns_file, body_format=body_format) return command
def postman_to_autogen_configuration( collection_path: Union[Path, str], name, command_prefix, context_path_prefix, category=None) -> IntegrationGeneratorConfig: with open(collection_path, mode='rb') as f: postman_collection = json.load(f) info = postman_collection.get('info', {}) items = postman_collection.get('item', []) postman_auth = postman_collection.get('auth', {}) variable = postman_collection.get('variable', []) logger.debug('trying to find the default base url') host = '' for v in variable: if v['key'] in ('url', 'server'): host = v['value'] logger.debug(f'base url found: {host}') break docker_image = get_docker_image() description = '' commands = [] for item in items: command = convert_request_to_command(item) if command is None: # skip command in case is None # probably something was wrong with the request and command is not created continue commands.append(command) params = [ IntegrationGeneratorParam(name='url', display='Server URL', type_=ParameterType.STRING, required=True, defaultvalue=host or 'https://www.example.com'), IntegrationGeneratorParam(name='proxy', display='Use system proxy', type_=ParameterType.BOOLEAN, required=False), IntegrationGeneratorParam(name='insecure', display='Trust any certificate', type_=ParameterType.BOOLEAN, required=False) ] if postman_auth: if postman_auth['type'] == 'apikey': params.append( IntegrationGeneratorParam(name='api_key', display='API Key', type_=ParameterType.ENCRYPTED, required=True)) elif postman_auth['type'] == 'bearer': params.append( IntegrationGeneratorParam(name='token', display='API Token', type_=ParameterType.ENCRYPTED, required=True)) elif postman_auth['type'] == 'basic': params.append( IntegrationGeneratorParam(name='credentials', display='Username', type_=ParameterType.AUTH, required=True)) else: # look for apikey in headers logger.debug('trying to find apikey in request headers') auth_header_found = False for item in items: if auth_header_found: break request = item.get('request', {}) for header in request.get('header', []): if header.get('key') == 'Authorization': params.append( IntegrationGeneratorParam( name='api_key', display='API Key', type_=ParameterType.ENCRYPTED, required=True)) logger.debug('found Authorization header') if '{{' in header.get('value'): # if header value contains {{ means it has a format like 'Authorization': 'SWSS {{apikey}}' # header_format will be f'SWSS {api_key}' header_format = "f'" + re.sub( r'\{\{.*\}\}', '{params["api_key"]}', header.get('value')) + "'" else: header_format = 'params[\'api_key\']' postman_auth = { 'type': 'apikey', 'apikey': [{ "key": "format", "value": header_format, "type": "string" }, { "key": "in", "value": "header", "type": "string" }, { "key": "key", "value": "Authorization", "type": "string" }] } auth_header_found = True break if name: display_name = name id_ = ''.join(e for e in name if e.isalnum()) elif info.get('name'): display_name = info.get('name') id_ = ''.join(e for e in info.get('name') if e.isalnum()) else: display_name = 'Generated Name Replace It' id_ = 'GeneratedNameReplaceIt' config = IntegrationGeneratorConfig( name=id_, display_name=display_name, description=description or info.get('description', 'Generated description - REPLACE THIS'), params=params, category=category or 'Utilities', command_prefix=command_prefix or tools.to_kebab_case(id_), commands=commands, docker_image=docker_image, context_path=context_path_prefix or id_, url=host, base_url_path='', auth=postman_auth, ) return config
def convert_request_to_command(item: dict): logger.debug(f'converting request to command: {item.get("name")}') name = item.get('name') assert isinstance( name, str), 'Could not find name. Is this a valid postman 2.1 collection?' command_name = tools.to_kebab_case(name) context_prefix = tools.to_pascal_case(name) request = item.get('request') if request is None: raise DemistoException( 'Could not find request in the collection. Is it a valid postman collection?' ) logger.debug(f'converting postman headers of request: {name}') headers = postman_headers_to_conf_headers(request.get('header'), skip_authorization_header=True) args = [] outputs = [] returns_file = False logger.debug(f'creating url arguments of request: {name}') request_url_object = request.get('url') if not request_url_object: logger.error( f'failed to get item.request.url.path object of request {name}. ' f'Go to Postman, Save the request and try again with the updated collection.' ) return None url_path = '/'.join(request_url_object.get('path')).replace('{{', '{').replace( '}}', '}') for url_path_item in request_url_object.get('path'): if re.match(r'\{\{.*\}\}', url_path_item): arg = IntegrationGeneratorArg(name=url_path_item.replace( '{{', '').replace('}}', ''), description='', in_='url') args.append(arg) for url_path_variable in request_url_object.get('variable', []): variable_name = url_path_variable.get('key') if not variable_name: continue arg = IntegrationGeneratorArg(name=variable_name, description='', in_='url') args.append(arg) url_path = url_path.replace(f'/:{variable_name}', f'/{{{variable_name}}}') logger.debug(f'creating query arguments of request: {name}') for q in request_url_object.get('query', []): arg = IntegrationGeneratorArg(name=q.get('key'), description='', in_='query') args.append(arg) logger.debug( f'creating arguments which will be passed to the request body of request: {name}' ) request_body = request.get('body') body_format = None if request_body: if request_body.get('mode') == 'raw': try: body_obj = json.loads(request_body.get('raw')) flattened_json = flatten_json(body_obj) shared_arg_to_split_position_dict = find_shared_args_path( flattened_json) for key, value in flattened_json.items(): path_split = key.split('.') json_path = path_split[:-1] min_unique_path_length = shared_arg_to_split_position_dict[ path_split[-1].lower()] + 1 arg_name = '_'.join(path_split[-min_unique_path_length:]) arg = IntegrationGeneratorArg(name=arg_name, description='', in_='body', in_object=json_path) args.append(arg) body_format = create_body_format(body_obj, args) except Exception: logger.exception( f'Failed to parse {name} request body as JSON.') if not item.get('response') or item.get('response') == 0: logger.error( f'[{name}] request is missing response. Make sure to save at least one successful ' f'response in Postman') else: try: response = item.get('response')[ 0] # type: ignore[index] # It will be catched in the except if response.get('_postman_previewlanguage') == 'json': outputs = generate_command_outputs( json.loads(response.get('body'))) elif response.get('_postman_previewlanguage') == 'raw': returns_file = True except (ValueError, IndexError, TypeError): logger.exception( f'Failed to parse to JSON response body of {name} request.') command = IntegrationGeneratorCommand( name=command_name, url_path=url_path, http_method=request.get('method'), headers=headers, description=request.get('description') or '', arguments=args, outputs=outputs, context_path=context_prefix, root_object='', unique_key='', returns_file=returns_file, body_format=body_format) return command