Saurabh
Saurabh

Reputation: 6950

Sending JSON File as Post Request Flask

I'm writing a test to retrieve the top n posts from a community.

Before getting those values, I want to post data from a JSON file, instead of posting them to the endpoint individually.

I've tried slight variations of test_retrieve_recent_posts_existing_community, but keep getting a TypeError.

Can anyone suggest how I can fix the test case?

Route:

@app.route('/api/v2/post/retrieve/<community>/<top>', methods=['GET'])
def recent_community_posts(community=None, top=0):
    community = request.args.get('community')
    top       = request.args.get('top')
    filtered_posts = []
    if community:
        for post in posts:
            if post['community'] == community:
                filtered_posts.append(post)
    
    if len(filtered_posts) == 0:
        return {'message': 'Community does not exist ...', 'status': 402}
    return {'data': jsonify(filtered_posts[:top]), 'message': "Filtered data based on community", 'status': 203} 

Test:

def test_retrieve_recent_posts_existing_community(client):
    with open('posts.json') as posts_data:
        posts_json = json.dumps(posts_data)

    client.post('/api/v2/post/create', data=json.dump(posts_json), content_type='application/json')

    response = client.get('/api/v2/post/retrieve/tech/2')
    data = json.load(response.get_data(as_text=True))
    assert "Filtered data" in data["message"] and data["status"] == 203

Error:

TypeError: Object of type TextIOWrapper is not JSON serializable

File (posts.json):

[
    {
        "postid": 2,
        "title": "Heading 2",
        "text": "Content of post 2",
        "published": "YYYY/MM/DD hh:mm:ss",
        "community": "tech"
    },
    {
        "postid": 3,
        "title": "Heading 3",
        "text": "Content of post 3",
        "published": "YYYY/MM/DD hh:mm:ss",
        "community": "tech"
    },
    {
        "postid": 4,
        "title": "Heading 4",
        "text": "Content of post 4",
        "published": "YYYY/MM/DD hh:mm:ss",
        "community": "art"
    },
    {
        "postid": 5,
        "title": "Heading 5",
        "text": "Content of post 5",
        "published": "YYYY/MM/DD hh:mm:ss",
        "community": "art"
    },
    {
        "postid": 6,
        "title": "Heading 6",
        "text": "Content of post 6",
        "published": "YYYY/MM/DD hh:mm:ss",
        "community": "science"
    },
    {
        "postid": 7,
        "title": "Heading 7",
        "text": "Content of post 7",
        "published": "YYYY/MM/DD hh:mm:ss",
        "community": "tech"
    }
]

Link to files routes and tests

Upvotes: 0

Views: 867

Answers (3)

Mostafa Kashwaa
Mostafa Kashwaa

Reputation: 341

All errors in your test are caused by wrong usage of json module methods.

Here is a modified version of your test with comments on the changes

def test_retrieve_recent_posts_existing_community(client):
with open('posts.json') as posts_data:
    posts_json = json.load(posts_data)  # posts data is a TextIOWrapper object. you read it using load not dumps

    # posts_json is a list and should be turned into a json string using json.dumps not dump
    client.post('/api/v2/post/create', data=json.dumps(posts_json), content_type='application/json')

    response = client.get('/api/v2/post/retrieve/tech/2')
    data = json.loads(response.get_data(as_text=True))  # use loads not load to read the json string.
    assert "Filtered data" in data["message"] and data["status"] == 203

Notice that you also have a problem in the create method. It expects one object to be passed in the json body. and you're sending an array. So either change your json file to contain one object or change the create implementation to deal with an array of objects. for example change it to the following

def create():
data_list = request.get_json()
for data in data_list:
    postid = data['postid']
    title = data['title']
    text = data['text']
    published = data['published']
    community = data['community']
    new_post = {
        'postid': postid,
        'title': title,
        'text': text,
        'published': published,
        'community': community
    }
    posts.append(new_post)
return {'message': "Post created successfully!", 'status': 202}

At this point the test should run, but it would result in AssertionError. That is because you have two problem in the implementation of recent_community_posts() method.

  1. You're extracting query parameters in the first two lines and you pass the "community" and the "top" as path parameters. so if community will always return false unless you pass it as a parameter in the url (/api/v2/post/retrieve/tech/2?community=tech&top=2) which is redundant.

  2. You're using jsonify inside the dictionary in the return statement like this:

    return {'data': jsonify(filtered_posts[:top]), 'message': "Filtered data based on community", 'status': 203}

jsonify returns a flask.wrappers.Response object which is not JSON serializable. you can replace it with the following

return jsonify({
    'data': filtered_posts[:top],
    'message': "Filtered data based on community",
    'status': 203
})

Upvotes: 1

joao
joao

Reputation: 2293

In your test_retrieve_recent_posts_existing_community function, you're doing

posts_json = json.dumps(posts_data)

Now json.dumps takes a python object (dicts and lists) and turns it into a string, which is the textual representation in JSON. But that is already what you have in your file. Just do this instead:

with open('posts.json') as posts_data:
    posts_json = posts_data.read()

and again, in the client.post call, just do

data=posts_json

no need for json.dump.

Upvotes: 0

Rafael Valero
Rafael Valero

Reputation: 2816

To upload the body of the request:

with open('posts.json') as posts_data:
    posts_json = json.load(posts_data)

Which output should be a list:

print(type(posts_json))
list

As to post the body should by dumped:

data=json.dumps(posts_json)

Which is a string:

Out[9]: '[{"postid": 2, "title": "Heading 2", "text": "Content of post 2", "published": "YYYY/MM/DD hh:mm:ss", "community": "tech"}, {"postid": 3, "title": "Heading 3", "text": "Content of post 3", "published": "YYYY/MM/DD hh:mm:ss", "community": "tech"}, {"postid": 4, "title": "Heading 4", "text": "Content of post 4", "published": "YYYY/MM/DD hh:mm:ss", "community": "art"}, {"postid": 5, "title": "Heading 5", "text": "Content of post 5", "published": "YYYY/MM/DD hh:mm:ss", "community": "art"}, {"postid": 6, "title": "Heading 6", "text": "Content of post 6", "published": "YYYY/MM/DD hh:mm:ss", "community": "science"}, {"postid": 7, "title": "Heading 7", "text": "Content of post 7", "published": "YYYY/MM/DD hh:mm:ss", "community": "tech"}]'

I think you have posts_json dumped twice. Could you try with only once?. Please.

def test_retrieve_recent_posts_existing_community(client):
    with open('posts.json') as posts_data:
        posts_json = json.load(posts_data)
    #SMALL CHANGE BELOW
    client.post('/api/v2/post/create', data=json.dumps(posts_json), content_type='application/json')
    response = client.get('/api/v2/post/retrieve/tech/2')
    data = json.load(response.get_data(as_text=True))
    assert "Filtered data" in data["message"] and data["status"] == 203

Upvotes: 0

Related Questions