def dispatch(self, request): try: logger.info('Start request process / ID request - {}'.format( request.id)) result = self.process_request(request) if not result: logger.info('Method `process_request` did not return result') return params = {} if isinstance(result, ActivationTileResponse): params = {'activation_tile': result.tile} elif isinstance(result, ActivationTemplateResponse): params = {'template_id': result.template_id} self.approve(request.id, params) except FulfillmentInquire as inquire: self.update_parameters(request.id, inquire.params) return self.inquire(request.id) except FulfillmentFail as fail: return self.fail(request.id, reason=fail.message) except Skip as skip: return skip.code return
def __cancel(self, req): logger.info("Cancelling...") tenant_id = req.asset.get_param_by_id("customer_tenant_id").value tenant_suspended = self.__toggle(False, req) self.service.remove(tenant_id, tenant_suspended['version'], {'version': tenant_suspended['version']}) logger.info("Cancelled")
def _upload_spreadsheet(self, usage_file, spreadsheet): # type: (UsageFile, openpyxl.Workbook) -> None # Generate spreadsheet file with NamedTemporaryFile() as tmp: spreadsheet.save(tmp) tmp.seek(0) file_contents = tmp.read() # Setup request url = self._api.get_url(usage_file.id + '/upload/') headers = self._api.headers headers['Accept'] = 'application/json' del headers[ 'Content-Type'] # This must NOT be set for multipart post requests multipart = {'usage_file': ('usage_file.xlsx', file_contents)} logger.info('HTTP Request: {} - {} - {}'.format( url, headers, multipart)) # Post request try: content, status = self._api.post(path=usage_file.id + '/upload/', headers=headers, files=multipart) except requests.RequestException as ex: raise FileCreationError('Error uploading file: {}'.format(ex)) logger.info('HTTP Code: {}'.format(status)) if status != 201: msg = 'Unexpected server response, returned code {}'.format(status) logger.error('{} -- Raw response: {}'.format(msg, content)) raise FileCreationError(msg)
def __toggle(self, enabled, req): logger.info("Toggle") tenant_id = req.asset.get_param_by_id("customer_tenant_id").value version = self.service.get_tenant_info(tenant_id)['version'] result = self.service.edit(tenant_id, { 'enabled': enabled, 'version': version }) return result
def process_request(self, request): # type: (Fulfillment) -> Union[ActivationTemplateResponse, ActivationTileResponse] if request.needs_migration(): # Skip request if it needs migration (migration is performed by an external service) logger.info( 'Skipping request {} because it needs migration.'.format( request.id)) raise SkipRequest() else: logger.info( 'Processing request {} for contract {}, product {}, marketplace {}' .format(request.id, request.contract.id, request.asset.product.name, request.marketplace.name)) # Custom logic if request.type == 'purchase': for item in request.asset.items: if item.quantity > 100000: raise FailRequest( 'Is not possible to purchase product in such quantities' ) for param in request.asset.params: if param.name == 'email' and not param.value: param.value_error = 'Email address has not been provided, ' \ 'please provide one' raise InquireRequest(params=[param]) # Find a param by its id param = request.asset.get_param_by_id('purchase_id') if param: param.value = '...' # We can assign the id given by the external service here self.update_parameters( request.id, [param]) # Update param on the platform else: raise FailRequest( 'The asset is expected to have a "purchase_id" param.') # Approve by Template return ActivationTemplateResponse('TL-497-535-242') # Or # return TemplateResource().get(pk='TEMPLATE_ID') # Approve by ActivationTile # return ActivationTileResponse('\n # Welcome to Fallball!\n\nYes, you decided ' # 'to have an account in our amazing service!') # Or # return TemplateResource().render(pk='TEMPLATE_ID', request_id=request.id) elif request.type == 'change': # Fail raise FailRequest() else: # Skip request raise SkipRequest()
def __purchase(self, req): logger.info("PURCHASE") setattr(self, 'tenant', None) login_name = req.asset.get_param_by_id('customer_admin').value # Check if logged user exists self.service.check_login(login_name) try: # Create customer account logger.info("Creating tenant...") tenant_created = self.service.provision_customer( req.id, self.get_tier_config(req.asset.tiers.tier1.id, self.config.products[0]).get_param_by_id( 'tenant_id').value) logger.debug(tenant_created) setattr(self, 'tenant', tenant_created['id']) # Set offering based in sku logger.info("Setting offerings...") self.service.set_customer_offerings( tenant_created['id'], self.get_tier_config(req.asset.tiers.tier1.id, self.config.products[0]).get_param_by_id( 'tenant_id').value, [ Utils.sku_to_offering_item(item) for item in req.asset.items if item.quantity > 0 ]) # Create admin for user account logger.info("Creating admin...") admin_created = self.service.create_admin(tenant_created, login_name) logger.debug(admin_created) # Create access policies for created user self.service.access_policies(admin_created, tenant_created) # Send activation email self.service.send_email(login_name, admin_created) params = [ Param(id='customer_tenant_id', value=str(tenant_created['id'])), ] self.update_parameters(req.id, params) logger.info("PURCHASE END") except Exception as err: if self.tenant: logger.error("Rollback tenant creation") suspended = self.service.edit(self.tenant, { 'enabled': False, 'version': 1 }) self.service.remove(self.tenant, suspended['version'], {'version': suspended['version']}) raise FailRequest(err)
def __change(self, req): logger.info("Changing...") changed = [ Utils.sku_to_offering_item(item) for item in req.asset.items if item.quantity > 1 and item.quantity != 0 ] logger.debug(changed) self.service.set_customer_offerings( req.asset.get_param_by_id("customer_tenant_id").value, self.get_tier_config( req.asset.tiers.tier1.id, self.config.products[0]).get_param_by_id('tenant_id').value, changed, True)
def process_request(self, request): if (request.type == 'purchase'): for param in request.asset.params: if (param.name == 'tenantId'): tenant_param_id = param.value url = VENDOR_API_URL + 'tenant/' + request.id response = requests.get(url, data='').json() if (tenant_param_id != '' and response['status'] == 'ready'): self.approve_request(request) else: logger.info('Skip process') else: logger.info('This processor not handle this type of request') return False
def create_request(self, request): for item in request.asset.items: mpn = item.mpn quantity = item.quantity break url = VENDOR_API_URL + "tenant?externalId=" + mpn response = requests.get(url, data='').json() first_name = request.asset.tiers.customer.contact_info.contact.first_name last_name = request.asset.tiers.customer.contact_info.contact.last_name address = request.asset.tiers.customer.contact_info.address_line1 postal_code = request.asset.tiers.customer.contact_info.postal_code country = request.asset.tiers.customer.contact_info.country email = request.asset.tiers.customer.contact_info.contact.email account_phone = request.asset.tiers.customer.contact_info.contact.phone_number.phone_number if response['externalId'] != request.asset.id: url = VENDOR_API_URL + 'tenant' payload = { 'Attributes': { 'product': { 'item': mpn, 'quantity': quantity }, 'account': { 'accountFirstName': first_name, 'accountLastName': last_name, 'accountCompany': request.asset.tiers.customer.name, 'accountAddress': address, 'accountCity': request.asset.tiers.customer.contact_info.city, 'accountState': request.asset.tiers.customer.contact_info.state, 'accountPostalCode': postal_code, 'accountCountry': country, 'accountEmail': email, 'accountPhone': account_phone } } } response = requests.post(url, data=payload).json() if (response['tenantId'] != ''): data = { "params": [{ "id": "tenantId", "value": response['tenantId'] }] } self._fulfillment.update_param_asset_request(request.id, data, 'vendor system Id') return response else: logger.info('Error in Vendor System') return False
def process_request(self, request): # type: (Fulfillment) -> Union[ActivationTemplateResponse, ActivationTileResponse] if request.needs_migration(): # Skip request if it needs migration (migration is performed by an external service) logger.info( 'Skipping request {} because it needs migration.'.format( request.id)) raise SkipRequest() else: logger.info( 'Processing request {} for contract {}, product {}, marketplace {}' .format(request.id, request.contract.id, request.asset.product.name, request.marketplace.name)) # Custom logic if request.type == 'purchase': for item in request.asset.items: if item.quantity > 100000: raise FailRequest( message='Is Not possible to purchase product') for param in request.asset.params: if param.name == 'email' and not param.value: param.value_error = 'Email address has not been provided, ' \ 'please provide one' raise InquireRequest(params=[param]) # Approve by ActivationTile return ActivationTileResponse( '\n # Welcome to Fallball!\n\nYes, you decided ' 'to have an account in our amazing service!') # Or # return TemplateResource().render(pk='TEMPLATE_ID', request_id=request.id) # Approve by Template # return ActivationTemplateResponse('TL-497-535-242') # Or # return TemplateResource().get(pk='TEMPLATE_ID') elif request.type == 'change': # Fail raise FailRequest() else: # Skip request raise SkipRequest()
def dispatch(self, request): # type: (TierConfigRequest) -> str try: if self.config.products \ and request.configuration.product.id not in self.config.products: return 'Invalid product' logger.info( 'Start tier config request process / ID request - {}'.format( request.id)) result = self.process_request(request) if not result: logger.info('Method `process_request` did not return result') return '' params = {} if isinstance(result, ActivationTileResponse): params = {'template': {'representation': result.tile}} elif isinstance(result, ActivationTemplateResponse): params = {'template': {'id': result.template_id}} self.approve(request.id, params) except InquireRequest as inquire: self.update_parameters(request.id, inquire.params) return self.inquire(request.id) except FailRequest as fail: return self.fail(request.id, reason=str(fail)) except SkipRequest as skip: return skip.code except NotImplementedError: raise except Exception as ex: logger.warning( 'Skipping request {} because an exception was raised: {}'. format(request.id, ex)) return '' return ''
def dispatch(self, request): # type: (UsageListing) -> str # TODO Shouldn't this raise an exception on ALL automation classes? if self.config.products \ and request.product.id not in self.config.products: return 'Listing not handled by this processor' logger.info('Processing Usage for Product {} ({}) '.format( request.product.id, request.product.name) + 'on Contract {} '.format(request.contract.id) + 'and provider {}({})'.format(request.provider.id, request.provider.name)) try: result = self.process_request(request) except FileCreationError: logger.info('Error processing Usage for Product {} ({}) '.format( request.product.id, request.product.name) + 'on Contract {} '.format(request.contract.id) + 'and provider {}({})'.format(request.provider.id, request.provider.name)) return 'failure' logger.info('Processing result for usage on listing {}: {}'.format( request.product.id, result)) return 'success'
def dispatch(self, request): # type: (UsageFile) -> str try: # Validate product if self.config.products \ and request.product.id not in self.config.products: return 'Invalid product' # Process request logger.info( 'Start usage file request process / ID request - {}'.format( request.id)) result = self.process_request(request) # Report that expected exception was not raised processing_result = 'UsageFileAutomation.process_request returned {} while ' \ 'is expected to raise UsageFileAction or SkipRequest exception' \ .format(str(result)) logger.warning(processing_result) raise UserWarning(processing_result) # Catch action except UsageFileAction as usage: self._api.post( path='{}/{}'.format(request.id, usage.code), json=usage.obj.json if isinstance(usage.obj, BaseModel) else getattr(usage.obj, '__dict__', str(usage.obj))) processing_result = usage.code # Catch skip except SkipRequest: processing_result = 'skip' logger.info( 'Finished processing of usage file with ID {} with result {}'. format(request.id, processing_result)) return processing_result
def process_request(self, request): if (request.type == 'purchase'): if (len(request.asset.items) == 1): tenant_param_id = '' for param in request.asset.params: if (param.name == 'tenantId'): tenant_param_id = param.value if (tenant_param_id == ''): self.create_request(request) else: logger.info('Skip process') else: logger.info('Request malformed, too many items') else: logger.info('This processor not handle this type of request') return False
def process_request(self, req: Fulfillment) -> object: try: logger.info("Processing request " + req.id) partner_config = Utils.get_partner_data(req) setattr(self, 'service', Service(partner_config, req.asset.tiers.customer)) switcher = { "purchase": lambda: __class__.__purchase(self, req), "cancel": lambda: __class__.__cancel(self, req), "change": lambda: __class__.__change(self, req), "suspend": lambda: __class__.__toggle(self, False, req), "resume": lambda: __class__.__toggle(self, True, req) } switcher.get(req.type, "ERROR: Action type is no valid")() if req.type in ['purchase', 'change', 'resume']: result = ActivationTemplateResponse( partner_config['templates']['activation_template']) else: result = ActivationTileResponse('Operation ' + req.type + ' done successfully') logger.info("Finishing request " + req.id) return result except SkipRequest as err: logger.info(err.message) raise SkipRequest(str(err)) except FailRequest as err: logger.error( "Issue while processing Purchase request. Print Error: %s" % str(err)) raise err except InquireRequest as err: logger.error( "Issue while processing Purchase request. Print Error: %s" % str(err)) raise err except Exception as err: logger.error( "Issue while processing Purchase request. Print Error: %s" % str(err)) raise err
def list(self, filters=None): # type: (Dict[str, Any]) -> List[Any] filters = filters or self.filters() logger.info('Get list request with filters - {}'.format(filters)) response, _ = self._api.get(params=filters) return self.model_class.deserialize(response)
def process_request(self, req): try: setattr(self, 'tenant', None) if req.type == 'update': raise FailRequest("Update operations are not allowed") logger.info("Begin tier config process:" + req.configuration.id) logger.debug(req) partner_config = Utils.get_partner_data( {'connection': req.configuration.connection.id, 'tier_1': req.configuration.account.external_id, 'tier_2': 'default', 'req_id': req.id}) logger.info( "Validate if req requires Skip according accountExternalIdsConfigRequestSkipPrefix config parameter") if req.configuration.account.external_id.startswith( tuple(partner_config['accountExternalIdsConfigRequestSkipPrefix'])): raise SkipRequest('Skipped by processor configuration') setattr(self, 'service', Service(partner_config, req.configuration.account)) # Check if current logged user exists self.service.check_login(req.get_param_by_id('admin_login_name').value) subs_start = str(time.time()) internal_tag = 'aps-abc-start-prod-date=%s;aps-abc-tier=%s;aps-abc-billing-model=%s' % ( subs_start, '-1', 'per_gb') # Create tenant subscription logger.info("Creating tenant...") tenant_created = self.service.create_tenant(req.id, internal_tag) logger.debug(tenant_created) setattr(self, 'tenant', tenant_created['id']) # Create admin for subscription logger.info("Creating admin...") admin_created = self.service.create_admin(tenant_created, req.get_param_by_id('admin_login_name').value, True) logger.debug(admin_created) # Add access policies for the user logger.info("Adding access policies...") self.service.access_policies(admin_created, tenant_created, True) # Set offerings available for reseller customers logger.info("Set offerings...") self.service.set_offerings(tenant_created) # Send activation email logger.info("Sending email...") self.service.send_email(req.get_param_by_id('admin_login_name').value, admin_created) params = [ Param(id='tenant_id', value=str(tenant_created['id'])), Param(id='admin_id', value=str(admin_created['id'])), Param(id='reseller_customer_id', value=str(internal_tag)), Param(id='billing_date', value=str(subs_start)) ] logger.info("End tier configuration for " + req.configuration.id) self.update_parameters(req.id, params) return ActivationTemplateResponse(partner_config['templates']['t1_template']) except FailRequest as err: self.__rollback() logger.error("Issue while processing Purchase request. Print Error: %s" % err) raise err except SkipRequest as err: logger.info(err) raise err except InquireRequest as err: self.__rollback() if self.tenant: self.service.remove(self.tenant) logger.error("Issue while processing Purchase request. Print Error: %s" % err) raise err except Exception as err: self.__rollback() if self.tenant: self.service.remove(self.tenant) logger.error("Issue while processing Purchase request. Print Error: %s" % err) raise err
def migrate(self, request): """ Call this function to perform migration of one request. :param Fulfillment request: The request to migrate. :return: A new request object with the parameter values updated. :rtype: Fulfillment :raises SkipRequest: Raised if migration fails for some reason. :raises MigrationParamError: Raised if the value for a parameter is not a string. """ if request.needs_migration(self.migration_key): logger.info( '[MIGRATION::{}] Running migration operations for request {}'. format(request.id, request.id)) request_copy = copy.deepcopy(request) raw_data = request.asset.get_param_by_id(self.migration_key).value logger.debug('[MIGRATION::{}] Migration data `{}`: {}'.format( request.id, self.migration_key, raw_data)) try: try: parsed_data = json.loads(raw_data) except ValueError as ex: raise MigrationAbortError(str(ex)) logger.debug( '[MIGRATION::{}] Migration data `{}` parsed correctly'. format(request.id, self.migration_key)) # These will keep track of processing status processed_params = [] succeeded_params = [] failed_params = [] skipped_params = [] # Exclude param for migration_info from process list params = [ param for param in request_copy.asset.params if param.id != self.migration_key ] for param in params: # Try to process the param and report success or failure try: if param.id in self.transformations: # Transformation is defined, so apply it logger.info( '[MIGRATION::{}] Running transformation for parameter {}' .format(request.id, param.id)) param.value = self.transformations[param.id]( parsed_data, request.id) succeeded_params.append(param.id) elif param.id in parsed_data: # Parsed data contains the key, so assign it if not isinstance(parsed_data[param.id], six.string_types): if self.serialize: parsed_data[param.id] = json.dumps( parsed_data[param.id]) else: type_name = type( parsed_data[param.id]).__name__ raise MigrationParamError( 'Parameter {} type must be str, but {} was given' .format(param.id, type_name)) param.value = parsed_data[param.id] succeeded_params.append(param.id) else: skipped_params.append(param.id) except MigrationParamError as ex: logger.error('[MIGRATION::{}] {}'.format( request.id, ex)) failed_params.append(param.id) # Report processed param processed_params.append(param.id) logger.info( '[MIGRATION::{}] {} processed, {} succeeded{}, {} failed{}, ' '{} skipped{}.'.format( request.id, len(processed_params), len(succeeded_params), self._format_params(succeeded_params), len(failed_params), self._format_params(failed_params), len(skipped_params), self._format_params(skipped_params))) # Raise abort if any params failed if failed_params: raise MigrationAbortError( 'Processing of parameters {} failed, unable to complete migration.' .format(', '.join(failed_params))) except MigrationAbortError as ex: logger.error('[MIGRATION::{}] {}'.format(request.id, ex)) raise SkipRequest('Migration failed.') return request_copy else: logger.info( '[MIGRATION::{}] Request does not need migration.'.format( request.id)) return request
def list(self): filters = self.build_filter() logger.info('Get list request by filter - {}'.format(filters)) response = self.api.get(url=self._list_url, params=filters) return self.__loads_scheme(response)
def dispatch(self, request): # type: (Fulfillment) -> str conversation = request.get_conversation(self.config) try: if self.config.products \ and request.asset.product.id not in self.config.products: return 'Invalid product' logger.info('Start request process / ID request - {}'.format( request.id)) process_result = self.process_request(request) if not process_result: logger.info('Method `process_request` did not return result') return '' if isinstance(process_result, ActivationTileResponse): message = 'Activated using custom activation tile.' approved = self.approve( request.id, {'activation_tile': process_result.tile}) elif isinstance(process_result, ActivationTemplateResponse): message = 'Activated using template {}.'.format( process_result.template_id) approved = self.approve( request.id, {'template_id': process_result.template_id}) else: # We should not get here message = '' approved = '' self._update_conversation_if_exists(conversation, request.id, message) return approved except InquireRequest as inquire: self.update_parameters(request.id, inquire.params) inquired = self.inquire(request.id) self._update_conversation_if_exists(conversation, request.id, inquire) return inquired except FailRequest as fail: # PyCharm incorrectly detects unreachable code here, so disable # noinspection PyUnreachableCode failed = self.fail(request.id, reason=str(fail)) self._update_conversation_if_exists(conversation, request.id, fail) return failed except SkipRequest as skip: self._update_conversation_if_exists(conversation, request.id, skip) return skip.code except NotImplementedError: raise except Exception as ex: logger.warning( 'Skipping request {} because an exception was raised: {}'. format(request.id, ex)) return ''