Exemple #1
0
 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 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)
Exemple #3
0
 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)
Exemple #4
0
 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)
Exemple #5
0
    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 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)
Exemple #7
0
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)
Exemple #8
0
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
Exemple #10
0
    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()