Error in GET request for Central API

Hi All!!!

Apologies, this is my first time posting here. If I have not gone about it the right way please tell me! I have been following the documentation for setting up ODK central. I have tried to follow all steps exactly using these guidelines. I set up a server on digital ocean and I have my domain name.

I have been following the developer documentation on how to use the API. Below is the python script that I have been using:

import os
import requests
import json
import urllib
username=os.environ["odk_username"]
password=os.environ["odk_password"]

base_url="https://MY_WEBSITE"
values ={"email":username,"password":password}
headers = {
    'Content-Type': 'application/json'
}
request=requests.get(url=base_url,
                    data=values,
                    headers=headers)
print(request.text)

I get this error (along with some other HTML):
"We're sorry but ODK Central doesn't work properly without JavaScript enabled. Please enable it to continue."

I really apologize but I have been trying for days and I cannot understand the problem!

Welcome, @lgorman, and thanks for taking the time to introduce yourself!

When you hit your website directly, you are accessing the Central frontend and indeed, it needs JavaScript. What you want to do is to access the backend by adding /v1/ between your base url and the endpoint you want to access.

1 Like

@LN Thank you so so much! Do I also need to add /Session/. I am seeing this in the documentation now!

Apologies for the daft questions!

Yes, every API call you make needs to specify what you will interact with. That’s what I was referring to as an “endpoint”. When you look at the docs, you should be able to use a drop down on the examples to get Python code.

1 Like

Thanks again @LN , and can I say thank you to everyone who worked on the documentation, it is really fantastic.

This has solved the initial error but I am getting a newer one:

{"message":"Could not parse the given data (56 chars) as json.","code":400.1,"details": 
{"format":"json","rawLength":56}}

I will post the solution if I manage to find one!

Hi @lgorman! My knowledge of Python is limited, but I'm guessing that values is being converted to a string representation rather than to JSON. From a brief look into this, it seems that one option is to specify a json parameter (json=values) rather than a data parameter. Apparently an added benefit of this approach is that you don't need to specify the Content-Type header.

Hope this helps!

2 Likes

Hi @Matthew_White! Thanks for your responses :slight_smile: Unfortunately now I get the following error message:

{"message":"Could not find the resource you were looking for.","code":404.1}

Which I do no understand as my login credentials are correct and I have modified the URL as describes above (i.e https://lgormanodk.co.uk/v1/sessions). Is there something I am missing? I can log into the website no problem and create app-users etc... I just don't seem to be able to connect with the API.

Sorry to bother you with the questions!

One thing that's interesting about Central is that the management website simply uses the API: the frontend has no privileged access to the data. That means that if you're able to log into the website, that's a sign that the API is working correctly. I think the next step is making sure the API request is formulated correctly.

I'm noticing in your first post that you specify requests.get(). However, to create a session, you need to send a POST request. Does it help if you specify POST instead of GET?

2 Likes

@Matthew_White @LN
Thank you so much for this! I am new to all this stuff and you both massively helped me!!! Hopefully one day I'll be able to help with something!

One thing to note is that in the API documentation, the python code uses urllib2. I am told the urllib2 is a "poisoned package" and that it is easier to use the requests library (see here).

For anyone who encounters the same issue here is the code that worked for me:

import os
import requests


# need to ensure that these variables are set in your environment
username=os.environ["odk_username"]
password=os.environ["odk_password"]

base_url="https://MYDOMAIN.co.uk/v1/sessions"
values ={"email":username,"password":password}

headers = {'Content-Type': 'application/json'}
request=requests.post(url=base_url,json=values,headers=headers)
print(request.text)

Once again thanks for the help everyone!

1 Like

Just to give you something to start from, here's a quick script that lists projects and app_users.

#!/usr/bin/env python3

import requests
import json

central_url = "https://central.example.org"
central_email = "myemail@email.com"
central_password = "mypassword"


def get_email_token():

    email_token_response = requests.post(
        central_url + "/v1/sessions",
        data=json.dumps({"email": central_email, "password": central_password}),
        headers={"Content-Type": "application/json"},
    )

    if email_token_response.status_code == 200:
        return email_token_response.json()["token"]


def list_app_users(email_token, central_project_id):

    app_users_response = requests.get(
        central_url + "/v1/projects/" + str(central_project_id) + "/app-users",
        headers={"Authorization": "Bearer " + email_token},
    )

    app_users = {}
    for app_user in app_users_response.json():
        app_users[app_user["id"]] = app_user["displayName"]
    return app_users


def list_projects(email_token):

    projects_response = requests.get(
        central_url + "/v1/projects/",
        headers={"Authorization": "Bearer " + email_token},
    )

    projects = {}
    for project in projects_response.json():
        projects[project["id"]] = project["name"]

    return projects


email_token = get_email_token()
if email_token:
    projects = list_projects(email_token)
    for project_id, project_name in projects.items():
        print("- Project", project_id, "- " + project_name)
        app_users = list_app_users(email_token, project_id)
        for app_user_id, app_user_name in app_users.items():
            print("  - App user", app_user_id, "- " + app_user_name)
else:
    print("Error getting email token")
3 Likes

Hi @yanokwa! Thank you so so much, this is fantastic! Sorry for the daft questions in the first place!

1 Like