예제 #1
0
def check_offer_is_editable(offer: Offer):
    if not offer.isEditable:
        error = ApiErrors()
        error.status_code = 400
        error.add_error('global',
                        "Les offres importées ne sont pas modifiables")
        raise error
예제 #2
0
    def check_and_save(*objects):
        if not objects:
            raise ValueError('Objects to save need to be passed as arguments' +
                             ' to check_and_save')

        # CUMULATE ERRORS IN ONE SINGLE API ERRORS DURING ADD TIME
        api_errors = ApiErrors()
        for obj in objects:
            obj_api_errors = obj.errors()
            if obj_api_errors.errors.keys():
                api_errors.errors.update(obj_api_errors.errors)
            else:
                db.session.add(obj)

        # CHECK BEFORE COMMIT
        if api_errors.errors.keys():
            raise api_errors

        # COMMIT
        try:
            db.session.commit()
        except DataError as de:
            api_errors.addError(*BaseObject.restize_data_error(de))
            raise api_errors
        except IntegrityError as ie:
            api_errors.addError(*BaseObject.restize_integrity_error(ie))
            raise api_errors
        except TypeError as te:
            api_errors.addError(*BaseObject.restize_type_error(te))
            raise api_errors
        except ValueError as ve:
            api_errors.addError(*BaseObject.restize_value_error(ve))
            raise api_errors
        if api_errors.errors.keys():
            raise api_errors
예제 #3
0
 def ensure_can_be_updated(self):
     if self.lastProvider:
         errors = ApiErrors()
         errors.add_error(
             'global',
             'not allowed because data come from provider ' + element.lastProvider.name
         )
         raise errors
예제 #4
0
def get_offerer(id):
    dehumanize_id = dehumanize(id)
    for offerer in current_user.offerers:
        if offerer.id == dehumanize_id:
            return jsonify(offerer._asdict(include=OFFERERS_INCLUDES)), 200
    e = ApiErrors()
    e.addError('global', "Cette structure n'est pas enregistrée chez cet utilisateur.")
    return jsonify(e.errors), 400
예제 #5
0
def ensure_current_user_has_rights(rights, offerer_id, user=current_user):
    if not user.hasRights(rights, offerer_id):
        errors = ApiErrors()
        errors.add_error(
            'global',
            "Vous n'avez pas les droits d'accès suffisant pour accéder à cette information."
        )
        errors.status_code = 403
        raise errors
예제 #6
0
def internal_error(error):
    tb = traceback.format_exc()
    oneline_stack = ''.join(tb).replace('\n', ' ### ')
    app.logger.error('500 on %s %s — %s', request.method, request.url,
                     oneline_stack)
    e = ApiErrors()
    e.add_error(
        'global', "Il semble que nous ayons des problèmes techniques :(" +
        " On répare ça au plus vite.")
    return jsonify(e.errors), 500
예제 #7
0
def list_offerers_venues(id):
    dehumanize_id = dehumanize(id)
    for offerer in current_user.offerers:
        if offerer.id == dehumanize_id:
            venues = [
                o._asdict(include=VENUES_INCLUDES)
                for o in offerer.managedVenues
            ]
            return jsonify(venues), 200
    e = ApiErrors()
    e.addError('global', "Cette structure n'est pas enregistrée chez cet utilisateur.")
    return jsonify(e.errors), 400
예제 #8
0
def check_single_order_by_string(order_by_string):
    order_by_string = order_by_string.strip(' ')
    optional_table_prefix = '("?\\w+"?\\.|)'
    column_identifier = '"?\\w+"?'
    optional_sorting_order = '(|\\s+desc|\\s+asc)'
    if not re.match(
            f'^{optional_table_prefix}{column_identifier}{optional_sorting_order}$',
            order_by_string, re.IGNORECASE):
        api_errors = ApiErrors()
        api_errors.add_error('order_by',
                             'Invalid order_by field : "%s"' % order_by_string)
        raise api_errors
예제 #9
0
def load_or_raise_error(obj_class, human_id):
    data = obj_class.query.filter_by(id=dehumanize(human_id)).first()
    if data is None:
        errors = ApiErrors()
        errors.add_error(
            'global',
            'Aucun objet ne correspond à cet identifiant dans notre base de données'
        )
        errors.status_code = 400
        raise errors
    else:
        return data
예제 #10
0
def list_venue_providers():
    venueId = request.args.get('venueId')
    if venueId is None:
        e = ApiErrors()
        e.addError('venueId', 'Vous devez obligatoirement fournir le paramètre venueId')
        return jsonify(e.errors), 400

    vp_query = VenueProvider.query\
                            .filter_by(venueId=dehumanize(venueId))
    return jsonify([
        vp._asdict(include=VENUE_PROVIDER_INCLUDES)
        for vp in vp_query.all()
    ])
예제 #11
0
def handle_rest_get_list(modelClass, query=None, filter_fn=None,
                         refine=None, order_by=None, flask_request=None,
                         include=None, resolve=None, print_elements=None,
                         paginate=None, page=None):
    if flask_request is None:
        flask_request = request
    if query is None:
        query = modelClass.query
    # DELETED
    if hasattr(modelClass, 'deleted'):
        query = query.filter_by(deleted=False)
    # FILTER
    filters = flask_request.args.copy()
    if filter_fn:
        query = filter_fn(query, filters)
    # REFINE
    if refine:
        query = refine(query)
    # ORDER BY
    if order_by:
        try:
            order_by = [order_by] if not isinstance(order_by, list)\
                       else order_by
            query = query.order_by(*order_by)
        except ProgrammingError as e:
            field = re.search('column "?(.*?)"? does not exist', e._message, re.IGNORECASE)
            if field:
                errors = ApiErrors()
                errors.addError('order_by', 'order_by value references an unknown field : '+field.group(1))
                raise errors
            else:
                raise e
    # PAGINATE
    if paginate:
        page = 'page' in filters and filters['page']
        query = query.paginate(page, per_page=paginate, error_out=False)\
                     .items
    # DICTIFY
    elements = list(map(
        lambda o: o._asdict(
            include=include,
            resolve=resolve,
            filters=filters
        ),
        query))
    # PRINT
    if print_elements:
        print(elements)
    # RETURN
    return jsonify(elements), 200
예제 #12
0
def check_valid_edition(payload: dict):
    forbidden_keys = {
        'idAtProviders', 'dateModifiedAtLastProvider', 'thumbCount',
        'firstThumbDominantColor', 'owningOffererId', 'id', 'lastProviderId',
        'dateCreated'
    }
    all_keys = payload.keys()
    keys_in_error = forbidden_keys.intersection(all_keys)
    if keys_in_error:
        errors = ApiErrors()
        for key in keys_in_error:
            errors.add_error(key,
                             'Vous ne pouvez pas modifier cette information')
        raise errors
예제 #13
0
def check_offer_id_is_present_in_request(offer_id: str):
    if offer_id is None:
        errors = ApiErrors()
        errors.status_code = 400
        errors.add_error('global', 'Le paramètre offerId est obligatoire')
        errors.maybe_raise()
        raise errors
예제 #14
0
def edit_offer(offer_id):
    updated_offer_dict = request.json
    query = Offer.query.filter_by(id=dehumanize(offer_id))
    offer = query.first_or_404()
    ensure_provider_can_update(offer)
    update(offer, updated_offer_dict)
    try:
        app.model.PcObject.check_and_save(offer)
    except InternalError as ie:
        if 'check_offer' in str(ie.orig):
            ae = ApiErrors()
            ae.addError(
                'available', 'la quantité pour cette offre' +
                ' ne peut pas être inférieure' +
                ' au nombre de réservations existantes.')
            return jsonify(ae.errors), 400
        else:
            raise ie
    return jsonify(offer._asdict(include=OFFERS_INCLUDES)), 200
예제 #15
0
def query_with_order_by(query, order_by):
    if order_by:
        if type(order_by) == str:
            order_by = text(order_by)
        try:
            order_by = [order_by] if not isinstance(order_by, list) \
                else order_by
            query = query.order_by(*order_by)
        except ProgrammingError as e:
            field = re.search('column "?(.*?)"? does not exist', e._message,
                              re.IGNORECASE)
            if field:
                errors = ApiErrors()
                errors.add_error(
                    'order_by',
                    'order_by value references an unknown field : ' +
                    field.group(1))
                raise errors
            else:
                raise e
    return query
예제 #16
0
def export_table(model_name):
    check_token()
    ae = ApiErrors()
    try:
        model = app.model[model_name]
    except KeyError:
        ae.addError('global', 'Nom de classe incorrect : '+model_name)
        return jsonify(ae.errors), 400

    if not is_exportable(model_name):
        ae.addError('global', 'Classe non exportable : '+model_name)
        return jsonify(ae.errors), 400

    objects = model.query.all()
    csvfile = StringIO()
    header = objects[0]._asdict().keys()
    if model_name == 'User':
        header = list(filter(lambda h: h!='id' and h!='password', header))
    writer = csv.DictWriter(csvfile, header)
    writer.writeheader()
    for obj in objects:
        dct = obj._asdict()
        if model_name == 'User':
            del(dct['password'])
            del(dct['id'])
        writer.writerow(dct)
    csvfile.seek(0)
    mem = BytesIO()
    mem.write(csvfile.getvalue().encode('utf-8'))
    mem.seek(0)
    csvfile.close()
    return send_file(mem,
                     attachment_filename='export.csv',
                     as_attachment=True)
예제 #17
0
def get_user_with_credentials(identifier: str, password: str) -> User:

    with auto_close_db_transaction():
        user = find_user_by_email(identifier)

    errors = ApiErrors()
    errors.status_code = 401

    if not user:
        errors.add_error('identifier', 'Identifiant incorrect')
        raise errors
    if not user.isValidated:
        errors.add_error('identifier', "Ce compte n'est pas validé.")
        raise errors
    if not user.checkPassword(password):
        errors.add_error('password', 'Mot de passe incorrect')
        raise errors

    return user
예제 #18
0
def find_or_make_recommendation(user,
                                occasion_type,
                                occasion_id,
                                mediation_id,
                                from_user_id=None):
    query = Recommendation.query
    print('(special) offer_id', occasion_id, 'mediation_id', mediation_id)
    if not mediation_id and not (occasion_id and occasion_type):
        return None
    if mediation_id:
        filter = (Recommendation.mediationId == mediation_id)
    elif occasion_id and occasion_type:
        if occasion_type == 'thing':
            filter = (Recommendation.thingId == mediation_id)
        elif occasion_type == 'event':
            filter = (Recommendation.eventId == mediation_id)
        else:
            ae = ApiErrors()
            ae.addError('occasion_type',
                        "Invalid occasion type : " + occasion_type)
            raise ae
    requested_recommendation = query.filter(filter & (Recommendation.userId==user.id))\
                                    .first()
    if requested_recommendation is None:
        if mediation_id:
            return None
        elif occasion_type == 'thing':
            occasion = Thing.query.get(occasion_id)
        elif occasion_type == 'event':
            occasion = Event.query.get(occasion_id)
        mediation = Mediation.query.get(mediation_id)
        requested_recommendation = create_recommendation(user,
                                                         occasion,
                                                         mediation=mediation)

    return requested_recommendation
예제 #19
0
def check_token():
    if EXPORT_TOKEN is None or EXPORT_TOKEN == '':
        raise ValueError("Missing environment variable EXPORT_TOKEN")
    token = request.args.get('token')
    ae = ApiErrors()
    if token is None:
        ae.addError('token', 'Vous devez préciser un jeton dans l''adresse (token=XXX)')
    if not token == EXPORT_TOKEN:
        ae.addError('token', 'Le jeton est invalide')
    if ae.errors:
        raise ae
예제 #20
0
def create_mediation():
    # TODO: Allow to receive a URL from request.form['thumb']
    # save_thumb already does it so it should be easy, but I can't make it ...

    if 'thumb' not in request.files\
       or request.files['thumb'].filename == '':
        e = ApiErrors()
        e.addError('thumb', "Vous devez fournir une image d'accroche")
        return jsonify(e.errors), 400

    thumb = request.files['thumb']
    filename_parts = thumb.filename.rsplit('.', 1)
    if len(filename_parts)<2\
       or filename_parts[1].lower() not in ALLOWED_EXTENSIONS:
        e = ApiErrors()
        e.addError('thumb', "Ce format d'image n'est pas autorisé")
        return jsonify(e.errors), 400

    offererId = dehumanize(request.form['offererId'])
    ensure_current_user_has_rights(RightsType.editor, offererId)

    new_mediation = Mediation()
    new_mediation.author = current_user
    new_mediation.eventId = dehumanize(request.form['eventId'])
    new_mediation.offererId = offererId
    app.model.PcObject.check_and_save(new_mediation)

    if 'croppingRect[x]' in request.form:
        crop = [
            float(request.form['croppingRect[x]']),
            float(request.form['croppingRect[y]']),
            float(request.form['croppingRect[height]'])
        ]
    else:
        crop = None

    new_mediation.save_thumb(thumb.read(), 0, crop=crop)

    return jsonify(new_mediation), 201
예제 #21
0
def export_table(model_name):
    _check_token()
    ae = ApiErrors()
    if model_name not in models.__all__:
        ae.add_error('global', 'Classe inconnue : ' + model_name)
        return jsonify(ae.errors), 400

    try:
        model = getattr(models, model_name)
    except KeyError:
        ae.add_error('global', 'Nom de classe incorrect : ' + model_name)
        return jsonify(ae.errors), 400

    if not _is_exportable(model_name):
        ae.add_error('global', 'Classe non exportable : ' + model_name)
        return jsonify(ae.errors), 400

    objects = model.query.all()

    if len(objects) == 0:
        return "", 200

    csvfile = StringIO()
    header = _clean_dict_for_export(model_name, as_dict(objects[0])).keys()
    if model_name == 'User':
        header = list(filter(lambda h: h != 'id' and h != 'password', header))
    writer = csv.DictWriter(csvfile, header, extrasaction='ignore')
    writer.writeheader()
    for obj in objects:
        dct = _clean_dict_for_export(model_name, as_dict(obj))
        writer.writerow(dct)
    csvfile.seek(0)
    mem = BytesIO()
    mem.write(csvfile.getvalue().encode('utf-8'))
    mem.seek(0)
    csvfile.close()
    return send_file(mem, attachment_filename='export.csv', as_attachment=True)
예제 #22
0
def send_401():
    e = ApiErrors()
    e.addError('global', 'Authentification nécessaire')
    return jsonify(e.errors), 401
예제 #23
0
def get_user_with_credentials(identifier, password):
    errors = ApiErrors()
    errors.status_code = 401

    if identifier is None:
        errors.addError('identifier', 'Identifiant manquant')
    if password is None:
        errors.addError('password', 'Mot de passe manquant')
    errors.maybeRaise()

    user = User.query.filter_by(email=identifier).first()

    if not user:
        errors.addError('identifier', 'Identifiant incorrect')
        raise errors
    if not user.checkPassword(password):
        errors.addError('password', 'Mot de passe incorrect')
        raise errors

    login_user(user, remember=True)
    return user
예제 #24
0
def post_booking():
    offer_id = request.json.get('offerId')
    ae = ApiErrors()
    if current_user.canBook == False:
        ae.addError('canBook',
                    'L\'utilisateur n\'a pas le droit de réserver d\'offre')
        return jsonify(ae.errors), 400

    if offer_id is None:
        ae.addError('offerId', 'Vous devez préciser un identifiant d\'offre')
        return jsonify(ae.errors), 400

    offer = Offer.query.filter_by(id=dehumanize(offer_id)).first()

    if offer is None:
        ae.addError('offerId', 'offerId ne correspond à aucune offer')
        return jsonify(ae.errors), 400

    if offer.bookingLimitDatetime is not None and\
       offer.bookingLimitDatetime < datetime.utcnow():
        ae.addError(
            'global',
            'la date limite de réservation de cette offre' + ' est dépassée')
        return jsonify(ae.errors), 400

    new_booking = Booking()
    new_booking.offerId = dehumanize(offer_id)

    token = random_token()
    new_booking.token = token
    new_booking.user = current_user
    recommendation_id = request.json.get('recommendationId')
    if recommendation_id is not None:
        new_booking.recommendationId = dehumanize(recommendation_id)

    try:
        app.model.PcObject.check_and_save(new_booking)
    except InternalError as ie:
        if 'check_booking' in str(ie.orig):
            ae.addError(
                'global',
                'la quantité disponible pour cette offre' + ' est atteinte')
            return jsonify(ae.errors), 400
        else:
            raise ie

    send_booking_recap_emails(app.model.Offer.query.get(new_booking.offerId),
                              new_booking)
    return jsonify(new_booking._asdict(include=BOOKINGS_INCLUDES,
                                       venueTz=True)), 201
예제 #25
0
def get_sandbox(module_name, getter_name):

    if not hasattr(getters, module_name):
        errors = ApiErrors()
        errors.add_error(
            'module',
            'Il n\'existe pas de tel \"{}\" module de getters pour la sandbox'.
            format(module_name))
        raise errors

    testcafes_module = getattr(getters, module_name)

    if not hasattr(testcafes_module, getter_name):
        errors = ApiErrors()
        errors.add_error(
            'getter',
            'Il n\'existe pas de tel \"{} {}\" getter pour la sandbox'.format(
                module_name, getter_name))
        raise errors

    getter = getattr(testcafes_module, getter_name)

    try:
        obj = getter()
        return jsonify(obj)
    except:
        errors = ApiErrors()
        errors.add_error(
            'query',
            'Une erreur s\'est produite lors du calcul de \"{} {}\" pour la sandbox'
            .format(module_name, getter_name))
        raise errors
예제 #26
0
def date_time_cast_error(error):
    api_errors = ApiErrors()
    app.logger.warning(json.dumps(error.errors))
    for field in error.errors.keys():
        api_errors.add_error(field, 'Format de date invalide')
    return jsonify(api_errors.errors), 400
예제 #27
0
def invalid_id_for_dehumanize_error(error):
    api_errors = ApiErrors()
    api_errors.add_error('global', 'La page que vous recherchez n\'existe pas')
    app.logger.error('404 %s' % str(error))
    return jsonify(api_errors.errors), 404
예제 #28
0
 def errors(self):
     errors = ApiErrors()
     data = self.__class__.__table__.columns._data
     for key in data.keys():
         col = data[key]
         val = getattr(self, key)
         if not isinstance(col, Column):
             continue
         if not col.nullable\
            and not col.foreign_keys\
            and not col.primary_key\
            and col.default is None\
            and val is None:
             errors.addError(key, 'Cette information est obligatoire')
         if val is None:
             continue
         if (isinstance(col.type, String) or isinstance(col.type, CHAR))\
            and not isinstance(col.type, Enum)\
            and not isinstance(val, str):
             errors.addError(key, 'doit etre une chaine de caracteres')
         if (isinstance(col.type, String) or isinstance(col.type, CHAR))\
            and isinstance(val, str)\
            and col.type.length\
            and len(val)>col.type.length:
             errors.addError(
                 key, 'Vous devez saisir moins de ' + str(col.type.length) +
                 ' caracteres')
         if isinstance(col.type, Integer)\
            and not isinstance(val, int):
             errors.addError(key, 'doit etre un entier')
         if isinstance(col.type, Float)\
            and not isinstance(val, float):
             errors.addError(key, 'doit etre un nombre')
     return errors
예제 #29
0
def decimal_cast_error(error):
    api_errors = ApiErrors()
    app.logger.warning(json.dumps(error.errors))
    for field in error.errors.keys():
        api_errors.add_error(field, 'Saisissez un nombre valide')
    return jsonify(api_errors.errors), 400
예제 #30
0
def restize_invalid_header_exception(e):
    e = ApiErrors()
    e.add_error('global', 'Header non autorisé')
    return jsonify(e.errors), 400