class BaseCampaigns(Resource): """ This resource creates a base-campaign in database table base-campaign. """ # Access token decorator decorators = [require_oauth()] def post(self): """ This creates a base campaign with following payload { "name": "Jobs at getTalent "Description": "We are looking for Python developers } """ user = request.user data = request.get_json(silent=True) if not data: raise InvalidUsage("Received empty request body") name = data.get('name', '') description = data.get('description', '') if not name or not description: raise InvalidUsage('Name and description are required fields') base_campaign = BaseCampaign(user_id=user.id, name=name, description=description) base_campaign.save() return {'id': base_campaign.id}, codes.CREATED
class TestEmailResource(Resource): """ This resource is to send test email to preview the email before sending the actual email campaign to candidates. """ # Access token decorator decorators = [require_oauth()] def post(self): """ POST /v1/test-email Send email campaign with data (subject, from, body_html, emails) Sample POST Data: { "subject": "Test Email", "from": "Zohaib Ijaz", "body_html": "<html><body><h1>Welcome to email campaign service <a href=https://www.github.com>Github</a></h1></body></html>", "emails": ["*****@*****.**", "*****@*****.**"], "reply_to": "*****@*****.**" } """ user = request.user send_test_email(user, request) return { 'message': 'test email has been sent to given emails' }, codes.OK
class EmailCampaignBlastById(Resource): """ Endpoint looks like /v1/email-campaigns/:id/blasts/:id. This resource returns a blast object for given blast_id associated with given campaign. """ decorators = [require_oauth()] def get(self, campaign_id, blast_id): """ This endpoint returns a blast object for a given campaign_id and blast_id. From that blast object we can extract sends, clicks etc. :param campaign_id: int, unique id of a email campaign :param blast_id: id of blast object :type campaign_id: int | long :type blast_id: int | long :return: JSON data containing dict of blast object :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> campaign_id = 1 >>> blast_id = 1 >>> response = requests.get(EmailCampaignApiUrl.BLAST % (campaign_id, blast_id), >>> headers=headers) .. Response:: { "blast": { "updated_datetime": "2016-02-10 19:37:15", "sends": 1, "bounces": 0, "campaign_id": 1, "text_clicks": 0, "html_clicks": 0, "complaints": 0, "id": "1", "opens": 0, "sent_datetime": "2016-02-10 19:37:04" } } .. Status:: 200 (OK) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Requested campaign does not belong to user's domain OR requested blast object is not associated with given campaign_id) 404 (Campaign not found OR blast_obj with given id not found) 500 (Internal server error) """ raise_if_dict_values_are_not_int_or_long( dict(campaign_id=campaign_id, blast_id=blast_id)) # Get valid blast object blast_obj = EmailCampaignBase.get_valid_blast_obj( campaign_id, blast_id, request.user) return dict(blast=blast_obj.to_json()), codes.OK
class EmailCampaignSendApi(Resource): """ This endpoint looks like /v1/email-campaigns/:id/send """ decorators = [require_oauth()] def post(self, campaign_id): """ Sends campaign emails to the candidates present in smartlists of campaign. Scheduler service will call this to send emails to candidates. :param int|long campaign_id: Campaign id """ raise_if_dict_values_are_not_int_or_long(dict(campaign_id=campaign_id)) email_campaign = EmailCampaignBase.get_campaign_if_domain_is_valid( campaign_id, request.user) if email_campaign.is_hidden: logger.info( "Email campaign(id:%s) is archived, it cannot be sent." % campaign_id) # Unschedule task from scheduler_service if email_campaign.scheduler_task_id: headers = {'Authorization': request.oauth_token} # campaign was scheduled, remove task from scheduler_service if CampaignUtils.delete_scheduled_task( email_campaign.scheduler_task_id, headers): email_campaign.update( scheduler_task_id='') # Delete scheduler task id raise ResourceNotFound("Email campaign(id:%s) has been deleted." % campaign_id, error_code=EMAIL_CAMPAIGN_NOT_FOUND[1]) email_client_id = email_campaign.email_client_id results_send = send_email_campaign(request.user, email_campaign, new_candidates_only=False) if email_client_id: if not isinstance(results_send, list): raise InternalServerError( error_message="Something went wrong, response is not list") data = { 'email_campaign_sends': [{ 'email_campaign_id': email_campaign.id, 'new_html': new_email_html_or_text.get('new_html'), 'new_text': new_email_html_or_text.get('new_text'), 'candidate_email_address': new_email_html_or_text.get('email') } for new_email_html_or_text in results_send] } return jsonify(data) return dict( message='email_campaign(id:%s) is being sent to candidates.' % campaign_id), codes.OK
class TemplatesInFolder(Resource): """ Endpoint looks like /v1/email-template-folders/:id/email-templates. """ decorators = [validate_domain_id_for_email_templates(), require_oauth()] @require_all_permissions(Permission.PermissionNames.CAN_GET_CAMPAIGNS) def get(self, folder_id): """ GET /v1/email-template-folders/:id/email-templates Required parameters: :param int|long folder_id: ID of of email template :return: template-folder object in dict format, status 200 :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> template_folder_id = 1 >>> response = requests.get(EmailCampaignApiUrl.TEMPLATES_IN_FOLDER % template_folder_id, >>> headers=headers) ..Response:: { "email_templates": [ { "user_id": 1, "name": "My Template", "body_text": "This is the text part of the email", "template_folder_id": 8, "updated_datetime": "2016-09-05 15:11:22", "body_html": "<html><body>Email Body</body></html>", "is_immutable": 1, "type": 0, "id": 41 } ] } .. Status:: 200 (Resource found) 401 (Unauthorized to access getTalent) 403 (Requested email-template-folder does not belong to user's domain) 404 (Requested email-template-folder not found) 500 (Internal server error) """ template_folder = EmailTemplateFolder.get_valid_template_folder( folder_id, request.user.domain_id) return { "email_templates": [ template.to_json() for template in template_folder.user_email_template ] }, codes.OK
class InvitationStatus(Resource): """ This returns invitation status of candidate for given campaign. """ # Access token decorator decorators = [require_oauth()] def get(self, email_campaign_id, candidate_id): """ This returns invitation status of candidate for given campaign. Invitations statuses are as: Delivered: Candidate has received the email-campaign Not-Delivered: Candidate has not received the email-campaign Opened: Candidate has opened the email-campaign Accepted: Candidate RSVP'd YES to the promoted event Rejected: Candidate RSVP'd NO to the promoted event """ raise_if_dict_values_are_not_int_or_long( dict(campaign_id=email_campaign_id, candidate_id=candidate_id)) user = request.user email_campaign_send_id = None invitation_status = INVITATION_STATUSES['Not-Delivered'] email_campaign = EmailCampaignBase.get_campaign_if_domain_is_valid( email_campaign_id, request.user) # Check if candidate has received the email-campaign for send in email_campaign.sends.all(): if candidate_id == send.candidate_id: invitation_status = INVITATION_STATUSES['Delivered'] email_campaign_send_id = send.id break # Check if candidate has opened the email-campaign for activity_type in (Activity.MessageIds.CAMPAIGN_EMAIL_OPEN, Activity.MessageIds.CAMPAIGN_EMAIL_CLICK): url = "{}?type={}&source_id={}&source_table={}".format( ActivityApiUrl.ACTIVITIES, activity_type, email_campaign_send_id, EmailCampaignSend.__tablename__) response = send_request('get', url, request.headers['Authorization']) if response.ok: invitation_status = INVITATION_STATUSES['Opened'] if email_campaign.base_campaign_id: base_campaign = BaseCampaign.search_by_id_in_domain( email_campaign.base_campaign_id, user.domain_id) base_campaign_events = base_campaign.base_campaign_events.all() if base_campaign_events: event = base_campaign_events[0].event rsvp_in_db = RSVP.filter_by_keywords(candidate_id=candidate_id, event_id=event.id) if rsvp_in_db and rsvp_in_db[0].status.lower() == 'yes': invitation_status = INVITATION_STATUSES['Accepted'] elif rsvp_in_db and rsvp_in_db[0].status.lower() == 'no': invitation_status = INVITATION_STATUSES['Rejected'] return dict(invitation_status=invitation_status), codes.OK
class EmailCampaignSendById(Resource): """ Endpoint looks like /v1/email-campaigns/:id/sends/:id. This resource returns a send object for given send_id associated with given campaign. """ decorators = [require_oauth()] def get(self, campaign_id, send_id): """ This endpoint returns a send object for a given campaign_id and send_id. :param campaign_id: int, unique id of a email campaign :param send_id: id of send object :type campaign_id: int | long :type send_id: int | long :return: JSON data containing dict of send object :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> campaign_id = 1 >>> send_id = 1 >>> response = requests.get(EmailCampaignApiUrl.SEND_BY_ID % (campaign_id, send_id), >>> headers=headers) .. Response:: { "send": { "ses_message_id": "", "is_ses_bounce": false, "updated_datetime": "2016-02-29 15:26:08", "ses_request_id": "", "campaign_id": 1, "candidate_id": 11, "is_ses_complaint": false, "id": 114, "sent_datetime": "2016-02-29 15:41:38" } } .. Status:: 200 (OK) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Requested campaign does not belong to user's domain or requested send object is not associated with given campaign id) 404 (Campaign not found or send object with given id not found) 500 (Internal server error) """ # Get valid send object send_obj = get_valid_send_obj(campaign_id, send_id, request.user) return dict(send=send_obj.to_json()), codes.OK
class SingleEmailCampaign(Resource): # Access token decorator decorators = [require_oauth()] def get(self, campaign_id): """ GET /v1/email-campaigns/<id> Fetch EmailCampaign object """ raise_if_dict_values_are_not_int_or_long(dict(campaign_id=campaign_id)) include_fields = request.values['fields'].split( ',') if request.values.get('fields') else None email_campaign = EmailCampaignBase.get_campaign_if_domain_is_valid( campaign_id, request.user) return { "email_campaign": email_campaign.to_dict(include_fields=include_fields) } def patch(self, campaign_id): """ This endpoint updates an existing campaign :param int|long campaign_id: Id of campaign """ raise_if_dict_values_are_not_int_or_long(dict(campaign_id=campaign_id)) # Get and validate request data data = request.get_json(silent=True) if not data: raise InvalidUsage("Received empty request body", error_code=INVALID_REQUEST_BODY[1]) is_hidden = data.get('is_hidden', False) if is_hidden not in (True, False, 1, 0): raise InvalidUsage( "is_hidden field should be a boolean, given: %s" % is_hidden, error_code=INVALID_INPUT[1]) email_campaign = EmailCampaignBase.get_campaign_if_domain_is_valid( campaign_id, request.user) # Unschedule task from scheduler_service if email_campaign.scheduler_task_id: headers = {'Authorization': request.oauth_token} # campaign was scheduled, remove task from scheduler_service if CampaignUtils.delete_scheduled_task( email_campaign.scheduler_task_id, headers): email_campaign.update( scheduler_task_id='') # Delete scheduler task id email_campaign.update(is_hidden=is_hidden) logger.info("Email campaign(id:%s) has been archived successfully" % campaign_id) return dict(message="Email campaign (id: %s) updated successfully" % campaign_id), codes.OK
class EmailClientsWithId(Resource): """ This endpoint looks like /v1/email-clients/:id. We can get an email-client with its id in database table "email_client_credentials". """ # Access token decorator decorators = [require_oauth()] def get(self, email_client_id): """ This will get record from database table email_client_credentials for requested id. .. Response:: { "email_client_credentials": { "user_id": 1, "name": "Gmail", "updated_datetime": "2016-09-28 19:38:55", "id": 69, "port": "587", "host": "smtp.gmail.com", "password": "******", "email": "*****@*****.**" } } .. Status:: 200 (Resource Found) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Unauthorized to access requested resource) 404 (Resource not found) 500 (Internal server error) """ raise_if_dict_values_are_not_int_or_long( dict(email_client_id=email_client_id)) client_in_db = EmailClientCredentials.get_by_id(email_client_id) if not client_in_db: raise ResourceNotFound(EMAIL_CLIENT_NOT_FOUND[0], error_code=EMAIL_CLIENT_NOT_FOUND[1]) if not client_in_db.user.domain_id == request.user.domain_id: raise ForbiddenError(EMAIL_CLIENT_FORBIDDEN[0], error_code=EMAIL_CLIENT_FORBIDDEN[1]) return {'email_client_credentials': client_in_db.to_json()}, codes.OK
class BaseCampaignLinkEvent(Resource): """ This resource links an social-network event with base-campaign in database table base-campaign-event. """ # Access token decorator decorators = [require_oauth()] def post(self, base_campaign_id, event_id): """ This links an event with base-campaign. """ event_in_db = Event.get_by_id(event_id) if not event_in_db or event_in_db.is_deleted_from_vendor or event_in_db.is_hidden: raise ResourceNotFound('Requested event not found in database') is_event_in_domain = Event.get_by_event_id_and_domain_id(event_id, request.user.domain_id) if not is_event_in_domain: raise ForbiddenError('Requested event does not belong to requested user`s domain') validate_base_campaign_id(base_campaign_id, request.user.domain_id) base_campaign_event = BaseCampaignEvent(base_campaign_id=base_campaign_id, event_id=event_id) base_campaign_event.save() return {'id': base_campaign_event.id}, codes.CREATED
class EmailCampaignSends(Resource): """ Endpoint looks like /v1/email-campaigns/:id/sends This resource returns all the sends objects associated with given campaign. """ decorators = [require_oauth()] def get(self, campaign_id): """ This endpoint returns a list of send objects (dict) associated with a specific email campaign. :param campaign_id: int, unique id of a email campaign :type campaign_id: int | long :return: JSON data containing list of send objects and their count :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> campaign_id = 1 >>> response = requests.get(EmailCampaignApiUrl.SENDS % campaign_id, headers=headers) .. Response:: { "sends": [ { "ses_message_id": "", "is_ses_bounce": false, "updated_datetime": "2016-02-29 15:26:08", "ses_request_id": "", "campaign_id": 1, "candidate_id": 11, "is_ses_complaint": false, "id": 114, "sent_datetime": "2016-02-29 15:41:38" }, { "ses_message_id": "", "is_ses_bounce": false, "updated_datetime": "2016-02-29 15:26:08", "ses_request_id": "", "campaign_id": 1, "candidate_id": 1, "is_ses_complaint": false, "id": 115, "sent_datetime": "2016-02-29 15:41:38" } ] } .. Status:: 200 (OK) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Requested campaign does not belong to user's domain) 404 (Campaign not found) 500 (Internal Server Error) """ raise_if_dict_values_are_not_int_or_long(dict(campaign_id=campaign_id)) # Get a campaign that was created by this user campaign = EmailCampaignBase.get_campaign_if_domain_is_valid( campaign_id, request.user) # get paginated response page, per_page = get_pagination_params( request, error_code=INVALID_VALUE_OF_PAGINATION_PARAM[1]) return get_paginated_response('sends', campaign.sends, page, per_page)
class EmailCampaigns(Resource): # Access token decorator decorators = [require_oauth()] def get(self): """ GET /v1/email-campaigns Fetches all EmailCampaign objects from auth user's domain """ user = request.user page, per_page = get_pagination_params( request, error_code=INVALID_VALUE_OF_PAGINATION_PARAM[1]) sort_type = request.args.get('sort_type', 'DESC') search_keyword = request.args.get('search', '') sort_by = request.args.get('sort_by', 'added_datetime') is_hidden = request.args.get('is_hidden', 0) user_id = request.args.get('user_id') # Validation of query parameters if isinstance(user_id, basestring): if not user_id.strip().isdigit() or int(user_id) <= 0: raise InvalidUsage(NOT_NON_ZERO_NUMBER[0].format('`user_id`'), error_code=NOT_NON_ZERO_NUMBER[1]) if request.user.role.name != Role.TALENT_ADMIN \ and User.get_domain_id(user_id) != request.user.domain_id: raise ForbiddenError( "Logged-in user and requested user_id are of different domains", EMAIL_CAMPAIGN_FORBIDDEN[1]) user_id = int(user_id) if not is_number(is_hidden) or int(is_hidden) not in (0, 1): raise InvalidUsage('`is_hidden` can be either 0 or 1', error_code=INVALID_VALUE_OF_QUERY_PARAM[1]) if sort_by not in ('added_datetime', 'name'): raise InvalidUsage('Value of sort_by parameter is not valid', error_code=INVALID_VALUE_OF_QUERY_PARAM[1]) if sort_type not in SORT_TYPES: raise InvalidUsage( 'Value of sort_type parameter is not valid. Valid values are %s' % list(SORT_TYPES), error_code=INVALID_VALUE_OF_QUERY_PARAM[1]) # Get all email campaigns from logged in user's domain query = EmailCampaign.get_by_domain_id_and_filter_by_name( user.domain_id, search_keyword, sort_by, sort_type, int(is_hidden), user_id=user_id) return get_paginated_response('email_campaigns', query, page, per_page, parser=EmailCampaign.to_dict) def post(self): """ POST /v1/email-campaigns Required parameters: name: Name of email campaign subject: subject of email body_html: email body list_ids: smartlist ids to which emails will be sent """ # Get and validate request data data = get_valid_json_data(request, error_code=INVALID_REQUEST_BODY[1]) data = validate_and_format_request_data(data, request.user) campaign = create_email_campaign( user_id=request.user.id, oauth_token=request.oauth_token, name=data['name'], subject=data['subject'], description=data['description'], _from=data['from'], reply_to=data['reply_to'], body_html=data['body_html'], body_text=data['body_text'], list_ids=data['list_ids'], email_client_id=data['email_client_id'], start_datetime=data['start_datetime'], end_datetime=data['end_datetime'], frequency_id=data['frequency_id'], email_client_credentials_id=data['email_client_credentials_id'], base_campaign_id=data['base_campaign_id']) return {'campaign': campaign}, codes.CREATED
class BaseCampaignOverview(Resource): """ This resource returns event and all chained campaigns with given base_campaign_id """ # Access token decorator decorators = [require_oauth()] def get(self, base_campaign_id): """ This resource returns event and all chained campaigns with given base_campaign_id. ..Response:: { "event": { "cost": 0, "start_datetime": "2016-08-13 16:21:42", "venue_id": 307, "user_id": 1, "is_deleted_from_vendor": 0, "description": "Test Event Description", "social_network_id": 13, "url": "", "title": "Eventbrite Test Event", "registration_instruction": "Just Come", "max_attendees": 10, "timezone": "Asia/Karachi", "currency": "USD", "venue": { "city": "Lahore", "user_id": 1, "social_network_id": 18, "country": "", "longitude": 0, "social_network_venue_id": "16271034", "state": "Punjab", "latitude": 0, "zip_code": "", "address_line_2": "H# 163, Block A", "id": 307, "address_line_1": "New Muslim Town" }, "rsvps": [ { "social_network_rsvp_id": "6956", "status": "yes", "social_network_id": 13, "event_id": 1, "payment_status": "", "datetime": "", "candidate_id": 362553, "id": 2 }, { "social_network_rsvp_id": "2983", "status": "yes", "social_network_id": 13, "event_id": 1, "payment_status": "", "datetime": "", "candidate_id": 362555, "id": 3 }, { "social_network_rsvp_id": "5146", "status": "yes", "social_network_id": 13, "event_id": 1, "payment_status": "", "datetime": "", "candidate_id": 362557, "id": 4 } ], "tickets_id": 53364240, "social_network_group_id": "18837246", "group_url_name": "QC-Python-Learning", "organizer_id": 33, "base_campaign_id": 1, "id": 1, "social_network_event_id": "27067814562", "end_datetime": "2016-08-14 16:21:42" }, "email_campaigns": [ { "email_client_credentials_id": null, "start_datetime": null, "user_id": 1, "name": "Email campaign", "body_text": null, "description": null, "list_ids": [1], "body_html": null, "blasts": [ { "updated_datetime": "2016-02-10 20:35:57", "sends": 0, "bounces": 0, "campaign_id": 11, "text_clicks": 0, "html_clicks": 0, "complaints": 0, "id": 15, "opens": 0, "sent_datetime": "2016-02-10 20:38:39" }, { "updated_datetime": "2016-02-10 20:35:57", "sends": 0, "bounces": 0, "campaign_id": 11, "text_clicks": 0, "html_clicks": 0, "complaints": 0, "id": 16, "opens": 0, "sent_datetime": "2016-02-10 20:38:40" } ], "added_datetime": "2016-02-10T20:35:57+00:00", "frequency": null, "end_datetime": null, "talent_pipelines": [], "reply_to": "*****@*****.**", "from": "basit", "is_hidden": false, "base_campaign_id": 1, "id": 11, "subject": "email campaign sample subject" }, { "email_client_credentials_id": null, "start_datetime": null, "user_id": 1, "name": "Email campaign", "body_text": null, "description": null, "list_ids": [1], "body_html": null, "blasts": [ { "updated_datetime": "2016-02-10 20:39:54", "sends": 1, "bounces": 0, "campaign_id": 12, "text_clicks": 0, "html_clicks": 0, "complaints": 0, "id": 18, "opens": 0, "sent_datetime": "2016-02-10 20:39:44" } ], "added_datetime": "2016-02-10T20:35:57+00:00", "frequency": null, "end_datetime": null, "talent_pipelines": [], "reply_to": "*****@*****.**", "from": "basit", "is_hidden": false, "base_campaign_id": 1, "id": 12, "subject": "email campaign sample subject" } ] } """ json_event = None email_campaigns_list = [] validate_base_campaign_id(base_campaign_id, request.user.domain_id) base_campaign = BaseCampaign.get_by_id(base_campaign_id) base_campaign_event = BaseCampaignEvent.filter_by_keywords(base_campaign_id=base_campaign_id) if base_campaign_event: event = base_campaign_event[0].event # Pick first associated event json_event = event.to_json() json_event['rsvps'] = [rsvp.to_json() for rsvp in event.rsvps.all()] json_event['venue'] = event.venue.to_json() if event.venue else {} email_campaigns = base_campaign.email_campaigns.all() if not json_event and not email_campaigns: raise InvalidUsage('Requested base campaign is orphaned') for email_campaign in email_campaigns: json_email_campaign = email_campaign.to_dict() json_email_campaign['blasts'] = [blast.to_json() for blast in email_campaign.blasts.all()] email_campaigns_list.append(json_email_campaign) return {'event': json_event, 'email_campaigns': email_campaigns_list}
class TemplateFolders(Resource): """ Endpoint looks like /v1/email-template-folders """ decorators = [validate_domain_id_for_email_templates(), require_oauth()] @require_all_permissions(Permission.PermissionNames.CAN_ADD_CAMPAIGNS) def post(self): """ POST /v1/email-template-folders Create email template folder Required parameters: name: Name of email template folder parent_id: Parent ID of email template folder is_immutable: Parameter to determine is the email template folder is mutable or not :return: Template folder id .. Request Body:: { "name": "My Template Folder", "is_immutable": 1 "parent_id": 12 } .. Response:: { "id": 347 } .. Status:: 201 (Resource created) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Referenced email-template-folder does not belong to user's domain) 404 (Referenced email-template-folder not found) 500 (Internal server error) """ # TODO: Add JSON schema validation, GET-2559 data = get_valid_json_data(request, error_code=INVALID_REQUEST_BODY[1]) domain_id = request.user.domain_id folder_data = validate_and_format_data_for_template_folder_creation( data, domain_id) # Create EmailTemplateFolder object template_folder = EmailTemplateFolder( name=folder_data['name'], domain_id=domain_id, parent_id=folder_data['parent_id'], is_immutable=folder_data['is_immutable']) EmailTemplateFolder.save(template_folder) return {'id': template_folder.id}, codes.CREATED @require_all_permissions(Permission.PermissionNames.CAN_GET_CAMPAIGNS) def get(self): """ GET /v1/email-template-folders Returns all email-template folders in a user's domain { "template_folders": [ { "name": "My Template Folder", "updated_datetime": "2016-08-23 18:04:45", "is_immutable": 1, "id": 8, "parent_id": "", "domain_id": 1 }, { "name": "My Template123456", "updated_datetime": "2016-09-06 15:03:51", "is_immutable": 1, "id": 347, "parent_id": "", "domain_id": 1 } ] } """ domain_id = request.user.domain_id template_folders = EmailTemplateFolder.filter_by_keywords( domain_id=domain_id) return { "template_folders": [ template_folder.to_json() for template_folder in template_folders ] }, codes.OK
class EmailTemplate(Resource): """ Endpoint looks like /v1/email-template/:id """ decorators = [validate_domain_id_for_email_templates(), require_oauth()] @require_all_permissions(Permission.PermissionNames.CAN_GET_CAMPAIGNS) def get(self, template_id): """ GET /v1/email-templates/:id Function will return email template based on specified id :param template_id: ID of of email template :return: Email Template with specified id .. Response:: { "template": { "user_id": 1, "name": "test_email_template850323", "body_text": "", "template_folder_id": 23, "updated_datetime": "2016-09-06 18:14:42", "body_html": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html> \r\n<head>\r\n\t<title></title>\r\n</head>\r\n<body>\r\n<p>test campaign mail testing through script</p>\r\n</body>\r\n</html>\r\n", "is_immutable": 1, "type": 0, "id": 3 } } .. Status:: 200 (Resource found) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Requested email-template does not belong to user's domain) 404 (Requested email-template not found) 500 (Internal server error) """ # Validate email template id template = UserEmailTemplate.get_valid_email_template( template_id, request.user) return {'template': template.to_json()}, codes.OK @require_all_permissions(Permission.PermissionNames.CAN_EDIT_CAMPAIGNS) def patch(self, template_id): """ PATCH /v1/email-templates/:id Function would update existing email template Required parameters: :param template_id: ID of of email template :return: Updated email template .. Response:: { "template": { "user_id": 1, "name": "test_email_template850323", "body_text": "", "template_folder_id": 23, "updated_datetime": "2016-09-06 18:14:42", "body_html": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html> \r\n<head>\r\n\t<title></title>\r\n</head>\r\n<body>\r\n<p>test campaign mail testing through script</p>\r\n</body>\r\n</html>\r\n", "is_immutable": 1, "type": 0, "id": 3 } } .. Status:: 200 (Resource updated) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Requested email-template does not belong to user's domain) 404 (Requested email-template not found) 500 (Internal server error) """ template = UserEmailTemplate.get_valid_email_template( template_id, request.user) data = get_valid_json_data(request, INVALID_REQUEST_BODY[1]) updated_data = { 'body_html': data.get('body_html') or template.body_html, 'body_text': data.get('body_text') or template.body_text } template.update(**updated_data) return {'template': template.to_json()}, codes.OK @require_all_permissions(Permission.PermissionNames.CAN_DELETE_CAMPAIGNS) def delete(self, template_id): """ This deletes the requested email-template. :param int|long template_id: Id of email-template .. Status:: 204 (Resource deleted) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Requested email-template does not belong to user's domain) 404 (Requested email-template not found) 500 (Internal server error) """ template = UserEmailTemplate.get_valid_email_template( template_id, request.user) # Delete the template UserEmailTemplate.delete(template) return '', codes.NO_CONTENT
class EmailTemplates(Resource): """ Endpoint looks like /v1/email-templates """ decorators = [validate_domain_id_for_email_templates(), require_oauth()] @require_all_permissions(Permission.PermissionNames.CAN_GET_CAMPAIGNS) def get(self): """ This resource will return all the email-templates in logged-in user's domain .. Response:: { "email_templates": [ { "user_id": 1, "name": "My Template", "body_text": "This is the text part of the email", "template_folder_id": 8, "updated_datetime": "2016-09-05 15:11:22", "body_html": "<html><body>Email Body</body></html>", "is_immutable": 1, "type": 0, "id": 41 }, { "user_id": 1, "name": "My Template 2", "body_text": "This is the text part of the email", "template_folder_id": 12, "updated_datetime": "2016-09-05 15:11:22", "body_html": "<html><body>Email Body</body></html>", "is_immutable": 0, "type": 0, "id": 234 } ] } .. Status:: 200 (OK) 400 (Bad request) 401 (Unauthorized to access getTalent) 500 (Internal server error) """ page, per_page = get_pagination_params( request, error_code=INVALID_VALUE_OF_PAGINATION_PARAM[1]) domain_id = request.user.domain_id # Get all email campaigns from logged in user's domain query = UserEmailTemplate.query_by_domain_id(domain_id) return get_paginated_response('email_templates', query, page, per_page) @require_all_permissions(Permission.PermissionNames.CAN_ADD_CAMPAIGNS) def post(self): """ Function will create an email template based on the values provided by user in post data. Values required from data are template name, html body of template, template folder id and either a 0 or 1 as is_immutable value for the template. :return: ID of the created template. :rtype: json .. Request Body:: { "type": 0, "name": "My Template", "body_html": "<html><body>Email Body</body></html>", "body_text": "This is the text part of the email", "template_folder_id":8, "is_immutable": 1 } .. Response:: { "id": 347 } .. Status:: 201 (Resource created) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Requested email-template-folder does not belong to user's domain) 404 (Requested email-template-folder not found) 500 (Internal server error) """ # TODO: Add JSON schema validation, GET-2559 data = get_valid_json_data(request, error_code=INVALID_REQUEST_BODY[1]) domain_id = request.user.domain_id template_data = validate_and_format_data_for_email_template_creation( data, domain_id) # Create UserEmailTemplate object template = UserEmailTemplate( user_id=request.user.id, type=0, name=template_data['name'], body_html=template_data['body_html'], body_text=template_data['body_text'], template_folder_id=template_data['template_folder_id'], is_immutable=template_data['is_immutable']) UserEmailTemplate.save(template) return {'id': template.id}, codes.CREATED
class EmailClientsEndpoint(Resource): # Access token decorator decorators = [require_oauth()] def post(self): """ This will save an entry in database table email_client_credentials. .. Request Body:: { "host": "Host Name", "port": 123, "name": "Server Name", "email": "*****@*****.**", "password": "******", } .. Response:: { "id": 347 } .. Status:: 201 (Resource created) 400 (Bad request) 401 (Unauthorized to access getTalent) 500 (Internal server error) """ data = get_json_data_if_validated(request, EMAIL_CLIENTS_SCHEMA) data = format_email_client_data(data) data['user_id'] = request.user.id client_in_db = EmailClientCredentials.get_by_user_id_host_and_email( data['user_id'], data['host'], data['email']) if client_in_db: raise InvalidUsage( 'Email client with given data already present in database') client = EmailClientBase.get_client(data['host']) client = client(data['host'], data['port'], data['email'], data['password']) logger.info('Connecting with given email-client') client.connect() client.authenticate() logger.info( 'Successfully connected and authenticated with given email-client') # Encrypt password ciphered_password = encrypt( app.config[TalentConfigKeys.ENCRYPTION_KEY], data['password']) b64_password = b64encode(ciphered_password) data['password'] = b64_password email_client = EmailClientCredentials(**data) EmailClientCredentials.save(email_client) headers = { 'Location': EmailCampaignApiUrl.EMAIL_CLIENT_WITH_ID % email_client.id } return ApiResponse(dict(id=email_client.id), status=requests.codes.CREATED, headers=headers) def get(self): """ This will get all the email-clients added by requested user from email_client_credentials. .. Response:: { "email_client_credentials": [ { "id": 1, "user_id": 12345 "host": "server_name", "port": 123, "name": "Server Name 1", "email": "*****@*****.**", "password": "******", "updated_datetime": "2016-09-26 14:20:06" }, { "id": 2, "user_id": 12345 "host": "server_name", "port": 123, "name": "Server Name 2", "email": "*****@*****.**", "password": "******", "updated_datetime": "2016-09-26 14:20:06" } ] } .. Status:: 200 (OK Response) 400 (Bad request) 401 (Unauthorized to access getTalent) 500 (Internal server error) """ # TODO: Return all in user's domain server_type = request.args.get('type', 'outgoing') email_client_credentials = [ email_client_credential.to_json() for email_client_credential in EmailClientCredentials. get_by_user_id_and_filter_by_name(request.user.id, server_type) ] return {'email_client_credentials': email_client_credentials}, codes.OK
class TemplateFolder(Resource): """ Endpoint looks like /v1/email-template-folders/:id. """ decorators = [validate_domain_id_for_email_templates(), require_oauth()] @require_all_permissions(Permission.PermissionNames.CAN_GET_CAMPAIGNS) def get(self, folder_id): """ GET /v1/email-template-folders/:id Required parameters: :param int|long folder_id: ID of of email template :return: template-folder object in dict format, status 200 :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> template_folder_id = 1 >>> response = requests.get(EmailCampaignApiUrl.TEMPLATE_FOLDER % template_folder_id, >>> headers=headers) ..Response:: { "email_template_folder": { "name": "My Template Folder", "is_immutable": 1, "id": 8, "parent_id": "", "updated_time": "2016-08-23 18:04:45", "domain_id": 1 } } .. Status:: 200 (Resource found) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Requested email-template-folder does not belong to user's domain) 404 (Requested email-template-folder not found) 500 (Internal server error) """ template_folder = EmailTemplateFolder.get_valid_template_folder( folder_id, request.user.domain_id) return {"email_template_folder": template_folder.to_json()}, codes.OK @require_all_permissions(Permission.PermissionNames.CAN_DELETE_CAMPAIGNS) def delete(self, folder_id): """ DELETE /v1/email-template-folders/:id Required parameters: :param int|long folder_id: ID of of email template :return: Response with no content and status 204 :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> template_folder_id = 1 >>> response = requests.delete(EmailCampaignApiUrl.TEMPLATE_FOLDER % template_folder_id, >>> headers=headers) .. Status:: 204 (Resource deleted) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Requested email-template-folder does not belong to user's domain) 404 (Requested email-template-folder not found) 500 (Internal server error) """ template_folder = EmailTemplateFolder.get_valid_template_folder( folder_id, request.user.domain_id) # Delete the requested template-folder EmailTemplateFolder.delete(template_folder) return '', codes.NO_CONTENT