def primarize_translation( api_url, api_key, translation_id, verbose=False, ): try: client = ConnectClient( api_key=api_key, endpoint=api_url, use_specs=False, max_retries=3, logger=RequestLogger() if verbose else None, ) payload = { 'primary': True, } translation = (client.ns('localization').translations[translation_id]. action('primarize').post(payload=payload)) except ClientError as error: if error.status_code == 404: status = format_http_status(error.status_code) raise click.ClickException( f'{status}: Translation {translation_id} not found.') handle_http_error(error) return translation
def _create_client(connect_responses): response_iterator = iter(connect_responses) def _execute_http_call(self, method, url, kwargs): res = next(response_iterator) query, ordering, select = _parse_qs(url) if res.query: assert query == res.query, 'RQL query does not match.' if res.ordering: assert ordering == res.ordering, 'RQL ordering does not match.' if res.select: assert select == res.select, 'RQL select does not match.' mock_kwargs = { 'match_querystring': False, } if res.count is not None: end = 0 if res.count == 0 else res.count - 1 mock_kwargs['status'] = 200 mock_kwargs['headers'] = {'Content-Range': f'items 0-{end}/{res.count}'} mock_kwargs['json'] = [] if isinstance(res.value, Iterable): count = len(res.value) end = 0 if count == 0 else count - 1 mock_kwargs['status'] = 200 mock_kwargs['json'] = res.value mock_kwargs['headers'] = { 'Content-Range': f'items 0-{end}/{count}', } elif isinstance(res.value, dict): mock_kwargs['status'] = res.status or 200 mock_kwargs['json'] = res.value elif res.value is None: if res.exception: mock_kwargs['body'] = res.exception else: mock_kwargs['status'] = res.status or 200 else: mock_kwargs['status'] = res.status or 200 mock_kwargs['body'] = str(res.value) with responses.RequestsMock() as rsps: rsps.add( method.upper(), url, **mock_kwargs, ) self.response = requests.request(method, url, **kwargs) if self.response.status_code >= 400: self.response.raise_for_status() client = ConnectClient('Key', use_specs=False) client._execute_http_call = MethodType(_execute_http_call, client) return client
def dump_customers(api_url, api_key, account_id, output_file, silent, output_path=None): # noqa: CCR001 if not output_path: output_path = os.path.join(os.getcwd(), account_id) else: if not os.path.exists(output_path): raise ClickException( "Output Path does not exist", ) output_path = os.path.join(output_path, account_id) if not output_file: output_file = os.path.join(output_path, 'customers.xlsx') else: output_file = os.path.join(output_path, output_file) if not os.path.exists(output_path): os.mkdir(output_path) elif not os.path.isdir(output_path): raise ClickException( "Exists a file with account id as name but a directory is expected, please rename it", ) try: client = ConnectClient( max_retries=3, api_key=api_key, endpoint=api_url, use_specs=False, default_limit=1000, ) wb = Workbook() _prepare_worksheet(wb.create_sheet('Customers')) _add_countries(wb.create_sheet('Countries')) customers = client.ns('tier').accounts.all() row_idx = 2 count = customers.count() progress = trange(0, count, disable=silent, leave=True, bar_format=DEFAULT_BAR_FORMAT) for customer in customers: progress.set_description(f'Processing customer {customer["id"]}') progress.update(1) _fill_customer_row(wb['Customers'], row_idx, customer) row_idx += 1 except ClientError as error: handle_http_error(error) default_sheet = wb['Sheet'] wb.remove(default_sheet) wb.save(output_file) return output_file
def test_validate_invalid_switch(fs, get_sync_params_env): get_sync_params_env['Ordering Parameters']['C2'] = 'create' get_sync_params_env['Ordering Parameters']['F2'] = 'fulfillment' get_sync_params_env.save(f'{fs.root_path}/test.xlsx') stats = SynchronizerStats() synchronizer = ParamsSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, stats=stats, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Ordering Parameters') synchronizer.sync() assert stats['Ordering Parameters'].get_counts_as_dict() == { 'processed': 1, 'created': 0, 'updated': 0, 'deleted': 0, 'skipped': 0, 'errors': 1, } assert stats['Ordering Parameters']._row_errors == { 2: [ 'Parameters of type ordering are only supported when processing Ordering Parameters. ' 'Has been provided fulfillment.' ], }
def test_update_item( mocked_responses, mocked_items_response, ): mocked_responses.add( method='PUT', url= 'https://localhost/public/v1/products/PRD-276-377-545/items/PRD-276-377-545-0001', json=mocked_items_response[0], status=200, ) client = ConnectClient( api_key='ApiKey SU:123', use_specs=False, endpoint='https://localhost/public/v1', ) item = update_item( client=client, product_id='PRD-276-377-545', item_id='PRD-276-377-545-0001', data=mocked_items_response[0], ) assert item == mocked_items_response[0]
def test_save(fs, mocked_responses, mocked_product_response): synchronizer = ProductSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) mocked_responses.add( method='GET', url='https://localhost/public/v1/products/PRD-276-377-545', json=mocked_product_response, ) synchronizer.open( './tests/fixtures/comparation_product.xlsx', 'Items', ) synchronizer.save(f'{fs.root_path}//test.xlsx') assert os.path.isfile(f'{fs.root_path}/test.xlsx')
def test_validate_primary_is_skipped(fs, get_sync_translations_env, action): get_sync_translations_env['Translations']['B2'] = action get_sync_translations_env.save(f'{fs.root_path}/test.xlsx') stats = SynchronizerStats() synchronizer = TranslationsSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, stats=stats, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Translations') synchronizer.sync() assert stats['Translations'].get_counts_as_dict() == { 'processed': 2, 'created': 0, 'updated': 0, 'deleted': 0, 'skipped': 2, 'errors': 0, }
def test_skipped(get_sync_params_env): stats = SynchronizerStats() synchronizer = ParamsSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, stats=stats, ) synchronizer.open('./tests/fixtures/params_sync.xlsx', 'Ordering Parameters') synchronizer.sync() assert stats['Ordering Parameters'].get_counts_as_dict() == { 'processed': 1, 'created': 0, 'updated': 0, 'deleted': 0, 'skipped': 1, 'errors': 0, }
def test_delete_500(fs, get_sync_config_env, mocked_responses): get_sync_config_env['Configuration']['A2'] = 'asdf#PRD-276-377-545-0001#MKP-123' get_sync_config_env['Configuration']['G2'] = 'MKP-123' get_sync_config_env['Configuration']['D2'] = 'delete' get_sync_config_env.save(f'{fs.root_path}/test.xlsx') synchronizer = ConfigurationValuesSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) mocked_responses.add( method='POST', url='https://localhost/public/v1/products/PRD-276-377-545/configurations', status=500, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Configuration') skipped, created, updated, deleted, errors = synchronizer.sync() assert skipped == 0 assert created == 0 assert updated == 0 assert deleted == 0 assert errors == {2: ['500 Internal Server Error']}
def test_validate_invalid_no_video_url(fs, get_sync_media_env): get_sync_media_env['Media']['C2'] = 'create' get_sync_media_env['Media']['D2'] = 'video' get_sync_media_env['Media']['E2'] = 'image.png' get_sync_media_env['Media']['F2'] = None get_sync_media_env.save(f'{fs.root_path}/test.xlsx') synchronizer = MediaSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Media') skipped, created, updated, deleted, errors = synchronizer.sync() assert skipped == 0 assert created == 0 assert updated == 0 assert deleted == 0 assert errors == { 2: ['Video URL location is required for video type'], }
def test_validate_invalid_json(fs, get_sync_params_env): get_sync_params_env['Ordering Parameters']['C2'] = 'create' get_sync_params_env['Ordering Parameters']['L2'] = 'nojson' get_sync_params_env.save(f'{fs.root_path}/test.xlsx') stats = SynchronizerStats() synchronizer = ParamsSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, stats=stats, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Ordering Parameters') synchronizer.sync() assert stats['Ordering Parameters'].get_counts_as_dict() == { 'processed': 1, 'created': 0, 'updated': 0, 'deleted': 0, 'skipped': 0, 'errors': 1, } assert stats['Ordering Parameters']._row_errors == { 2: ['JSON properties must have json format'] }
def test_validate_wrong_type(fs, get_sync_media_env): get_sync_media_env['Media']['C2'] = 'create' get_sync_media_env['Media']['D2'] = 'wrong' get_sync_media_env.save(f'{fs.root_path}/test.xlsx') synchronizer = MediaSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Media') skipped, created, updated, deleted, errors = synchronizer.sync() assert skipped == 0 assert created == 0 assert updated == 0 assert deleted == 0 assert errors == { 2: ['Media can be either image or video type, provided wrong'], }
def test_validate_wrong_file(fs, get_sync_media_env): get_sync_media_env['Media']['C2'] = 'create' get_sync_media_env['Media']['E2'] = 'wrong.png' get_sync_media_env.save(f'{fs.root_path}/test.xlsx') synchronizer = MediaSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Media') skipped, created, updated, deleted, errors = synchronizer.sync() assert skipped == 0 assert created == 0 assert updated == 0 assert deleted == 0 assert errors == { 2: [ 'Image file is not found, please check that file wrong.png exists in media folder' ], }
def test_validate_wrong_action(fs, get_sync_media_env): get_sync_media_env['Media']['C2'] = 'XYZ' get_sync_media_env.save(f'{fs.root_path}/test.xlsx') synchronizer = MediaSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Media') skipped, created, updated, deleted, errors = synchronizer.sync() assert skipped == 0 assert created == 0 assert updated == 0 assert deleted == 0 assert errors == { 2: [ 'Supported actions are `-`, `create`, `update` or `delete`. Provided XYZ' ], }
def test_delete_item_published(mocked_responses, ): mocked_responses.add( method='DELETE', url= 'https://localhost/public/v1/products/PRD-276-377-545/items/PRD-276-377-545-0001', json={ "error_code": "PRD_038", "errors": [ "Only draft Item can be deleted.", ], }, status=400, ) client = ConnectClient( api_key='ApiKey SU:123', use_specs=False, endpoint='https://localhost/public/v1', ) with pytest.raises(ClickException) as e: delete_item( client=client, product_id='PRD-276-377-545', item_id='PRD-276-377-545-0001', ) assert 'Only draft Item can be deleted.' in str(e.value)
def test_update_item_mpn_exists( mocked_responses, mocked_items_response, ): mocked_responses.add( method='PUT', url= 'https://localhost/public/v1/products/PRD-276-377-545/items/PRD-276-377-545-0001', json={ "error_code": "VAL_001", "errors": [ "mpn: Item with same mpn already exists for the product.", ], }, status=400, ) client = ConnectClient( api_key='ApiKey SU:123', use_specs=False, endpoint='https://localhost/public/v1', ) with pytest.raises(ClickException) as e: update_item( client=client, product_id='PRD-276-377-545', item_id='PRD-276-377-545-0001', data=mocked_items_response[0], ) assert 'Item with same mpn already exists for the product.' in str(e.value)
def test_validate_invalid_param_type(fs, get_sync_params_env): get_sync_params_env['Ordering Parameters']['C2'] = 'create' get_sync_params_env['Ordering Parameters']['H2'] = 'rocket' get_sync_params_env.save(f'{fs.root_path}/test.xlsx') stats = SynchronizerStats() synchronizer = ParamsSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, stats=stats, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Ordering Parameters') synchronizer.sync() assert stats['Ordering Parameters'].get_counts_as_dict() == { 'processed': 1, 'created': 0, 'updated': 0, 'deleted': 0, 'skipped': 0, 'errors': 1, } assert stats['Ordering Parameters']._row_errors == { 2: [ 'Parameter type rocket is not one of the supported ones:email,address,checkbox,' 'choice,domain,subdomain,url,dropdown,object,password,phone,text' ], }
def test_validate_invalid_video_url(fs, get_sync_media_env, video_domain): get_sync_media_env['Media']['C2'] = 'create' get_sync_media_env['Media']['D2'] = 'video' get_sync_media_env['Media']['E2'] = 'image.png' get_sync_media_env['Media']['F2'] = f'http://{video_domain}/video.mov' get_sync_media_env.save(f'{fs.root_path}/test.xlsx') synchronizer = MediaSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Media') skipped, created, updated, deleted, errors = synchronizer.sync() assert skipped == 0 assert created == 0 assert updated == 0 assert deleted == 0 assert errors == { 2: [ 'Videos can be hosted on youtube or vimeo, please also ensure to provide https url. ' f'Invalid url provided is http://{video_domain}/video.mov' ], }
def test_validate_invalid_scope_config(fs, get_sync_params_env): get_sync_params_env['Configuration Parameters']['C2'] = 'create' get_sync_params_env['Configuration Parameters']['G2'] = 'rocket' get_sync_params_env.save(f'{fs.root_path}/test.xlsx') stats = SynchronizerStats() synchronizer = ParamsSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, stats=stats, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Configuration Parameters') synchronizer.sync() assert stats['Configuration Parameters'].get_counts_as_dict() == { 'processed': 1, 'created': 0, 'updated': 0, 'deleted': 0, 'skipped': 0, 'errors': 1, } assert stats['Configuration Parameters']._row_errors == { 2: [ 'Only item, item_marketplace, marketplace and product scopes are supported for ' 'Configuration Parameters' ], }
def test_validate_no_position(fs, get_sync_media_env): get_sync_media_env['Media']['A2'] = None get_sync_media_env['Media']['C2'] = 'create' get_sync_media_env.save(f'{fs.root_path}/test.xlsx') synchronizer = MediaSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Media') skipped, created, updated, deleted, errors = synchronizer.sync() assert skipped == 0 assert created == 0 assert updated == 0 assert deleted == 0 assert errors == { 2: ['Position is required and must be an integer between 1 and 8'] }
def test_validate_delete_not_found(fs, get_sync_params_env, mocked_responses): get_sync_params_env['Ordering Parameters']['C2'] = 'delete' get_sync_params_env.save(f'{fs.root_path}/test.xlsx') stats = SynchronizerStats() synchronizer = ParamsSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, stats=stats, ) mocked_responses.add( method='DELETE', url= 'https://localhost/public/v1/products/PRD-276-377-545/parameters/PRM-276-377-545-0008', status=404, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Ordering Parameters') synchronizer.sync() assert stats['Ordering Parameters'].get_counts_as_dict() == { 'processed': 1, 'created': 0, 'updated': 0, 'deleted': 1, 'skipped': 0, 'errors': 0, }
def test_update_image_404(fs, get_sync_media_env, mocked_responses, mocked_media_response): get_sync_media_env['Media']['C2'] = 'update' get_sync_media_env.save(f'{fs.root_path}/test.xlsx') synchronizer = MediaSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Media') mocked_responses.add( method='PUT', url= 'https://localhost/public/v1/products/PRD-276-377-545/media/PRDM-276-377-545-67072', status=404, ) skipped, created, updated, deleted, errors = synchronizer.sync() assert skipped == 0 assert created == 0 assert updated == 0 assert deleted == 0 assert errors == {2: ['404 Not Found']}
def test_validate_invalid_id(fs, get_sync_params_env): get_sync_params_env['Ordering Parameters']['B2'] = 'XKL#' get_sync_params_env['Ordering Parameters']['C2'] = 'update' get_sync_params_env.save(f'{fs.root_path}/test.xlsx') stats = SynchronizerStats() synchronizer = ParamsSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, stats=stats, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Ordering Parameters') synchronizer.sync() assert stats['Ordering Parameters'].get_counts_as_dict() == { 'processed': 1, 'created': 0, 'updated': 0, 'deleted': 0, 'skipped': 0, 'errors': 1, } assert stats['Ordering Parameters']._row_errors == { 2: [ 'Parameter ID must contain only letters, numbers and `_`, provided XKL#' ], }
def test_create_video(fs, get_sync_media_env, mocked_responses, mocked_media_response, domain): get_sync_media_env['Media']['C2'] = 'create' get_sync_media_env['Media']['D2'] = 'video' get_sync_media_env['Media']['E2'] = 'image.png' get_sync_media_env['Media']['F2'] = f'https://{domain}/test' get_sync_media_env.save(f'{fs.root_path}/test.xlsx') synchronizer = MediaSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Media') mocked_responses.add( method='POST', url='https://localhost/public/v1/products/PRD-276-377-545/media', json=mocked_media_response[0], ) skipped, created, updated, deleted, errors = synchronizer.sync() assert skipped == 0 assert created == 1 assert updated == 0 assert deleted == 0 assert errors == {}
def test_invalid_items_sheet(fs, mocked_responses, mocked_product_response): wb = load_workbook('./tests/fixtures/comparation_product.xlsx') ws = wb['Items'] ws['A1'].value = 'Modified' wb.save(f'{fs.root_path}//test.xlsx') synchronizer = ProductSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, ) mocked_responses.add( method='GET', url='https://localhost/public/v1/products/PRD-276-377-545', json=mocked_product_response, ) with pytest.raises(ClickException) as e: synchronizer.open( f'{fs.root_path}/test.xlsx', 'Items', ) assert str(e.value) == 'Invalid input file: column A must be ID'
def test_validate_invalid_action(fs, get_sync_params_env): get_sync_params_env['Ordering Parameters']['C2'] = 'update' get_sync_params_env['Ordering Parameters']['A2'] = None get_sync_params_env.save(f'{fs.root_path}/test.xlsx') stats = SynchronizerStats() synchronizer = ParamsSynchronizer( client=ConnectClient( use_specs=False, api_key='ApiKey SU:123', endpoint='https://localhost/public/v1', ), silent=True, stats=stats, ) synchronizer.open(f'{fs.root_path}/test.xlsx', 'Ordering Parameters') synchronizer.sync() assert stats['Ordering Parameters'].get_counts_as_dict() == { 'processed': 1, 'created': 0, 'updated': 0, 'deleted': 0, 'skipped': 0, 'errors': 1, } assert stats['Ordering Parameters']._row_errors == { 2: ['Verbose ID is required on update and delete actions.'], }
def test_handle_param_inputs_dynamic(mocker, mocked_responses, mocked_product_response): mocked_active_account = mocker.MagicMock() mocked_active_account.is_vendor.return_value = True mocked_config = mocker.MagicMock(active=mocked_active_account) param = { 'id': 'product', 'type': 'product', 'name': 'Product list', 'description': 'Select the products you want to include in report', } client = ConnectClient( api_key='ApiKey X', endpoint='https://localhost/public/v1', use_specs=False, ) mocked_responses.add( url='https://localhost/public/v1/products', method='GET', json=[mocked_product_response], ) result = handle_param_input(mocked_config, client, param) assert result['type'] == 'selectmany' assert len(result['values']) == 1 assert result['values'][0] == ('PRD-276-377-545', 'My Product (PRD-276-377-545)')
def test_get_item_by_mpn( mocked_responses, mocked_items_response, ): mocked_responses.add( method='GET', url='https://localhost/public/v1/products/PRD-276-377-545/items?eq(mpn,' 'MPN-R-001)&limit=1&offset=0', json=[mocked_items_response[0]], status=200, ) client = ConnectClient( api_key='ApiKey SU:123', use_specs=False, endpoint='https://localhost/public/v1', ) item = get_item_by_mpn( client=client, product_id='PRD-276-377-545', mpn='MPN-R-001', ) assert item == mocked_items_response[0]
def test_marketplace_list(mocked_responses): param = { "id": "mkp", "type": "marketplace", "name": "Marketplaces", "description": "Select the marketplaces you want to include in report", } config = Config() config.load('/tmp') config.add_account('VA-000', 'Account 0', 'Api 0', 'https://localhost/public/v1') client = ConnectClient( api_key='ApiKey X', endpoint='https://localhost/public/v1', use_specs=False, ) mocked_responses.add( url='https://localhost/public/v1/marketplaces', method='GET', json=[ { "id": "MKP-1", "name": "Marketplace", }, ], ) result = marketplace_list(config, client, param) assert result['type'] == 'selectmany' assert len(result['values']) == 1 assert result['values'][0] == ('MKP-1', 'Marketplace (MKP-1)')
def test_product_2(mocked_responses, mocked_product_response): param = { "id": "product", "type": "product", "name": "Product list", "description": "Select the products you want to include in report", } config = Config() config.load('/tmp') config.add_account('PA-000', 'Account 0', 'Api 0', 'https://localhost/public/v1') client = ConnectClient( api_key='ApiKey X', endpoint='https://localhost/public/v1', use_specs=False, ) mocked_responses.add( url='https://localhost/public/v1/products', method='GET', json=[mocked_product_response], ) result = product_list(config.active, client, param) assert result['type'] == 'selectmany' assert len(result['values']) == 1 assert result['values'][0] == ('PRD-276-377-545', 'My Product (PRD-276-377-545)')
from connect.client import ConnectClient connect = ConnectClient(project_id="YOUR_PROJECT_ID", api_key="YOUR_PUSH_KEY") event = { 'type': 'cycling', 'distance': 21255, 'caloriesBurned': 455, 'duration': 67, 'Paid': 1, 'user': { 'id': '638396', 'name': 'Bruce' } } events = { "dev-collection": [{ 'type': 'cycling', 'distance': 21255, 'caloriesBurned': 455, 'duration': 67, 'user': { 'id': '638396', 'name': 'Bruce' } }, { 'type': 'swimming', 'distance': 21255, 'caloriesBurned': 455,