Exemplo n.º 1
0
def get_languages():
    language_paginator = utils.Paginator(Config.LANGUAGE_PAGINATOR, request)
    query = Language.query

    try:
        paginated_languages = language_paginator.paginated_data(query)
        if not paginated_languages:
            return redirect('/404')
        language_list = [
            language.serialize for language in paginated_languages.items
        ]
        pagination_details = language_paginator.pagination_details(
            paginated_languages)
    except Exception as e:
        logger.exception(e)
        return utils.standardize_response(status_code=500)

    return utils.standardize_response(
        payload=dict(data=language_list, **pagination_details))
Exemplo n.º 2
0
def page_not_found(e):
    errors = [
        {
            "status": 404,
            "code": "not-found"
        }
    ]

    # Set the 404 status explicitly
    return standardize_response(None, errors, "not found"), 404
Exemplo n.º 3
0
    def wrapper(*args, **kwargs):
        apikey = request.headers.get('x-apikey')
        key = Key.query.filter_by(apikey=apikey).first()

        if not key:
            return standardize_response(status_code=401)

        log_request(request, key)

        return func(*args, **kwargs)
Exemplo n.º 4
0
def internal_server_error(e):
    errors = [
        {
            "status": 500,
            "code": "internal-server-error"
        }
    ]

    # Set the 500 status explicitly
    return standardize_response(None, errors, "internal server error"), 500
Exemplo n.º 5
0
def get_categories():
    try:
        category_paginator = utils.Paginator(Config.CATEGORY_PAGINATOR,
                                             request)
        query = Category.query

        paginated_categories = category_paginator.paginated_data(query)
        if not paginated_categories:
            return redirect('/404')
        category_list = [
            category.serialize for category in paginated_categories.items
        ]
        pagination_details = category_paginator.pagination_details(
            paginated_categories)
    except Exception as e:
        logger.exception(e)
        return utils.standardize_response(status_code=500)

    return utils.standardize_response(
        payload=dict(data=category_list, **pagination_details))
Exemplo n.º 6
0
    def wrapper(*args, **kwargs):
        apikey = request.headers.get('x-apikey')
        key = Key.query.filter_by(apikey=apikey).first()

        if not key:
            errors = [{"code": "not-authorized"}]
            return standardize_response(None, errors, "not authorized", 401)

        log_request(request, key)

        return func(*args, **kwargs)
def add_click(id):
    resource = Resource.query.get(id)

    if not resource:
        return redirect('/404')

    initial_count = getattr(resource, 'times_clicked')
    setattr(resource, 'times_clicked', initial_count + 1)
    db.session.commit()

    return utils.standardize_response(payload=dict(data=resource.serialize))
def update_votes(id, vote_direction):
    resource = Resource.query.get(id)

    if not resource:
        return redirect('/404')

    initial_count = getattr(resource, vote_direction)
    setattr(resource, vote_direction, initial_count + 1)
    db.session.commit()

    return utils.standardize_response(payload=dict(data=resource.serialize))
Exemplo n.º 9
0
def post_resources():
    json = request.get_json()

    if not isinstance(json, list):
        return wrong_type("list of resources objects", type(json))

    validation_errors = validate_resource_list(request.method, json)

    if validation_errors:
        return utils.standardize_response(payload=validation_errors, status_code=422)

    return create_resources(json, db)
def put_resource(id):
    json = request.get_json()

    if not isinstance(json, dict):
        return wrong_type("resource object", type(json))

    validation_errors = validate_resource(request.method, json, id)

    if validation_errors:
        errors = {"errors": validation_errors}
        return utils.standardize_response(payload=errors, status_code=422)
    return update_resource(id, request.get_json(), db)
Exemplo n.º 11
0
def get_resources():
    """
    Gets a paginated list of resources.

    If the URL parameters `language` or `category` are found
    in the request, the list will be filtered by these parameters.

    The filters are case insensitive.
    """
    resource_paginator = Paginator(Config.RESOURCE_PAGINATOR, request)

    # Fetch the filter params from the url, if they were provided.
    language = request.args.get('language')
    category = request.args.get('category')

    # Filter on language
    if language and not category:
        query = Resource.query.filter(
            Resource.languages.any(
                Language.name.ilike(language)
            )
        )

    # Filter on category
    elif category and not language:
        query = Resource.query.filter(
            Resource.category.has(
                func.lower(Category.name) == category.lower()
            )
        )

    # Filter on both
    elif category and language:
        query = Resource.query.filter(
            and_(
                Resource.languages.any(
                    Language.name.ilike(language)
                ),
                Resource.category.has(
                    func.lower(Category.name) == category.lower()
                )
            )
        )

    # No filters
    else:
        query = Resource.query

    resource_list = [
        resource.serialize for resource in resource_paginator.items(query)
    ]

    return standardize_response(resource_list, None, "ok")
Exemplo n.º 12
0
def add_click(id):
    resource = Resource.query.get(id)
    api_key = g.auth_key.apikey if g.auth_key else None

    if not resource:
        return redirect('/404')

    initial_count = getattr(resource, 'times_clicked')
    setattr(resource, 'times_clicked', initial_count + 1)
    db.session.commit()

    return utils.standardize_response(
        payload=dict(data=resource.serialize(api_key)), datatype="resource")
Exemplo n.º 13
0
def add_click(id):
    try:
        resource = Resource.query.get(id)

        if not resource:
            return redirect('/404')

    except NoResultFound as e:
        print_tb(e.__traceback__)
        logger.exception(e)
        return redirect('/404')

    except Exception as e:
        print_tb(e.__traceback__)
        logger.exception(e)
        return standardize_response(status_code=500)

    initial_count = getattr(resource, 'times_clicked')
    setattr(resource, 'times_clicked', initial_count + 1)
    db.session.commit()

    return standardize_response(payload=dict(data=resource.serialize))
Exemplo n.º 14
0
def create_resource(json, db):
    langs, categ = get_attributes(json)
    new_resource = Resource(name=json.get('name'),
                            url=json.get('url'),
                            category=categ,
                            languages=langs,
                            paid=json.get('paid'),
                            notes=json.get('notes'))

    db.session.add(new_resource)
    db.session.commit()

    return standardize_response(new_resource.serialize, None, "ok")
Exemplo n.º 15
0
def get_resource(id):
    resource = None
    try:
        resource = Resource.query.get(id)

    except NoResultFound as e:
        print_tb(e.__traceback__)
        logger.exception(e)
        return redirect('/404')

    if resource:
        return standardize_response(payload=dict(data=(resource.serialize)))

    return redirect('/404')
Exemplo n.º 16
0
def wrong_type(type_accepted, type_provided):
    types = {
        dict: "object",
        int: "int",
        list: "array",
        float: "number",
        bool: "boolean",
        str: "string"
    }
    json_type = types[type_provided]
    msg = f"Expected {type_accepted}, but found {json_type}"
    validation_errors = {"errors": {"invalid-type": {"message": msg}}}

    return standardize_response(payload=validation_errors, status_code=422)
Exemplo n.º 17
0
def get_categories():
    try:
        category_paginator = Paginator(Config.CATEGORY_PAGINATOR, request)
        query = Category.query

        category_list = [
            category.serialize for category in category_paginator.items(query)
        ]

    except Exception as e:
        print_tb(e.__traceback__)
        print(e)
        category_list = []
    finally:
        return standardize_response(category_list, None, "ok")
Exemplo n.º 18
0
def set_resource(id, json, db):
    resource = None
    resource = Resource.query.get(id)
    langs, categ = get_attributes(json)

    if resource:
        if json.get('languages'):
            resource.languages = langs
        if json.get('category'):
            resource.category = categ
        if json.get('name'):
            resource.name = json.get('name')
        if json.get('url'):
            resource.url = json.get('url')
        if 'paid' in json:
            resource.paid = json.get('paid')
        if 'notes' in json:
            resource.notes = json.get('notes')

        db.session.commit()

        return standardize_response(resource.serialize, None, "ok")
    else:
        return standardize_response({}, None, "ok")
Exemplo n.º 19
0
def apikey():
    """
    Verify OC membership and return an API key. The API key will be
    saved in the DB to verify use as well as returned upon subsequent calls
    to this endpoint with the same OC credentials.
    """
    json = request.get_json()
    email = json.get('email')
    password = json.get('password')
    is_oc_member = is_user_oc_member(email, password)

    if not is_oc_member:
        return unauthorized_response()

    try:
        # We need to check the database for an existing key
        apikey = Key.query.filter_by(email=email).first()

        # Don't return success for denied keys
        if apikey and apikey.denied:
            return unauthorized_response()

        if not apikey:
            # Since they're already authenticated by is_oc_user(), we know we
            # can generate an API key for them if they don't already have one
            apikey = create_new_apikey(email, db.session)
            if not apikey:
                return utils.standardize_response(status_code=500)

        logger.info(apikey.serialize)
        return utils.standardize_response(
            payload=dict(data=apikey.serialize),
            datatype="credentials")
    except Exception as e:
        logger.exception(e)
        return utils.standardize_response(status_code=500)
Exemplo n.º 20
0
def create_resource(json, db):
    langs, categ = get_attributes(json)
    new_resource = Resource(name=json.get('name'),
                            url=json.get('url'),
                            category=categ,
                            languages=langs,
                            paid=json.get('paid'),
                            notes=json.get('notes'))

    try:
        db.session.add(new_resource)
        db.session.commit()
        index.save_object(new_resource.serialize_algolia_search)

    except (AlgoliaUnreachableHostException, AlgoliaException) as e:
        logger.exception(e)
        print(f"Algolia failed to index new resource '{new_resource.name}'")

    except Exception as e:
        logger.exception(e)
        return utils.standardize_response(status_code=500)

    return utils.standardize_response(payload=dict(
        data=new_resource.serialize))
    def pay(self, data, user):
        methods = {
            'briantree': payment.Briantree(),
            'stripe': payment.Stripe()
        }
        vault = Vault.query.filter_by(user_id=user.id).filter_by(
            uuid=data['token']).first()
        data['card'] = json.loads(self.decode_token(user, vault.card_token))
        status = methods[self.gateway].pay(data)

        response = standardize_response(self.gateway, status)
        if response == True:
            return {"status": "success", "message": "charge successful"}
        elif response == False:
            return {"status": "error", "message": "charge failure"}, 500
        else:
            # do further processing on the transaction
            return further_processing(self.gateway, response)
Exemplo n.º 22
0
def update_votes(id, vote_direction_attribute):
    resource = Resource.query.get(id)

    if not resource:
        return redirect('/404')

    initial_count = getattr(resource, vote_direction_attribute)
    vote_direction = vote_direction_attribute[:-1]

    opposite_direction_attribute = 'downvotes' \
        if vote_direction_attribute == 'upvotes' else 'upvotes'
    opposite_direction = opposite_direction_attribute[:-1]
    opposite_count = getattr(resource, opposite_direction_attribute)

    api_key = g.auth_key.apikey
    vote_info = VoteInformation.query.get({
        'voter_apikey': api_key,
        'resource_id': id
    })

    if vote_info is None:
        voter = Key.query.filter_by(apikey=api_key).first()
        new_vote_info = VoteInformation(voter_apikey=api_key,
                                        resource_id=resource.id,
                                        current_direction=vote_direction)
        new_vote_info.voter = voter
        resource.voters.append(new_vote_info)
        setattr(resource, vote_direction_attribute, initial_count + 1)
    else:
        if vote_info.current_direction == vote_direction:
            setattr(resource, vote_direction_attribute, initial_count - 1)
            setattr(vote_info, 'current_direction', None)
        else:
            setattr(resource, opposite_direction_attribute, opposite_count - 1) \
                if vote_info.current_direction == opposite_direction else None
            setattr(resource, vote_direction_attribute, initial_count + 1)
            setattr(vote_info, 'current_direction', vote_direction)
    db.session.commit()

    return utils.standardize_response(
        payload=dict(data=resource.serialize(api_key)), datatype="resource")
Exemplo n.º 23
0
def update_resource(id, json, db):
    resource = Resource.query.get(id)
    api_key = g.auth_key.apikey

    if not resource:
        return redirect('/404')

    langs, categ = get_attributes(json)
    index_object = {'objectID': id}

    def get_unique_resource_categories_as_strings():
        resources = Resource.query.all()
        return {resource.category.name for resource in resources}

    def get_unique_resource_languages_as_strings():
        resources = Resource.query.all()
        return {
            language.name
            for resource in resources for language in resource.languages
        }

    try:
        logger.info(f"Updating resource. Old data: "
                    f"{json_module.dumps(resource.serialize(api_key))}")
        if json.get('languages') is not None:
            old_languages = resource.languages[:]
            resource.languages = langs
            index_object['languages'] = resource.serialize(
                api_key)['languages']
            resource_languages = get_unique_resource_languages_as_strings()
            for language in old_languages:
                if language.name not in resource_languages:
                    db.session.delete(language)
        if json.get('category'):
            old_category = resource.category
            resource.category = categ
            index_object['category'] = categ.name
            resource_categories = get_unique_resource_categories_as_strings()
            if old_category.name not in resource_categories:
                db.session.delete(old_category)
        if json.get('name'):
            resource.name = json.get('name')
            index_object['name'] = json.get('name')
        if json.get('url'):
            resource.url = json.get('url')
            index_object['url'] = json.get('url')
        if 'free' in json:
            free = ensure_bool(json.get('free'))
            resource.free = free
            index_object['free'] = free
        if 'notes' in json:
            resource.notes = json.get('notes')
            index_object['notes'] = json.get('notes')

        try:
            index.partial_update_object(index_object)

        except (AlgoliaUnreachableHostException, AlgoliaException) as e:
            if environ.get("FLASK_ENV") != 'development':
                logger.exception(e)
                msg = f"Algolia failed to update index for resource '{resource.name}'"
                logger.warn(msg)
                error = {'errors': [{"algolia-failed": {"message": msg}}]}
                return utils.standardize_response(payload=error,
                                                  status_code=500)

        # Wait to commit the changes until we know that Aloglia was updated
        db.session.commit()

        return utils.standardize_response(
            payload=dict(data=resource.serialize(api_key)),
            datatype="resource")

    except IntegrityError as e:
        logger.exception(e)
        return utils.standardize_response(status_code=422)

    except Exception as e:
        logger.exception(e)
        return utils.standardize_response(status_code=500)
Exemplo n.º 24
0
def rotate_apikey():
    new_key = rotate_key(g.auth_key, db.session)
    if not new_key:
        return utils.standardize_response(status_code=500)
    return utils.standardize_response(payload=dict(data=new_key.serialize),
                                      datatype="credentials")
Exemplo n.º 25
0
def post_resources():
    validation_errors = validate_resource(request.get_json())
    if validation_errors:
        return standardize_response(payload=validation_errors, status_code=422)
    return create_resource(request.get_json(), db)
Exemplo n.º 26
0
def get_resources():
    """
    Gets a paginated list of resources.

    If the URL parameters `language` or `category` are found
    in the request, the list will be filtered by these parameters.

    The filters are case insensitive.
    """
    resource_paginator = Paginator(Config.RESOURCE_PAGINATOR, request)

    # Fetch the filter params from the url, if they were provided.
    language = request.args.get('language')
    category = request.args.get('category')
    updated_after = request.args.get('updated_after')
    paid = request.args.get('paid')

    q = Resource.query

    # Filter on language
    if language:
        q = q.filter(Resource.languages.any(Language.name.ilike(language)))

    # Filter on category
    if category:
        q = q.filter(
            Resource.category.has(
                func.lower(Category.name) == category.lower()))

    # Filter on updated_after
    if updated_after:
        try:
            uaDate = parser.parse(updated_after)
            if uaDate > datetime.now():
                raise Exception("updated_after greater than today's date")
            uaDate = uaDate.strftime("%Y-%m-%d")
        except Exception as e:
            logger.exception(e)
            message = 'The value for "updated_after" is invalid'
            res = dict(errors=[{
                'code': 'unprocessable-entity',
                'message': message
            }])
            return standardize_response(payload=res, status_code=422)

        q = q.filter(
            or_(Resource.created_at >= uaDate,
                Resource.last_updated >= uaDate))

    # Filter on paid
    if isinstance(paid, str):
        paidAsBool = paid.lower() == 'true'
        q = q.filter(Resource.paid == paidAsBool)

    try:
        paginated_resources = resource_paginator.paginated_data(q)
        if not paginated_resources:
            return redirect('/404')
        resource_list = [
            resource.serialize for resource in paginated_resources.items
        ]
        pagination_details = resource_paginator.pagination_details(
            paginated_resources)
    except Exception as e:
        logger.exception(e)
        return standardize_response(status_code=500)

    return standardize_response(
        payload=dict(data=resource_list, **pagination_details))
Exemplo n.º 27
0
def unauthorized_response():
    message = "The email or password you submitted is incorrect " \
              "or your account is not allowed api access"
    payload = {'errors': {"unauthorized": {"message": message}}}
    return utils.standardize_response(payload=payload, status_code=401)
Exemplo n.º 28
0
    def response(*args):

        return standardize_response(dict(), 1337)
Exemplo n.º 29
0
def get_resources():
    """
    Gets a paginated list of resources.

    If the URL parameters `languages` or `category` are found
    in the request, the list will be filtered by these parameters.

    The filters are case insensitive.
    """
    resource_paginator = utils.Paginator(Config.RESOURCE_PAGINATOR, request)

    # Fetch the filter params from the url, if they were provided.
    languages = request.args.getlist('languages')
    category = request.args.get('category')
    updated_after = request.args.get('updated_after')
    paid = request.args.get('paid')

    q = Resource.query

    # Filter on languages
    if languages:
        # Take the list of languages they pass in, join them all with OR
        q = q.filter(
            or_(*map(Resource.languages.any, map(Language.name.ilike,
                                                 languages))))

    # Filter on category
    if category:
        q = q.filter(
            Resource.category.has(
                func.lower(Category.name) == category.lower()))

    # Filter on updated_after
    if updated_after:
        try:
            uaDate = parser.parse(updated_after)
            if uaDate > datetime.now():
                raise Exception("updated_after greater than today's date")
            uaDate = uaDate.strftime("%Y-%m-%d")
        except Exception as e:
            logger.exception(e)
            message = 'The value for "updated_after" is invalid'
            res = {"errors": {"unprocessable-entity": {"message": message}}}
            return utils.standardize_response(payload=res, status_code=422)

        q = q.filter(
            or_(Resource.created_at >= uaDate,
                Resource.last_updated >= uaDate))

    # Filter on paid
    if isinstance(paid, str) and paid.lower() in ['true', 'false']:
        paidAsBool = paid.lower() == 'true'
        q = q.filter(Resource.paid == paidAsBool)

    # Order by "getting started" category
    if not languages and not category and paid is None:
        show_first = Category.query.filter(
            Category.name == "Getting Started").first()
        clause = (f" CASE resource.category_id"
                  f"   WHEN {show_first.id} THEN 1"
                  f"   ELSE 2"
                  f" END, id")
        q = q.order_by(text(clause))

    try:
        paginated_resources = resource_paginator.paginated_data(q)
        if not paginated_resources:
            return redirect('/404')
        resource_list = [
            resource.serialize for resource in paginated_resources.items
        ]
        details = resource_paginator.details(paginated_resources)
    except Exception as e:
        logger.exception(e)
        return utils.standardize_response(status_code=500)

    return utils.standardize_response(payload=dict(data=resource_list,
                                                   **details),
                                      datatype="resources")
Exemplo n.º 30
0
def search_results():
    term = request.args.get('q', '', str)
    page = request.args.get('page', 0, int)
    page_size = request.args.get('page_size', Config.RESOURCE_PAGINATOR.per_page, int)

    # Fetch the filter params from the url, if they were provided.
    free = request.args.get('free')
    category = request.args.get('category')
    languages = request.args.getlist('languages')
    filters = []

    # Filter on free
    if free:
        free = free.lower()
        # algolia filters boolean attributes with either 0 or 1
        if free == 'true':
            filters.append('free=1')
        elif free == 'false':
            filters.append('free=0')

    # Filter on category
    if category:
        # to not let double quotes conflict with algolia filter format
        category = category.replace('"', "'")

        filters.append(
            f'category: "{category}"'
        )

    # Filter on languages
    if languages and '' not in languages:
        for i, _ in enumerate(languages):
            # to not let double quotes conflict with algolia filter format
            languages[i] = 'languages:"{}"'.format(languages[i].replace('"', "'"))

        # joining all possible language values to algolia filter query
        filters.append(f"( {' OR '.join(languages)} )")

    try:
        search_result = index.search(f'{term}', {
            'filters': " AND ".join(filters),
            'page': page,
            'hitsPerPage': page_size
        })

    except (AlgoliaUnreachableHostException, AlgoliaException) as e:
        logger.exception(e)
        msg = "Failed to get resources from Algolia"
        logger.warn(msg)
        error = {'errors': [{"algolia-failed": {"message": msg}}]}
        return utils.standardize_response(payload=error, status_code=500)

    if page >= int(search_result['nbPages']):
        return redirect('/404')

    results = [utils.format_resource_search(result) for result in search_result['hits']]

    details = {
        "details": {
            "page": search_result['page'],
            "number_of_pages": search_result['nbPages'],
            "records_per_page": search_result['hitsPerPage'],
            "total_count": search_result['nbHits'],
        }
    }
    return utils.standardize_response(
        payload=dict(data=results, **details),
        datatype="resources")