def it_creates_an_event_without_metadata(self): data = { 'event_name': 'sale of item', 'email': '*****@*****.**', } with patch.object(Intercom, 'post', return_value=data) as mock_method: Event.create(**data) mock_method.assert_called_once_with('/events/', **data)
def test(self): User.create(email='*****@*****.**') # no exception here as empty response expected data = { 'event_name': 'Eventful 1', 'created_at': int(time.time()), 'email': '*****@*****.**' } Event.create(**data)
def it_creates_an_event_with_metadata(self): data = { 'event_name': 'Eventful 1', 'created_at': self.created_time, 'email': '*****@*****.**', 'metadata': { 'invitee_email': '*****@*****.**', 'invite_code': 'ADDAFRIEND', 'found_date': 12909364407 } } with patch.object(Intercom, 'post', return_value=data) as mock_method: Event.create(**data) mock_method.assert_called_once_with('/events/', **data)
def add_event(customer_id, event, data=None, order_id=None): customer = Customer.objects.select_related('user').get(id=customer_id) if data is None: data = {} if order_id: data.update(_get_order_data(order_id=order_id)) IntercomLocal.objects.create(customer=customer, event=event, data=data) try: IntercomUser.find(email=customer.user.email) except ResourceNotFound as e: create_intercom_profile(email=customer.user.email) logger.error('add_event [ResourceNotFound]: %s' % e) IntercomEvent.create( event_name=event, email=customer.user.email, created_at=int(time.time()), # UTC metadata=data)
def create_event(event_name, username, metadata=None): if metadata is None: metadata = {} Intercom.app_id = settings.INTERCOM_APP_ID Intercom.app_api_key = settings.INTERCOM_API_KEY try: Event.create(event_name=event_name, created_at=int(time.time()), user_id=username, metadata=metadata) except (ResourceNotFound, UnexpectedError) as e: raise create_event.retry(exc=e, countdown=10, max_retries=None) except RateLimitExceeded as e: # pragma: no cover # We don't care too much about events getting spanned. They at least # don't affect the customer in a highly noticeable way. So if we've # exceeded our rate limit we can delay # But we def don't want to continue bashing their API if we've # hit our limit. Intercom may then reduce our overall limit as # highlighted here # https://developers.intercom.io/reference#rate-limiting # Not covering because Intercom does not have a good way of # simulating these conditions as of 06/04/2016 - Devon Bleibtrey raise create_email.retry(exc=e, countdown=86400, max_retries=None) except AuthenticationError as e: # pragma: no cover # AuthenticationError can be caused by a user selecting # Unsubscribe from email. So we only retry this a limited amount of # times before giving up. # Not covering because Intercom does not have a good way of # simulating these conditions as of 06/04/2016 - Devon Bleibtrey raise create_email.retry(exc=e, countdown=3600, max_retries=10) except (ServerError, ServiceUnavailableError, BadGatewayError, HttpError) as e: # pragma: no cover # FYI AuthenticationError can be caused by a user selecting # Unsubscribe from email. So This may not be the best place to catch # it. # Not covering because Intercom does not have a good way of # simulating these conditions as of 04/16/2016 - Devon Bleibtrey raise create_event.retry(exc=e, countdown=43200, max_retries=None)
def create(self, validated_data): request = self.context.get('request') response_dict = { "id": validated_data['id'], "type": validated_data['type'], 'data': validated_data['data'] } Intercom.app_id = settings.INTERCOM_APP_ID Intercom.app_api_key = settings.INTERCOM_API_KEY stripe.api_key = settings.STRIPE_SECRET_KEY stripe.api_version = settings.STRIPE_API_VERSION event_type = validated_data['type'] if event_type == "invoice.payment_failed": try: customer = stripe.Customer.retrieve( validated_data['data']['object']['customer']) except stripe.InvalidRequestError: raise serializers.ValidationError("Ran into some issues") try: pleb = Pleb.nodes.get(email=customer['email']) except (DoesNotExist, Pleb.DoesNotExist): return response_dict message_data = { 'message_type': 'email', 'subject': 'Subscription Failure Notice', 'body': "Hi {{ first_name }},\nIt looks like we ran into " "some trouble processing your subscription payment. " "Please verify your billing information is correct " "and we'll automatically retry to process the payment. " "If the payment continues to fail we'll automatically " "move you over to a free account. If you believe " "there has been a mistake please respond to this " "email and we'd be happy to help!\n\nBest Regards," "\n\nDevon", 'template': 'personal', 'from_user': { 'type': 'admin', 'id': settings.INTERCOM_ADMIN_ID_DEVON }, 'to_user': { 'type': 'user', 'user_id': pleb.username } } serializer = IntercomMessageSerializer(data=message_data) serializer.is_valid(raise_exception=True) serializer.save() response_dict["detail"] = "Invoice Payment Failed" return response_dict if event_type == "account.updated": try: account = stripe.Account.retrieve( validated_data['data']['object']['id']) except (stripe.InvalidRequestError, stripe.APIConnectionError) as e: logger.exception(e) raise serializers.ValidationError(e) except stripe.AuthenticationError: # Just return silently here so that Stripe stops sending us the # unauthenticatable user return response_dict if account.get('deleted', False): return response_dict try: pleb = Pleb.nodes.get(email=account.email) except (DoesNotExist, Pleb.DoesNotExist): return response_dict quest = Quest.nodes.get(owner_username=pleb.username) if quest.account_verified != "verified" \ and account.legal_entity.verification.status == "verified": # Need this to ensure the notification is only sent once if # Stripe sends a subsequent message before we've saved the # verification off. query = 'MATCH (a:Notification {action_name: ' \ '"Your Quest has been verified!"})' \ '-[:NOTIFICATION_TO]->(pleb:Pleb {username: "******"}) ' \ 'RETURN a' % pleb.username res, _ = db.cypher_query(query) if res.one is None: create_system_notification( to_plebs=[pleb], notification_id=str(uuid1()), url=reverse('quest_manage_banking', kwargs={'username': pleb.username}), action_name="Your Quest has been verified!") quest_ser = QuestSerializer(instance=quest, data={'active': True}, context={ "secret": settings.SECRET_KEY, "request": request }) quest_ser.is_valid(raise_exception=True) quest_ser.save() cache.delete("%s_quest" % quest.owner_username) # Update quest after saving from serializer so we're not working # with a stale instance. quest = Quest.nodes.get(owner_username=pleb.username) # Need this because the serializer doesn't get into the area # where verification is updated unless a new external account # is added. quest.account_verified = \ account.legal_entity.verification.status quest.account_verified_date = datetime.now(pytz.utc) # When a quest is verified it automatically becomes active # If a user wants to take their quest inactive they have the # option but to reactivate they have to reach out to us and # explain because we don't want people just going active, # inactive and screwing with donors. quest.active = True message_data = { 'message_type': 'email', 'subject': 'Quest Verified', 'body': "Hi Team,\n%s's Quest has been verified by Stripe. " "Please review it in the <a href='%s'>" "council area</a>." % (quest.owner_username, reverse('council_missions', request=request)), 'template': "personal", 'from_user': { 'type': "admin", 'id': settings.INTERCOM_ADMIN_ID_DEVON }, 'to_user': { 'type': "user", 'user_id': settings.INTERCOM_USER_ID_DEVON } } serializer = IntercomMessageSerializer(data=message_data) if serializer.is_valid(): serializer.save() if account.verification.fields_needed: quest.account_verification_fields_needed = \ account.verification.fields_needed quest.account_verified = \ account.legal_entity.verification.status quest.account_verification_details = \ str(account.legal_entity.verification.details) if quest.account_first_updated is None: quest.account_first_updated = datetime.now(pytz.utc) verify = account.verification # Determine if we need Quest to upload identification documentation if 'legal_entity.verification.document' in verify.fields_needed: quest.verification_document_needed = True # Save off when the additional information is due by if hasattr(verify, 'due_by') and verify.due_by is not None: quest.verification_due_date = datetime.fromtimestamp( account.verification.due_by) else: quest.verification_due_date = None # Indicate why the account was disabled by the payment processing # platform if verify.disabled_reason is not None: quest.verification_disabled_reason = \ account.verification.disabled_reason\ .replace('_', " ").title() else: quest.verification_disabled_reason = None quest.save() cache.delete("%s_quest" % quest.owner_username) response_dict["detail"] = "Account Updated" return response_dict if event_type == "transfer.failed": try: transfer = stripe.Transfer.retrieve( validated_data['data']['object']['id']) if transfer.type == 'stripe_account': account = stripe.Account.retrieve(transfer.destination) if account.get('deleted', False): return response_dict else: raise serializers.ValidationError('An error occurred') except (stripe.InvalidRequestError, stripe.APIConnectionError) as e: logger.exception(e) raise serializers.ValidationError(e) except stripe.AuthenticationError: # Just return silently here so that Stripe stops sending us the # unauthenticatable user return response_dict try: pleb = Pleb.nodes.get(email=account.email) except (DoesNotExist, Pleb.DoesNotExist): return response_dict create_system_notification( to_plebs=[pleb], notification_id=str(uuid1()), url=reverse('quest_manage_banking', kwargs={'username': pleb.username}), action_name="A transfer to your bank account has " "failed! Please review that your " "Quest Banking information is correct.") Event.create(event_name="stripe-transfer-failed", user_id=pleb.username, created=calendar.timegm( datetime.now(pytz.utc).utctimetuple())) response_dict["detail"] = "Transfer Failed" return response_dict if event_type == 'customer.subscription.trial_will_end': try: customer = stripe.Customer.retrieve( validated_data['data']['object']['customer']) except (stripe.InvalidRequestError, stripe.APIConnectionError) as e: logger.exception(e) raise serializers.ValidationError(e) try: pleb = Pleb.nodes.get(email=customer.email) except (DoesNotExist, Pleb.DoesNotExist): return response_dict create_system_notification( to_plebs=[pleb], notification_id=str(uuid1()), url=reverse('quest_manage_billing', kwargs={'username': pleb.username}), action_name="Your Pro Trial will " "be ending soon, add a payment method to " "keep your Pro Account features.") response_dict["detail"] = "Trail Will End" return response_dict response_dict["detail"] = "No updates" return response_dict
def deploy(self): instance = self._get_pusher_instance() cio = CustomerIO(settings.CUSTOMERIO_SITE_ID, settings.CUSTOMERIO_API_KEY) cio.identify(id=self.email, email=self.email) instance[self.deploy_id].trigger('info_update', { 'message': "Creating a new container...", 'percent': 30 }) headers = { 'content-type': 'application/json', } # run the container ports = self.project.ports.split(' ') hostnames = self.project.hostnames.split(' ') payload = { "image": self.project.image_name, "hosts": ["/api/v1/hosts/1/"], "ports": ports, "command": "", "links": "", "memory": "", "environment": self.project.env_vars, } if "edx" in self.project.name.lower(): edx_env = [] edx_env.append("EDX_LMS_BASE=lms-{0}.demo.appsembler.com".format(self.deploy_id)) edx_env.append("EDX_PREVIEW_LMS_BASE=lms-{0}.demo.appsembler.com".format(self.deploy_id)) edx_env.append("EDX_CMS_BASE=cms-{0}.demo.appsembler.com".format(self.deploy_id)) edx_env.append("INTERCOM_APP_ID={0}".format(settings.INTERCOM_EDX_APP_ID)) edx_env.append("INTERCOM_APP_SECRET={0}".format(settings.INTERCOM_EDX_APP_SECRET)) edx_env.append("INTERCOM_USER_EMAIL={0}".format(self.email)) env_string = " ".join(edx_env) env_string = " " + env_string payload['environment'] += env_string r = requests.post( "{0}/api/v1/containers/?username={1}&api_key={2}".format(settings.SHIPYARD_HOST, settings.SHIPYARD_USER, settings.SHIPYARD_KEY), data=json.dumps(payload), headers=headers ) if r.status_code == 201: # This sleep is needed to avoid problems with the API time.sleep(3) container_uri = urlparse(r.headers['location']).path self.remote_container_id = container_uri.split('/')[-2] # create the app (for dynamic routing) instance[self.deploy_id].trigger('info_update', { 'message': "Assigning an URL to the app...", 'percent': 60 }) time.sleep(2) app_ids = [] domains = [] for port, hostname in zip(ports, hostnames): domain_name = "{0}.demo.appsembler.com".format(self.deploy_id) if hostname: domain_name = "{0}-{1}".format(hostname, domain_name) domains.append(domain_name) payload = { "name": self.deploy_id, "description": "{0} for {1}".format(self.project.name, self.email), "domain_name": domain_name, "backend_port": port, "protocol": "https" if "443" in self.project.ports else "http", "containers": [container_uri] } r = requests.post( "{0}/api/v1/applications/?username={1}&api_key={2}".format(settings.SHIPYARD_HOST, settings.SHIPYARD_USER, settings.SHIPYARD_KEY), data=json.dumps(payload), headers=headers ) if r.status_code == 201: app_uri = urlparse(r.headers['location']).path app_ids.append(app_uri.split('/')[-2]) time.sleep(2) self.remote_app_id = " ".join(app_ids) status = r.status_code time.sleep(1) instance[self.deploy_id].trigger('info_update', { 'message': "Getting information...", 'percent': 90 }) time.sleep(1) if status == 201: scheme = "https" if "443" in self.project.ports else "http" app_urls = [] for domain in domains: app_url = "{0}://{1}".format(scheme, domain) app_urls.append(app_url) self.url = " ".join(app_urls) self.status = 'Completed' self.launch_time = timezone.now() self.expiration_time = self.expiration_datetime() instance[self.deploy_id].trigger('deployment_complete', { 'app_name': self.project.name, 'message': "Deployment complete!", 'app_url': self.url, 'username': self.project.default_username, 'password': self.project.default_password }) Event.create( event_name="deployed_app", email=self.email, metadata={ 'app_name': self.project.name, 'app_url': self.url, 'deploy_id': self.deploy_id, } ) if self.email: cio.track(customer_id=self.email, name='app_deploy_complete', app_url=self.url.replace(" ", "\n"), app_name=self.project.name, status_url="http://launcher.appsembler.com" + reverse('deployment_detail', kwargs={'deploy_id': self.deploy_id}), trial_duration=self.project.trial_duration, username=self.project.default_username, password=self.project.default_password ) else: self.status = 'Failed' error_log = DeploymentErrorLog(deployment=self, http_status=status, error_log=r.text) error_log.save() send_mail( "Deployment failed: {0}".format(self.deploy_id), "Error log link: {0}".format(reverse('admin:deployment_deploymenterrorlog_change', args=(error_log.id,))), '*****@*****.**', ['*****@*****.**', '*****@*****.**'] ) instance[self.deploy_id].trigger('deployment_failed', { 'message': "Deployment failed!", }) self.save()