def delete_evidence_draft(evidence_id): json_payload = get_json_from_request() actioned_by = json_payload.get('actioned_by', None) deleted = delete_draft_evidence(evidence_id, actioned_by) if not deleted: abort(400, 'Evidence is not valid or not in a draft state') return jsonify(message="done"), 200
def notify_callback(): notify_data = get_json_from_request() if notify_data['status'] == 'permanent-failure': user = User.query.filter( User.email_address == notify_data['to'] ).first() if user and user.active: user.active = False db.session.add(user) audit_event = AuditEvent( audit_type=AuditTypes.update_user, user='******', data={"user": {"active": False}, "notify_callback_data": notify_data}, db_object=user, ) db.session.add(audit_event) db.session.commit() current_app.logger.info( "User account disabled for {hashed_email} after Notify reported permanent delivery " "failure.".format(hashed_email=hash_string(notify_data['to'])) ) elif notify_data['status'] == 'technical-failure': current_app.logger.warning("Notify failed to deliver {reference} to {hashed_email}".format( reference=notify_data['reference'], hashed_email=hash_string(notify_data['to']), )) return jsonify(status='ok'), 200
def sign_agreement(): signed_agreement_json = get_json_from_request() json_has_required_keys(signed_agreement_json, ['signed_agreement']) user_id = signed_agreement_json['signed_agreement']['user_id'] user = User.query.get(user_id) if user.supplier_code != signed_agreement_json['signed_agreement']['supplier_code']: abort(400, 'User is not authorized to submit application') signed_agreement = SignedAgreement() signed_agreement.update_from_json(signed_agreement_json['signed_agreement']) db.session.add(signed_agreement) try: db.session.flush() except IntegrityError as e: db.session.rollback() abort(400, e.orig) db.session.commit() return jsonify(signed_agreement=signed_agreement.serializable), 201
def reset_password(): json_payload = get_json_from_request() email_address = json_payload.get('email_address', None) if not email_address: abort(400, "Must supply the email address of the account to reset") user = user_service.get_by_email(email_address.lower()) if not user: abort(404, "User not found") user_data = {'user_id': user.id} claim = user_claims_service.make_claim(type='password_reset', email_address=email_address, data=user_data) if not claim: abort(500, "There was an issue completing the password reset process.") result = {'token': claim.token, 'email_address': email_address} try: audit = AuditEvent(audit_type=AuditTypes.update_user, user=email_address.lower(), data={}, db_object=user) db.session.add(audit) db.session.commit() except Exception: pass return jsonify(result)
def get_duplicate_users(): json_payload = get_json_from_request() json_has_required_keys(json_payload, ["email_address"]) email_address = json_payload["email_address"] domain = email_address.split('@')[-1] if domain in current_app.config['GENERIC_EMAIL_DOMAINS']: return jsonify(duplicate=None) supplier_code = db.session.execute(""" select distinct(supplier_code) from vuser where email_domain = :domain """, {'domain': domain}).fetchone() if (supplier_code and supplier_code[0]): send_existing_seller_notification(email_address, supplier_code[0]) duplicate_audit_event(email_address, {'supplier_code': supplier_code[0]}) return jsonify(duplicate={"supplier_code": supplier_code[0]}) application_id = db.session.execute(""" select distinct(application_id) from vuser where email_domain = :domain """, {'domain': domain}).fetchone() if (application_id and application_id[0]): send_existing_application_notification(email_address, application_id[0]) duplicate_audit_event(email_address, {'application_id': application_id[0]}) return jsonify(duplicate={"application_id": application_id[0]}) return jsonify(duplicate=None)
def create_application(): application_json = get_application_json() json_payload = get_json_from_request() application = Application() application.update_from_json(application_json) save_application(application) name = json_payload.get('name') updated_by = json_payload.get('updated_by') db.session.add(AuditEvent( audit_type=AuditTypes.create_application, user=updated_by, data={}, db_object=application )) db.session.commit() publish_tasks.application.delay( publish_tasks.compress_application(application), 'created', name=name, email_address=updated_by, from_expired=True ) return jsonify(application=application.serializable), 201
def reset_password(token): json_payload = get_json_from_request() required_keys = ['password', 'confirmPassword', 'email_address', 'user_id'] if not set(required_keys).issubset(json_payload): return jsonify( message='One or more required args were missing from the request' ), 400 if json_payload['password'] != json_payload['confirmPassword']: return jsonify(message="Passwords do not match"), 400 data = decode_reset_password_token(token.encode()) if data.get('error', None) is not None: return jsonify( message="An error occured decoding the reset password token"), 400 try: update_user_details(password=json_payload['password'], user_id=json_payload['user_id']) return jsonify( message="User with email {}, successfully updated their password". format(json_payload['email_address']), email_address=json_payload['email_address']), 200 except Exception as error: return jsonify(message=error.message), 400
def send_reset_password_email(framework_slug): json_payload = get_json_from_request() email_address = json_payload.get('email_address', None) if email_address is None: return jsonify( message='One or more required args were missing from the request' ), 400 user = User.query.filter(User.email_address == email_address).first() if user is None: return jsonify(email_address=email_address), 200 app_root_url = get_root_url(framework_slug) try: reset_password_token = generate_reset_password_token( email_address, user.id) reset_password_url = '{}{}/reset-password/{}'.format( current_app.config['FRONTEND_ADDRESS'], app_root_url, quote(reset_password_token)) send_reset_password_confirm_email(email_address=email_address, url=reset_password_url, locked=user.locked, framework=framework_slug) except Exception as error: return jsonify(message=error.message), 400 return jsonify(email_address=email_address, token=reset_password_token), 200
def update(code): """Update a supplier (role=buyer,supplier) --- tags: - suppliers security: - basicAuth: [] parameters: - name: code in: path type: integer required: true default: all responses: 200: description: A supplier type: object schema: $ref: '#/definitions/Supplier' """ try: json_payload = get_json_from_request() supplier = update_supplier(code, **json_payload) return jsonify(supplier.serializable), 200 except Exception as error: return jsonify(message=error.message), 400
def create_supplier(): request_data = get_json_from_request() if 'supplier' in request_data: supplier_data = request_data.get('supplier') else: abort(400) supplier = Supplier() return update_supplier_data_impl(supplier, supplier_data, 201)
def notify_callback(): notify_data = get_json_from_request() email_address = notify_data["to"] hashed_email = hash_string(email_address) reference = notify_data["reference"] status = notify_data["status"] # remove PII from response for logging # according to docs only "to" has PII # https://docs.notifications.service.gov.uk/rest-api.html#delivery-receipts clean_notify_data = notify_data.copy() del clean_notify_data["to"] current_app.logger.info( f"Notify callback: {status}: {reference} to {hashed_email}", extra={"notify_delivery_receipt": clean_notify_data}, ) if status == "permanent-failure": user = User.query.filter(User.email_address == email_address).first() if user and user.active: user.active = False db.session.add(user) audit_event = AuditEvent( audit_type=AuditTypes.update_user, user='******', data={ "user": { "active": False }, "notify_callback_data": notify_data }, db_object=user, ) db.session.add(audit_event) db.session.commit() current_app.logger.info( f"User account disabled for {hashed_email} after Notify reported permanent delivery " "failure.") elif status.endswith("failure"): current_app.logger.warning( f"Notify failed to deliver {reference} to {hashed_email}") return jsonify(status='ok'), 200
def set_a_declaration(code, framework_slug): framework = Framework.query.filter( Framework.slug == framework_slug ).first_or_404() supplier_framework = SupplierFramework.find_by_supplier_and_framework( code, framework_slug ) supplier = None if supplier_framework is not None: status_code = 200 if supplier_framework.declaration else 201 else: supplier = Supplier.query.filter( Supplier.code == code ).first_or_404() supplier_framework = SupplierFramework( supplier_code=supplier.code, framework_id=framework.id, declaration={} ) status_code = 201 request_data = get_json_from_request() updater_json = validate_and_return_updater_request() json_has_required_keys(request_data, ['declaration']) supplier_framework.declaration = request_data['declaration'] or {} db.session.add(supplier_framework) db.session.add( AuditEvent( audit_type=AuditTypes.answer_selection_questions, db_object=supplier_framework, user=updater_json['updated_by'], data={'update': request_data['declaration']}) ) try: db.session.commit() if supplier: publish_tasks.supplier.delay( publish_tasks.compress_supplier(supplier), 'set_declaration', updated_by=updater_json['updated_by'] ) except IntegrityError as e: db.session.rollback() abort(400, "Database Error: {}".format(e)) return jsonify(declaration=supplier_framework.declaration), status_code
def update_supplier(code): request_data = get_json_from_request() if 'supplier' in request_data: supplier_data = request_data.get('supplier') else: abort(400) if request.method == 'POST': supplier = Supplier(code=code) else: assert request.method == 'PATCH' supplier = Supplier.query.filter(Supplier.code == code).first_or_404() return update_supplier_data_impl(supplier, supplier_data, 200)
def record_supplier_invite(): json_data = get_json_from_request() json_has_required_keys(json_data, ('supplierCode', 'email')) supplier_contact = SupplierContact.query.join(Supplier).join(Contact) \ .filter(Supplier.code == json_data['supplierCode']) \ .filter(Contact.email == json_data['email']) \ .first() if supplier_contact is None: abort(400, 'No matching supplier and contact found') log_entry = SupplierUserInviteLog(supplier_id=supplier_contact.supplier_id, contact_id=supplier_contact.contact_id) db.session.merge(log_entry) db.session.commit() return jsonify(message='done')
def update_supplier(code): request_data = get_json_from_request() if 'supplier' in request_data: supplier_data = request_data.get('supplier') else: abort(400) if request.method == 'POST': supplier = Supplier(code=code) else: assert request.method == 'PATCH' supplier = Supplier.query.filter( Supplier.code == code ).first_or_404() return update_supplier_data_impl(supplier, supplier_data, 200)
def register_framework_interest(code, framework_slug): framework = Framework.query.filter( Framework.slug == framework_slug ).first_or_404() supplier = Supplier.query.filter( Supplier.code == code ).first_or_404() json_payload = get_json_from_request() updater_json = validate_and_return_updater_request() json_payload.pop('updated_by') if json_payload: abort(400, "This PUT endpoint does not take a payload.") interest_record = SupplierFramework.query.filter( SupplierFramework.supplier_code == supplier.code, SupplierFramework.framework_id == framework.id ).first() if interest_record: return jsonify(frameworkInterest=interest_record.serialize()), 200 if framework.status != 'open': abort(400, "'{}' framework is not open".format(framework_slug)) interest_record = SupplierFramework( supplier_code=supplier.code, framework_id=framework.id, declaration={} ) audit_event = AuditEvent( audit_type=AuditTypes.register_framework_interest, user=updater_json['updated_by'], data={'supplierId': supplier.code, 'frameworkSlug': framework_slug}, db_object=supplier ) try: db.session.add(interest_record) db.session.add(audit_event) db.session.commit() except IntegrityError as e: db.session.rollback() return jsonify(message="Database Error: {0}".format(e)), 400 return jsonify(frameworkInterest=interest_record.serialize()), 201
def reset_password(token): email_address_encoded = request.args.get('e') or '' if not email_address_encoded: return jsonify(message='You must provide an email address when resetting a password'), 400 email_address = unquote_plus(email_address_encoded) json_payload = get_json_from_request() required_keys = ['password', 'confirmPassword'] if not set(required_keys).issubset(json_payload): return jsonify(message='One or more required args were missing from the request'), 400 if json_payload['password'] != json_payload['confirmPassword']: return jsonify(message="Passwords do not match"), 400 try: token_age_limit = key_values_service.get_by_key('password_reset_token_age_limit') claim = user_claims_service.validate_and_update_claim( type='password_reset', token=token, email_address=email_address, age=token_age_limit['data']['age'] ) if not claim: return jsonify(message='Invalid token'), 400 except Exception as error: return jsonify(message='Invalid token'), 400 try: publish_tasks.user_claim.delay( publish_tasks.compress_user_claim(claim), 'updated' ) update_user_details( password=json_payload['password'], user_id=claim.data.get('user_id', None) ) return jsonify( message="User with email {}, successfully updated their password".format(email_address), email_address=email_address ), 200 except Exception as error: return jsonify(message=error.message), 400
def upsert_insight(): now = None incoming_month_ending = request.args.get('monthEnding', None) if incoming_month_ending: now = pendulum.parse(incoming_month_ending) json_payload = get_json_from_request() data = None if 'data' in json_payload: data = json_payload['data'] active = None if 'active' in json_payload: active = json_payload['active'] saved = insight_business.upsert(now, data, active) return jsonify(saved), 200
def update_supplier_domain(supplier_code, supplier_domain_id): json_payload = get_json_from_request() supplier_id = (db.session.query( Supplier.id).filter(Supplier.code == supplier_code).one_or_none()) supplier_domain = (db.session.query(SupplierDomain).filter( SupplierDomain.supplier_id == supplier_id).filter( SupplierDomain.id == supplier_domain_id).one_or_none()) if not supplier_domain: abort( 404, "Supplier {} does not have domain '{}' does not exist".format( supplier_id, supplier_domain_id)) user = '' dirty = False if 'update_details' in json_payload: user = json_payload['update_details']['updated_by'] if 'status' in json_payload: supplier_domain.status = json_payload['status'] dirty = True if 'price_status' in json_payload: supplier_domain.price_status = json_payload['price_status'] dirty = True if dirty is True: db.session.add( AuditEvent(audit_type=AuditTypes.assessed_domain, user=user, data={'payload': json_payload}, db_object=supplier_domain)) db.session.commit() publish_tasks.supplier_domain.delay( publish_tasks.compress_supplier_domain(supplier_domain), 'updated', supplier_code=supplier_code) supplier = (db.session.query(Supplier).filter( Supplier.code == supplier_code).one_or_none()) return jsonify(supplier=supplier.serializable), 200
def auth_user(): json_payload = get_json_from_request() json_has_required_keys(json_payload, ["authUsers"]) json_payload = json_payload["authUsers"] validate_user_auth_json_or_400(json_payload) email_address = json_payload.get('email_address', None) if email_address is None: # will remove camel case email address with future api email_address = json_payload.get('emailAddress', None) user = User.query.options( joinedload('supplier'), noload('supplier.*'), joinedload('application'), noload('application.*'), noload('*') ).filter( User.email_address == email_address.lower() ).first() if user is None or (user.supplier and user.supplier.status == 'deleted'): return jsonify(authorization=False), 404 elif encryption.authenticate_user(json_payload['password'], user) and user.active: user.logged_in_at = datetime.utcnow() user.failed_login_count = 0 db.session.add(user) db.session.commit() validation_result = None if user.role == 'supplier': messages = supplier_business.get_supplier_messages(user.supplier_code, False) validation_result = ( messages._asdict() if messages else None ) return jsonify(users=user.serialize(), validation_result=validation_result), 200 else: user.failed_login_count += 1 db.session.add(user) db.session.commit() return jsonify(authorization=False), 403
def register_framework_interest(code, framework_slug): framework = Framework.query.filter( Framework.slug == framework_slug).first_or_404() supplier = Supplier.query.filter(Supplier.code == code).first_or_404() json_payload = get_json_from_request() updater_json = validate_and_return_updater_request() json_payload.pop('updated_by') if json_payload: abort(400, "This PUT endpoint does not take a payload.") interest_record = SupplierFramework.query.filter( SupplierFramework.supplier_code == supplier.code, SupplierFramework.framework_id == framework.id).first() if interest_record: return jsonify(frameworkInterest=interest_record.serialize()), 200 if framework.status != 'open': abort(400, "'{}' framework is not open".format(framework_slug)) interest_record = SupplierFramework(supplier_code=supplier.code, framework_id=framework.id, declaration={}) audit_event = AuditEvent(audit_type=AuditTypes.register_framework_interest, user=updater_json['updated_by'], data={ 'supplierId': supplier.code, 'frameworkSlug': framework_slug }, db_object=supplier) try: db.session.add(interest_record) db.session.add(audit_event) db.session.commit() except IntegrityError as e: db.session.rollback() return jsonify(message="Database Error: {0}".format(e)), 400 return jsonify(frameworkInterest=interest_record.serialize()), 201
def supplier_search(): search_query = get_json_from_request() new_domains = False offset = get_nonnegative_int_or_400(request.args, 'from', 0) result_count = get_positive_int_or_400(request.args, 'size', current_app.config['DM_API_SUPPLIERS_PAGE_SIZE']) framework_slug = request.args.get('framework', 'digital-marketplace') sliced_results, count = do_search(search_query, offset, result_count, new_domains, framework_slug) result = { 'hits': { 'total': count, 'hits': [{'_source': r} for r in sliced_results] } } try: return jsonify(result), 200 except Exception as e: return jsonify(message=str(e)), 500
def evidence_approve(evidence_id): json_payload = get_json_from_request() try: action = DomainApproval( actioned_by=json_payload.get('actioned_by', None), evidence_id=evidence_id ) evidence_assessment = action.approve_domain() except DomainApprovalException as e: abort(400, str(e)) try: evidence = evidence_service.get_evidence_by_id(evidence_id) if evidence: send_evidence_assessment_approval_notification(evidence) except Exception as e: current_app.logger.warn('Failed to send approval email for evidence id: {}, {}'.format(evidence_id, e)) return jsonify(evidence_assessment=evidence_assessment.serialize()), 200
def set_a_declaration(code, framework_slug): framework = Framework.query.filter( Framework.slug == framework_slug).first_or_404() supplier_framework = SupplierFramework.find_by_supplier_and_framework( code, framework_slug) supplier = None if supplier_framework is not None: status_code = 200 if supplier_framework.declaration else 201 else: supplier = Supplier.query.filter(Supplier.code == code).first_or_404() supplier_framework = SupplierFramework(supplier_code=supplier.code, framework_id=framework.id, declaration={}) status_code = 201 request_data = get_json_from_request() updater_json = validate_and_return_updater_request() json_has_required_keys(request_data, ['declaration']) supplier_framework.declaration = request_data['declaration'] or {} db.session.add(supplier_framework) db.session.add( AuditEvent(audit_type=AuditTypes.answer_selection_questions, db_object=supplier_framework, user=updater_json['updated_by'], data={'update': request_data['declaration']})) try: db.session.commit() if supplier: publish_tasks.supplier.delay( publish_tasks.compress_supplier(supplier), 'set_declaration', updated_by=updater_json['updated_by']) except IntegrityError as e: db.session.rollback() abort(400, "Database Error: {}".format(e)) return jsonify(declaration=supplier_framework.declaration), status_code
def send_reset_password_email(): json_payload = get_json_from_request() email_address = json_payload.get('email_address', None) framework_slug = json_payload.get('framework', None) if email_address is None: return jsonify(message='One or more required args were missing from the request'), 400 user = User.query.filter( User.email_address == email_address.lower()).first() if user is None: return jsonify(email_address=email_address), 200 app_root_url = get_root_url(framework_slug) try: user_data = { 'user_id': user.id } claim = user_claims_service.make_claim(type='password_reset', email_address=email_address, data=user_data) if not claim: return jsonify(message="There was an issue completing the password reset process."), 500 publish_tasks.user_claim.delay( publish_tasks.compress_user_claim(claim), 'created' ) send_reset_password_confirm_email( token=claim.token, email_address=email_address, locked=user.locked, framework=framework_slug ) except Exception as error: return jsonify(message=error.message), 400 return jsonify( email_address=email_address ), 200
def get_duplicate_users(): json_payload = get_json_from_request() json_has_required_keys(json_payload, ["email_address"]) email_address = json_payload["email_address"] domain = email_address.split('@')[-1] if domain in current_app.config['GENERIC_EMAIL_DOMAINS']: return jsonify(duplicate=None) supplier_code = db.session.execute( """ select distinct(supplier_code) from vuser where email_domain = :domain """, { 'domain': domain }).fetchone() if (supplier_code and supplier_code[0]): send_existing_seller_notification(email_address, supplier_code[0]) duplicate_audit_event(email_address, {'supplier_code': supplier_code[0]}) return jsonify(duplicate={"supplier_code": supplier_code[0]}) application_id = db.session.execute( """ select distinct(application_id) from vuser where email_domain = :domain """, { 'domain': domain }).fetchone() if (application_id and application_id[0]): send_existing_application_notification(email_address, application_id[0]) duplicate_audit_event(email_address, {'application_id': application_id[0]}) return jsonify(duplicate={"application_id": application_id[0]}) return jsonify(duplicate=None)
def notify_callback(): notify_data = get_json_from_request() if notify_data['status'] == 'permanent-failure': user = User.query.filter( User.email_address == notify_data['to']).first() if user and user.active: user.active = False db.session.add(user) audit_event = AuditEvent( audit_type=AuditTypes.update_user, user='******', data={ "user": { "active": False }, "notify_callback_data": notify_data }, db_object=user, ) db.session.add(audit_event) db.session.commit() current_app.logger.info( "User account disabled for {hashed_email} after Notify reported permanent delivery " "failure.".format(hashed_email=hash_string(notify_data['to']))) elif notify_data['status'] == 'technical-failure': current_app.logger.warning( "Notify failed to deliver {reference} to {hashed_email}".format( reference=notify_data['reference'], hashed_email=hash_string(notify_data['to']), )) return jsonify(status='ok'), 200
def upsert(key): """Upsert a key value (role=admin) --- tags: - key_value security: - basicAuth: [] parameters: - name: key in: path type: string required: true - name: data in: body required: true schema: $ref: '#/definitions/KeyValueUpsert' definitions: KeyValueUpsert: properties: data: type: object responses: 200: description: A key value type: object schema: $ref: '#/definitions/KeyValue' """ try: json_payload = get_json_from_request() data = json_payload.get('data') saved = key_values_service.upsert(key, data) return jsonify(saved), 200 except Exception as error: return abort(error.message)
def supplier_search(): search_query = get_json_from_request() new_domains = False offset = get_nonnegative_int_or_400(request.args, 'from', 0) result_count = get_positive_int_or_400( request.args, 'size', current_app.config['DM_API_SUPPLIERS_PAGE_SIZE']) framework_slug = request.args.get('framework', 'digital-marketplace') sliced_results, count = do_search(search_query, offset, result_count, new_domains, framework_slug) result = { 'hits': { 'total': count, 'hits': [{ '_source': r } for r in sliced_results] } } try: return jsonify(result), 200 except Exception as e: return jsonify(message=str(e)), 500
def update_supplier_domain(supplier_code, supplier_domain_id): json_payload = get_json_from_request() supplier_id = ( db .session .query(Supplier.id) .filter(Supplier.code == supplier_code) .one_or_none() ) supplier_domain = ( db .session .query(SupplierDomain) .filter(SupplierDomain.supplier_id == supplier_id) .filter(SupplierDomain.id == supplier_domain_id) .one_or_none() ) if not supplier_domain: abort(404, "Supplier {} does not have domain '{}' does not exist".format(supplier_id, supplier_domain_id)) user = '' dirty = False if 'update_details' in json_payload: user = json_payload['update_details']['updated_by'] if 'status' in json_payload: supplier_domain.status = json_payload['status'] dirty = True if 'price_status' in json_payload: supplier_domain.price_status = json_payload['price_status'] dirty = True if dirty is True: db.session.add(AuditEvent( audit_type=AuditTypes.assessed_domain, user=user, data={ 'payload': json_payload }, db_object=supplier_domain )) db.session.commit() publish_tasks.supplier_domain.delay( publish_tasks.compress_supplier_domain(supplier_domain), 'updated', supplier_code=supplier_code ) supplier = ( db .session .query(Supplier) .filter(Supplier.code == supplier_code) .one_or_none() ) return jsonify(supplier=supplier.serializable), 200
def get_case_study_json(): json_payload = get_json_from_request() json_has_required_keys(json_payload, ['caseStudy']) return json_payload['caseStudy']
def submit_application(application_id): current_time = pendulum.now('UTC').to_iso8601_string(extended=True) application = Application.query.get(application_id) if application is None: abort(404, "Application '{}' does not exist".format(application_id)) if application.status == 'submitted': abort(400, 'Application is already submitted') errors = ApplicationValidator(application).validate_all() if errors: abort(400, 'Application has errors') json_payload = get_json_from_request() json_has_required_keys(json_payload, ['user_id']) user_id = json_payload['user_id'] user = User.query.get(user_id) if application.type != 'edit': if user.application_id != application.id: abort(400, 'User is not authorized to submit application') else: if user.supplier_code != application.supplier_code: abort( 400, 'User supplier code does not match application supplier code') agreement = get_current_agreement() if agreement is None: abort(404, 'Current master agreement not found') db.session.add( AuditEvent(audit_type=AuditTypes.submit_application, user=user_id, data={}, db_object=application)) application.submit_for_approval() application.update_from_json({'submitted_at': current_time}) signed_agreement = None if application.type != 'edit': # only create signed agreements on initial applications signed_agreement = SignedAgreement() signed_agreement.user_id = user_id signed_agreement.agreement_id = agreement.id signed_agreement.signed_at = current_time signed_agreement.application_id = application_id db.session.add(signed_agreement) if application.supplier_code: send_submitted_existing_seller_notification(application.id) else: send_submitted_new_seller_notification(application.id) db.session.commit() publish_tasks.application.delay( publish_tasks.compress_application(application), 'submitted') return jsonify(application=application.serializable, signed_agreement=signed_agreement)
def submit_application(application_id): current_time = pendulum.now('UTC').to_iso8601_string(extended=True) application = Application.query.get(application_id) if application is None: abort(404, "Application '{}' does not exist".format(application_id)) if application.status == 'submitted': abort(400, 'Application is already submitted') errors = ApplicationValidator(application).validate_all() if errors: abort(400, 'Application has errors') json_payload = get_json_from_request() json_has_required_keys(json_payload, ['user_id']) user_id = json_payload['user_id'] user = User.query.get(user_id) if application.type != 'edit': if user.application_id != application.id: abort(400, 'User is not authorized to submit application') else: if user.supplier_code != application.supplier_code: abort(400, 'User supplier code does not match application supplier code') current_agreement = Agreement.query.filter( Agreement.is_current == true() ).first_or_404() db.session.add(AuditEvent( audit_type=AuditTypes.submit_application, user=user_id, data={}, db_object=application )) application.submit_for_approval() application.update_from_json({'submitted_at': current_time}) signed_agreement = None if application.type != 'edit': # only create signed agreements on initial applications signed_agreement = SignedAgreement() signed_agreement.user_id = user_id signed_agreement.agreement_id = current_agreement.id signed_agreement.signed_at = current_time signed_agreement.application_id = application_id db.session.add(signed_agreement) if application.supplier_code: send_submitted_existing_seller_notification(application.id) else: send_submitted_new_seller_notification(application.id) db.session.commit() publish_tasks.application.delay( publish_tasks.compress_application(application), 'submitted' ) return jsonify(application=application.serializable, signed_agreement=signed_agreement)
def create_user(): json_payload = get_json_from_request() json_has_required_keys(json_payload, ["users"]) json_payload = json_payload["users"] validate_user_json_or_400(json_payload) email_address = json_payload.get('email_address', None) if email_address is None: email_address = json_payload.get('emailAddress', None) user = User.query.filter( User.email_address == email_address.lower()).first() if user: abort(409, "User already exists") if 'hashpw' in json_payload and not json_payload['hashpw']: password = json_payload['password'] else: password = encryption.hashpw(json_payload['password']) now = datetime.utcnow() user = User(email_address=email_address.lower(), phone_number=json_payload.get('phoneNumber') or None, name=json_payload['name'], role=json_payload['role'], password=password, active=True, created_at=now, updated_at=now, password_changed_at=now) audit_data = {} if "supplierCode" in json_payload: user.supplier_code = json_payload['supplierCode'] audit_data['supplier_code'] = user.supplier_code check_supplier_role(user.role, user.supplier_code) if "application_id" in json_payload: user.application_id = json_payload['application_id'] elif user.supplier_code is not None: appl = Application.query.filter_by( supplier_code=user.supplier_code).first() user.application_id = appl and appl.id or None check_applicant_role(user.role, user.application_id) try: db.session.add(user) db.session.flush() audit = AuditEvent(audit_type=AuditTypes.create_user, user=email_address.lower(), data=audit_data, db_object=user) db.session.add(audit) db.session.commit() user = db.session.query(User).options( noload('*')).filter(User.id == user.id).one_or_none() publish_tasks.user.delay(publish_tasks.compress_user(user), 'created') if user.role == 'buyer': notification_message = 'Domain: {}'.format( email_address.split('@')[-1]) notification_text = 'A new buyer has signed up' notify_team(notification_text, notification_message) except IntegrityError: db.session.rollback() abort(400, "Invalid supplier code or application id") except DataError: db.session.rollback() abort(400, "Invalid user role") return jsonify(users=user.serialize()), 201
def create_assessment(): updater_json = validate_and_return_updater_request() json_payload = get_json_from_request() json_has_required_keys(json_payload, ['assessment']) data = json_payload['assessment'] json_has_required_keys(data, ['supplier_code']) json_has_required_keys(data, ['domain_name']) supplier_code = data['supplier_code'] updated_by = updater_json['updated_by'] existing_assessment = db.session.query( Assessment ).join( SupplierDomain, Supplier, Domain ).filter( Supplier.code == supplier_code, Domain.name == data['domain_name'], Assessment.active ).first() if existing_assessment: send_assessment_requested_notification(existing_assessment, updated_by) publish_tasks.assessment.delay( publish_tasks.compress_assessment(existing_assessment), 'existing', updated_by=updated_by ) return jsonify(assessment=existing_assessment.serializable), 201 assessment = Assessment() assessment.update_from_json(json_payload['assessment']) db.session.add(assessment) try: db.session.commit() except IntegrityError: abort(400) db.session.add(AuditEvent( audit_type=AuditTypes.create_assessment, user=updated_by, data={}, db_object=assessment )) if current_app.config['JIRA_FEATURES']: application = db.session.query(Application).filter( Application.supplier_code == supplier_code, Application.type == 'edit', Application.status == 'submitted' ).one_or_none() mj = get_marketplace_jira() mj.create_domain_approval_task(assessment, application) send_assessment_requested_notification(assessment, updater_json['updated_by']) publish_tasks.assessment.delay( publish_tasks.compress_assessment(assessment), 'created', updated_by=updated_by ) return jsonify(assessment=assessment.serializable), 201
def update_user(user_id): """ Update a user. Looks user up in DB, and updates where necessary. """ update_details = validate_and_return_updater_request() user = User.query.options( noload('*')).filter(User.id == user_id).first_or_404() json_payload = get_json_from_request() json_has_required_keys(json_payload, ["users"]) user_update = json_payload["users"] json_has_matching_id(user_update, user_id) existing_user = publish_tasks.compress_user(user) if 'password' in user_update: user.password = encryption.hashpw(user_update['password']) user.password_changed_at = datetime.utcnow() user_update['password'] = '******' if 'active' in user_update: user.active = user_update['active'] if 'name' in user_update: user.name = user_update['name'] if 'emailAddress' in user_update: user.email_address = user_update['emailAddress'] if 'role' in user_update: if user.role == 'supplier' and user_update['role'] != user.role: user.supplier_code = None user_update.pop('supplierCode', None) user.role = user_update['role'] if 'supplierCode' in user_update: user.supplier_code = user_update['supplierCode'] if 'application_id' in user_update: user.application_id = user_update['application_id'] if 'locked' in user_update and not user_update['locked']: user.failed_login_count = 0 if 'termsAcceptedAt' in user_update: user.terms_accepted_at = user_update['termsAcceptedAt'] check_supplier_role(user.role, user.supplier_code) audit = AuditEvent(audit_type=AuditTypes.update_user, user=update_details.get('updated_by', 'no user data'), data={ 'user': user.email_address, 'update': user_update }, db_object=user) db.session.add(user) db.session.add(audit) publish_tasks.user.delay(publish_tasks.compress_user(user), 'updated', old_user=existing_user) try: db.session.commit() return jsonify(users=user.serialize()), 200 except (IntegrityError, DataError): db.session.rollback() abort(400, "Could not update user with: {0}".format(user_update))
def update_user(user_id): """ Update a user. Looks user up in DB, and updates where necessary. """ update_details = validate_and_return_updater_request() user = User.query.options( noload('*') ).filter( User.id == user_id ).first_or_404() json_payload = get_json_from_request() json_has_required_keys(json_payload, ["users"]) user_update = json_payload["users"] json_has_matching_id(user_update, user_id) existing_user = publish_tasks.compress_user(user) if 'password' in user_update: user.password = encryption.hashpw(user_update['password']) user.password_changed_at = datetime.utcnow() user_update['password'] = '******' if 'active' in user_update: user.active = user_update['active'] if 'name' in user_update: user.name = user_update['name'] if 'emailAddress' in user_update: user.email_address = user_update['emailAddress'] if 'role' in user_update: if user.role == 'supplier' and user_update['role'] != user.role: user.supplier_code = None user_update.pop('supplierCode', None) user.role = user_update['role'] if 'supplierCode' in user_update: user.supplier_code = user_update['supplierCode'] if 'application_id' in user_update: user.application_id = user_update['application_id'] if 'locked' in user_update and not user_update['locked']: user.failed_login_count = 0 if 'termsAcceptedAt' in user_update: user.terms_accepted_at = user_update['termsAcceptedAt'] check_supplier_role(user.role, user.supplier_code) audit = AuditEvent( audit_type=AuditTypes.update_user, user=update_details.get('updated_by', 'no user data'), data={ 'user': user.email_address, 'update': user_update }, db_object=user ) db.session.add(user) db.session.add(audit) publish_tasks.user.delay( publish_tasks.compress_user(user), 'updated', old_user=existing_user ) try: db.session.commit() return jsonify(users=user.serialize()), 200 except (IntegrityError, DataError): db.session.rollback() abort(400, "Could not update user with: {0}".format(user_update))
def create_user(): json_payload = get_json_from_request() json_has_required_keys(json_payload, ["users"]) json_payload = json_payload["users"] validate_user_json_or_400(json_payload) email_address = json_payload.get('email_address', None) if email_address is None: email_address = json_payload.get('emailAddress', None) user = User.query.filter( User.email_address == email_address.lower()).first() if user: abort(409, "User already exists") if 'hashpw' in json_payload and not json_payload['hashpw']: password = json_payload['password'] else: password = encryption.hashpw(json_payload['password']) now = datetime.utcnow() user = User( email_address=email_address.lower(), phone_number=json_payload.get('phoneNumber') or None, name=json_payload['name'], role=json_payload['role'], password=password, active=True, created_at=now, updated_at=now, password_changed_at=now ) audit_data = {} if "supplierCode" in json_payload: user.supplier_code = json_payload['supplierCode'] audit_data['supplier_code'] = user.supplier_code check_supplier_role(user.role, user.supplier_code) if "application_id" in json_payload: user.application_id = json_payload['application_id'] elif user.supplier_code is not None: appl = Application.query.filter_by(supplier_code=user.supplier_code).first() user.application_id = appl and appl.id or None check_applicant_role(user.role, user.application_id) try: db.session.add(user) db.session.flush() audit = AuditEvent( audit_type=AuditTypes.create_user, user=email_address.lower(), data=audit_data, db_object=user ) db.session.add(audit) db.session.commit() user = db.session.query(User).options(noload('*')).filter(User.id == user.id).one_or_none() publish_tasks.user.delay( publish_tasks.compress_user(user), 'created' ) if user.role == 'buyer': notification_message = 'Domain: {}'.format( email_address.split('@')[-1] ) notification_text = 'A new buyer has signed up' notify_team(notification_text, notification_message) except IntegrityError: db.session.rollback() abort(400, "Invalid supplier code or application id") except DataError: db.session.rollback() abort(400, "Invalid user role") return jsonify(users=user.serialize()), 201
def get_work_order_json(): json_payload = get_json_from_request() json_has_required_keys(json_payload, ['workOrder']) return json_payload['workOrder']
def create_application_from_supplier(code, application_type=None): json_payload = get_json_from_request() json_has_required_keys(json_payload, ["current_user"]) current_user = json_payload["current_user"] supplier = Supplier.query.options( joinedload('domains'), joinedload('domains.assessments'), joinedload('domains.domain'), joinedload('domains.recruiter_info'), noload('domains.supplier'), noload('domains.assessments.briefs') ).filter( Supplier.code == code ).first_or_404() # hotfix for exception. shouldn't need to do this supplier.data = supplier.data or {} application_type = application_type or 'upgrade' existing_application = Application.query.options( joinedload('supplier') ).filter( Application.supplier_code == supplier.code, or_(Application.status == 'submitted', Application.status == 'saved') ).first() if existing_application: errors = ApplicationValidator(existing_application).validate_all() return jsonify(application=existing_application.serializable, application_errors=errors) data = json.loads(supplier.json) data['status'] = 'saved' data = {key: data[key] for key in data if key not in ['id', 'contacts', 'domains', 'links', 'prices', 'frameworks', 'steps', 'signed_agreements']} if data.get('products'): for product in data['products']: if product.get('links'): del product['links'] application = Application() application.update_from_json(data) application.type = application_type db.session.add(application) db.session.flush() audit_type = application_type == 'edit' and AuditTypes.supplier_update or AuditTypes.create_application db.session.add(AuditEvent( audit_type=audit_type, user='', data={}, db_object=application )) db.session.flush() if application_type != 'edit': notification_message = '{}\nApplication Id:{}\nBy: {} ({})'.format( data['name'], application.id, current_user['name'], current_user['email_address'] ) notification_text = 'An existing seller has started a new application' notify_team(notification_text, notification_message) # TODO stop using application_id on user supplier.update_from_json({'application_id': application.id}) users = User.query.options( noload('supplier'), noload('application') ).filter( User.supplier_code == code and User.active == true() ).all() for user in users: user.application_id = application.id db.session.commit() publish_tasks.application.delay( publish_tasks.compress_application(application), 'created', name=current_user['name'], email_address=current_user['email_address'], from_expired=False ) return jsonify(application=application)
def create_application_from_supplier(code, application_type=None): json_payload = get_json_from_request() json_has_required_keys(json_payload, ["current_user"]) current_user = json_payload["current_user"] supplier = Supplier.query.options( joinedload('domains'), joinedload('domains.assessments'), joinedload('domains.domain'), joinedload('domains.recruiter_info'), noload('domains.supplier'), noload('domains.assessments.briefs')).filter( Supplier.code == code).first_or_404() # hotfix for exception. shouldn't need to do this supplier.data = supplier.data or {} application_type = application_type or 'upgrade' existing_application = Application.query.options( joinedload('supplier')).filter( Application.supplier_code == supplier.code, or_(Application.status == 'submitted', Application.status == 'saved')).first() if existing_application: errors = ApplicationValidator(existing_application).validate_all() return jsonify(application=existing_application.serializable, application_errors=errors) data = json.loads(supplier.json) data['status'] = 'saved' data = { key: data[key] for key in data if key not in [ 'id', 'contacts', 'domains', 'links', 'prices', 'frameworks', 'steps', 'signed_agreements' ] } if data.get('products'): for product in data['products']: if product.get('links'): del product['links'] application = Application() application.update_from_json(data) application.type = application_type db.session.add(application) db.session.flush() audit_type = application_type == 'edit' and AuditTypes.supplier_update or AuditTypes.create_application db.session.add( AuditEvent(audit_type=audit_type, user='', data={}, db_object=application)) db.session.flush() if application_type != 'edit': notification_message = '{}\nApplication Id:{}\nBy: {} ({})'.format( data['name'], application.id, current_user['name'], current_user['email_address']) notification_text = 'An existing seller has started a new application' notify_team(notification_text, notification_message) # TODO stop using application_id on user supplier.update_from_json({'application_id': application.id}) users = User.query.options(noload('supplier'), noload('application')).filter( User.supplier_code == code and User.active == true()).all() for user in users: user.application_id = application.id db.session.commit() publish_tasks.application.delay( publish_tasks.compress_application(application), 'created', name=current_user['name'], email_address=current_user['email_address'], from_expired=False) return jsonify(application=application)
def get_application_json(): json_payload = get_json_from_request() json_has_required_keys(json_payload, ['application']) return json_payload['application']
def update_supplier_framework_details(code, framework_slug): framework = Framework.query.filter( Framework.slug == framework_slug).first_or_404() supplier = Supplier.query.filter(Supplier.code == code).first_or_404() json_payload = get_json_from_request() updater_json = validate_and_return_updater_request() json_has_required_keys(json_payload, ["frameworkInterest"]) update_json = json_payload["frameworkInterest"] interest_record = SupplierFramework.query.filter( SupplierFramework.supplier_code == supplier.code, SupplierFramework.framework_id == framework.id).first() if not interest_record: abort( 404, "code '{}' has not registered interest in {}".format( code, framework_slug)) # `agreementDetails` shouldn't be passed in unless the framework has framework_agreement_details if 'agreementDetails' in update_json and framework.framework_agreement_details is None: abort( 400, "Framework '{}' does not accept 'agreementDetails'".format( framework_slug)) if ((framework.framework_agreement_details and framework.framework_agreement_details.get('frameworkAgreementVersion') ) and # noqa ('agreementDetails' in update_json or update_json.get('agreementReturned'))): required_fields = ['signerName', 'signerRole'] if update_json.get('agreementReturned'): required_fields.append('uploaderUserId') # Make a copy of the existing agreement_details with our new changes to be added and validate this # If invalid, 400 agreement_details = interest_record.agreement_details.copy( ) if interest_record.agreement_details else {} if update_json.get('agreementDetails'): agreement_details.update(update_json['agreementDetails']) if update_json.get('agreementReturned'): agreement_details[ 'frameworkAgreementVersion'] = framework.framework_agreement_details[ 'frameworkAgreementVersion'] # noqa validate_agreement_details_data(agreement_details, enforce_required=False, required_fields=required_fields) if update_json.get('agreementDetails') and update_json[ 'agreementDetails'].get('uploaderUserId'): user = User.query.filter(User.id == update_json['agreementDetails'] ['uploaderUserId']).first() if not user: abort( 400, "No user found with id '{}'".format( update_json['agreementDetails']['uploaderUserId'])) interest_record.agreement_details = agreement_details or None uniform_now = datetime.utcnow() if 'onFramework' in update_json: interest_record.on_framework = update_json['onFramework'] if 'agreementReturned' in update_json: if update_json["agreementReturned"] is False: interest_record.agreement_returned_at = None interest_record.agreement_details = None else: interest_record.agreement_returned_at = uniform_now if update_json.get('countersigned'): interest_record.countersigned_at = uniform_now audit_event = AuditEvent(audit_type=AuditTypes.supplier_update, user=updater_json['updated_by'], data={ 'supplierId': supplier.code, 'frameworkSlug': framework_slug, 'update': update_json }, db_object=supplier) try: db.session.add(interest_record) db.session.add(audit_event) db.session.commit() except IntegrityError as e: db.session.rollback() return jsonify(message="Database Error: {0}".format(e)), 400 return jsonify(frameworkInterest=interest_record.serialize()), 200
def update_supplier_framework_details(code, framework_slug): framework = Framework.query.filter( Framework.slug == framework_slug ).first_or_404() supplier = Supplier.query.filter( Supplier.code == code ).first_or_404() json_payload = get_json_from_request() updater_json = validate_and_return_updater_request() json_has_required_keys(json_payload, ["frameworkInterest"]) update_json = json_payload["frameworkInterest"] interest_record = SupplierFramework.query.filter( SupplierFramework.supplier_code == supplier.code, SupplierFramework.framework_id == framework.id ).first() if not interest_record: abort(404, "code '{}' has not registered interest in {}".format(code, framework_slug)) # `agreementDetails` shouldn't be passed in unless the framework has framework_agreement_details if 'agreementDetails' in update_json and framework.framework_agreement_details is None: abort(400, "Framework '{}' does not accept 'agreementDetails'".format(framework_slug)) if ( (framework.framework_agreement_details and framework.framework_agreement_details.get('frameworkAgreementVersion')) and # noqa ('agreementDetails' in update_json or update_json.get('agreementReturned')) ): required_fields = ['signerName', 'signerRole'] if update_json.get('agreementReturned'): required_fields.append('uploaderUserId') # Make a copy of the existing agreement_details with our new changes to be added and validate this # If invalid, 400 agreement_details = interest_record.agreement_details.copy() if interest_record.agreement_details else {} if update_json.get('agreementDetails'): agreement_details.update(update_json['agreementDetails']) if update_json.get('agreementReturned'): agreement_details['frameworkAgreementVersion'] = framework.framework_agreement_details['frameworkAgreementVersion'] # noqa validate_agreement_details_data( agreement_details, enforce_required=False, required_fields=required_fields ) if update_json.get('agreementDetails') and update_json['agreementDetails'].get('uploaderUserId'): user = User.query.filter(User.id == update_json['agreementDetails']['uploaderUserId']).first() if not user: abort(400, "No user found with id '{}'".format(update_json['agreementDetails']['uploaderUserId'])) interest_record.agreement_details = agreement_details or None uniform_now = datetime.utcnow() if 'onFramework' in update_json: interest_record.on_framework = update_json['onFramework'] if 'agreementReturned' in update_json: if update_json["agreementReturned"] is False: interest_record.agreement_returned_at = None interest_record.agreement_details = None else: interest_record.agreement_returned_at = uniform_now if update_json.get('countersigned'): interest_record.countersigned_at = uniform_now audit_event = AuditEvent( audit_type=AuditTypes.supplier_update, user=updater_json['updated_by'], data={'supplierId': supplier.code, 'frameworkSlug': framework_slug, 'update': update_json}, db_object=supplier ) try: db.session.add(interest_record) db.session.add(audit_event) db.session.commit() except IntegrityError as e: db.session.rollback() return jsonify(message="Database Error: {0}".format(e)), 400 return jsonify(frameworkInterest=interest_record.serialize()), 200
def casestudies_search(): search_query = get_json_from_request() offset = get_nonnegative_int_or_400(request.args, 'from', 0) result_count = get_positive_int_or_400(request.args, 'size', current_app.config['DM_API_SUPPLIERS_PAGE_SIZE']) sort_dir = search_query.get('sort_dir', 'asc') sort_by = search_query.get('sort_by', None) domains = search_query.get('domains', None) seller_types = search_query.get('seller_types', None) search_term = search_query.get('search_term', None) framework_slug = request.args.get('framework', 'digital-marketplace') q = db.session.query(CaseStudy).join(Supplier).outerjoin(SupplierDomain).outerjoin(Domain) \ .outerjoin(SupplierFramework).outerjoin(Framework) q = q.filter(Supplier.status != 'deleted', or_(Framework.slug == framework_slug, ~Supplier.frameworks.any())) tsquery = None if search_term: if ' ' in search_term: tsquery = func.plainto_tsquery(search_term) else: tsquery = func.to_tsquery(search_term + ":*") q = q.add_column(func.ts_headline( 'english', func.concat( CaseStudy.data['approach'].astext, ' ', CaseStudy.data['role'].astext), tsquery, 'MaxWords=150, MinWords=75, ShortWord=3, HighlightAll=FALSE, FragmentDelimiter=" ... " ' )) else: q = q.add_column("''") q = q.add_column(Supplier.name) q = q.add_column(postgres.array_agg(Supplier.data)) q = q.group_by(CaseStudy.id, Supplier.name) if domains: d_agg = postgres.array_agg(cast(Domain.name, TEXT)) q = q.having(d_agg.contains(array(domains))) if seller_types: selected_seller_types = select( [postgres.array_agg(column('key'))], from_obj=func.json_each_text(Supplier.data[('seller_type',)]), whereclause=cast(column('value'), Boolean) ).as_scalar() q = q.filter(selected_seller_types.contains(array(seller_types))) if sort_dir in ('desc', 'z-a'): ob = [desc(CaseStudy.data['title'].astext)] else: ob = [asc(CaseStudy.data['title'].astext)] if search_term: ob = [desc(func.ts_rank_cd(func.to_tsvector( func.concat(Supplier.name, CaseStudy.data['title'].astext, CaseStudy.data['approach'].astext)), tsquery))] + ob condition = func.to_tsvector(func.concat(Supplier.name, CaseStudy.data['title'].astext, CaseStudy.data['approach'].astext)).op('@@')(tsquery) q = q.filter(condition) q = q.order_by(*ob) raw_results = list(q) results = [] for x in range(len(raw_results)): result = raw_results[x][0].serialize() if raw_results[x][1] is not None and raw_results[x][1] != '': result['approach'] = raw_results[x][1] if raw_results[x][2] is not None: result['supplierName'] = raw_results[x][2] if raw_results[x][3] is not None and raw_results[x][3][0] is not None: result['seller_type'] = raw_results[x][3][0].get('seller_type') results.append(result) total_results = len(results) sliced_results = results[offset:(offset + result_count)] result = { 'hits': { 'total': total_results, 'hits': [{'_source': r} for r in sliced_results] } } try: response = jsonify(result), 200 except Exception as e: response = jsonify(message=str(e)), 500 return response
def get_project_json(): json_payload = get_json_from_request() json_has_required_keys(json_payload, ['project']) return json_payload['project']