In previous blogs (here & here), I talked about the Repository skeleton necessary and then about how to use REST-API to add questions and answers into Alfresco for the Trivia Game. In this third part, I will go through what would be needed to really play the game since that was the initial goal.

This time again, the first thing to do would be to define the target endpoint and setup my credentials so that I can exchange with Alfresco. No changes on the authentication method, I will continue to use the Basic authentication and I will of course use the same parent folder, which represents the quiz I prepared earlier (reminder: multiple parent folders = multiple quiz):

$ base_url="https://alf-trivia.dbi-services.com"
$ endpoint="${base_url}/alfresco"
$ folder_id="e6395354-d38e-489b-b112-3549b521b04c"
$ username="admin"
$ read -s -p "Please enter the password of the '${username}' user for '${endpoint}': " password
Please enter the password of the 'admin' user for 'https://alf-trivia.dbi-services.com/alfresco':
$
$ auth=$(echo -n ${username}:${password} | base64)
$

To be able to play the game, I will need to retrieve all the questions with all their associated answers and to do that, I will need to loop on all the children of the parent folder. Therefore, the next step is to retrieve the list of all Child Node IDs (“uuid“) that are present in the folder, i.e., the unique identifier of each question. For that purpose, I’m using the GET node’s children REST-API (listNodeChildren) that I used in the previous blog to retrieve the number of existing questions but this time, I will retrieve the list of IDs from it:

$ response=$(curl -k -s -X GET "${endpoint}/api/-default-/public/alfresco/versions/1/nodes/${folder_id}/children" \
>   -H "Authorization: Basic ${auth}" \
>   -H "Accept: application/json")
$
$ echo ${response} | jq
{
  "list": {
    "pagination": {
      "count": 8,
      "hasMoreItems": false,
      "totalItems": 8,
      "skipCount": 0,
      "maxItems": 100
    },
    "entries": [
      {
        "entry": {
          "createdAt": "2023-01-18T10:42:14.957+0000",
          "isFolder": true,
          ...
          "name": "Q1",
          "id": "d2afbdb6-2afb-4acc-85e7-61a800e96db3",
          "nodeType": "cm:folder",
          "parentId": "e6395354-d38e-489b-b112-3549b521b04c"
        }
      },
      ...
      {
        "entry": {
          "createdAt": "2023-11-03T09:38:45.786+0000",
          "isFolder": true,
          ...
          "name": "Q8",
          "id": "0b147c71-70fd-498e-9bf6-d40a738699fa",
          "nodeType": "cm:folder",
          "parentId": "e6395354-d38e-489b-b112-3549b521b04c"
        }
      }
    ]
  }
}
$
$ echo ${response} | jq -r ".list.entries[].entry.id"
d2afbdb6-2afb-4acc-85e7-61a800e96db3
c4655fb0-5eef-494c-8b1e-5f160ca53558
d53bb919-ce60-42a7-a4db-5c8e3c5bfdac
c4e9826a-a1f4-4bf7-9768-137205c01045
a45e1b98-5780-448e-b26d-a603f9b03a85
4218c4b7-0be1-4948-9ae6-50e1575d1185
46d78e31-c796-4839-bac8-e6d5a7ff5973
0b147c71-70fd-498e-9bf6-d40a738699fa
$

If I wanted to, I could just use the above single REST-API call to get the properties of all nodes (without retrieving their IDs first), by using “/nodes/${folder_id}/children?include=properties” instead of “/nodes/${folder_id}/children“. This would automatically include all the properties of the nodes and therefore I would see the aspect’s properties in this single command, for all the questions:

$ response=$(curl -k -s -X GET "${endpoint}/api/-default-/public/alfresco/versions/1/nodes/${folder_id}/children?include=properties" \
>   -H "Authorization: Basic ${auth}" \
>   -H "Accept: application/json")
$
$ echo ${response} | jq -r ".list.entries[].entry.properties"
{
  "cm:title": "Question 1",
  ...
}
...
{
  "cm:title": "Question 7",
  "dbi:dbi_question": "Is it possible to use Alfresco to play a Trivia Game?",
  "dbi:dbi_correct_answer": "Of course, Alfresco can do everything",
  "dbi:dbi_answer4": "I don't know",
  "dbi:dbi_answer3": "Of course, Alfresco can do everything",
  "dbi:dbi_answer2": "Only if using the Enterprise version...",
  "dbi:dbi_answer1": "Obviously not, it's just an ECM!",
  "cm:description": "Question #7 of the TriviaGame",
  "cm:taggable": [
    "6017bd2f-05d2-4828-9a1d-a418cf43a84e"
  ]
}
{
  "cm:title": "Question 8",
  "dbi:dbi_question": "Is this working as it should?",
  "dbi:dbi_correct_answer": "Of course...",
  "dbi:dbi_answer4": "Why do you ask me???",
  "dbi:dbi_answer3": "Definitively not",
  "dbi:dbi_answer2": "Maybe?",
  "dbi:dbi_answer1": "Of course...",
  "cm:description": "Question #8 of the TriviaGame",
  "cm:taggable": [
    "6017bd2f-05d2-4828-9a1d-a418cf43a84e"
  ]
}
$

The above query including the properties is probably the optimized way to retrieve the information, since it’s a single REST-API call and you have all you need there… But I couldn’t just complete the blog by using a single REST-API call, it would be too fast ;). Therefore, I will use the list of question IDs, going through them all, retrieving the question and all the possible answers for each one separately, so it can be displayed to the “player” to test his knowledge. For that purpose, I’m using the GET node REST-API (getNode):

$ node="0b147c71-70fd-498e-9bf6-d40a738699fa"
$ response=$(curl -k -s -X GET "${endpoint}/api/-default-/public/alfresco/versions/1/nodes/${node}" \
>   -H "Authorization: Basic ${auth}" \
>   -H "Accept: application/json")
$
$ echo ${response} | jq
{
  "entry": {
    "aspectNames": [
      "cm:titled",
      "cm:auditable",
      "dbi:dbi_trivia",
      "cm:taggable"
    ],
    "createdAt": "2023-11-03T09:38:45.786+0000",
    "isFolder": true,
    ...
    "name": "Q8",
    "id": "0b147c71-70fd-498e-9bf6-d40a738699fa",
    "nodeType": "cm:folder",
    "properties": {
      "cm:title": "Question 8",
      "dbi:dbi_question": "Is this working as it should?",
      "dbi:dbi_correct_answer": "Of course...",
      "dbi:dbi_answer4": "Why do you ask me???",
      "dbi:dbi_answer3": "Definitively not",
      "dbi:dbi_answer2": "Maybe?",
      "dbi:dbi_answer1": "Of course...",
      "cm:description": "Question #8 of the TriviaGame",
      "cm:taggable": [
        "6017bd2f-05d2-4828-9a1d-a418cf43a84e"
      ]
    },
    "parentId": "e6395354-d38e-489b-b112-3549b521b04c"
  }
}
$

From that point, it’s only a matter of display and verification, which is simple scripting not related to Alfresco, so I won’t go through it in details. The final content of the small bash script and its execution to play the game:

$ cat triviaPlay.sh
#!/bin/bash

# Define endpoint, credentials, and folder ID
base_url="https://alf-trivia.dbi-services.com"
endpoint="${base_url}/alfresco"
folder_id="e6395354-d38e-489b-b112-3549b521b04c"
username="admin"
read -s -p "Please enter the password of the '${username}' user for '${endpoint}': " password
auth=$(echo -n ${username}:${password} | base64)

# Get all nodes in the TriviaGame folder
echo
echo
echo "Fetching all existing questions from Alfresco..."
response=$(curl -k -s -X GET "${endpoint}/api/-default-/public/alfresco/versions/1/nodes/${folder_id}/children" \
  -H "Authorization: Basic ${auth}" \
  -H "Accept: application/json")
nodes=$(echo ${response} | jq -r ".list.entries[].entry.id")

# Iterate through all nodes
nb_correct=0
nb_incorrect=0
for node in ${nodes}; do
  # Get question, answers, and correct answer from node's custom aspect
  response=$(curl -k -s -X GET "${endpoint}/api/-default-/public/alfresco/versions/1/nodes/${node}" \
    -H "Authorization: Basic ${auth}" \
    -H "Accept: application/json")
  question=$(echo ${response} | jq -r '.entry.properties."dbi:dbi_question"')
  answer1=$(echo ${response} | jq -r '.entry.properties."dbi:dbi_answer1"')
  answer2=$(echo ${response} | jq -r '.entry.properties."dbi:dbi_answer2"')
  answer3=$(echo ${response} | jq -r '.entry.properties."dbi:dbi_answer3"')
  answer4=$(echo ${response} | jq -r '.entry.properties."dbi:dbi_answer4"')
  correct_answer=$(echo ${response} | jq -r '.entry.properties."dbi:dbi_correct_answer"')

  # Ask question and get user input
  echo
  echo -e "\033[4mQuestion #$((nb_correct+nb_incorrect+1)):\033[0m"
  echo "${question}"
  echo "  1) ${answer1}"
  echo "  2) ${answer2}"
  echo "  3) ${answer3}"
  echo "  4) ${answer4}"
  read -p "Please enter your answer (1, 2, 3 or 4): " user_answer
  answer=$(eval "echo \${answer$user_answer}")

  # Check if answer is correct
  if [[ "${answer}" == "${correct_answer}" ]]; then
    echo -e "\033[32;1m  --> Correct!\033[0m"
    nb_correct=$((nb_correct+1))
  else
    echo -e "\033[31m  --> Incorrect... The correct answer is: \033[31;1m${correct_answer}\033[31m.\033[0m"
    nb_incorrect=$((nb_incorrect+1))
  fi
done

# Print final score
echo
if [[ "${nb_incorrect}" == "0" ]]; then
  echo -e "\033[32;1m==> Congratulations, your final score is a perfect ${nb_correct}/$((nb_correct+nb_incorrect))!\033[0m"
else
  if [[ "${nb_correct}" -gt "${nb_incorrect}" ]]; then
    echo -e "\033[32;1m==> Your final score is an acceptable ${nb_correct}/$((nb_correct+nb_incorrect)). You can still do better!\033[0m"
  else
    echo -e "\033[31;1m==> Oops, your final score is ${nb_correct}/$((nb_correct+nb_incorrect))... You will do better next time!\033[0m"
  fi
fi
echo
$
$
$ # Execute the script to play the game
$ ./triviaPlay.sh
Please enter the password of the 'admin' user for 'https://alf-trivia.dbi-services.com/alfresco':

Fetching all existing questions from Alfresco...

Question #1:
What is the best ECM?
  1) Documentum
  2) Alfresco
  3) Nuxeo
  4) SharePoint (lol)
Please enter your answer (1, 2, 3 or 4): 2
  --> Correct!

Question #2:
Why?
  1) Because
  2) Because
  3) Because it is the best
  4) Because
Please enter your answer (1, 2, 3 or 4): 3
  --> Correct!

Question #3:
How can you interact with Alfresco REST-API?
  1) Using a browser
  2) Using a script (bash/python/java/etc)
  3) Using Postman
  4) All of the above
Please enter your answer (1, 2, 3 or 4): 4
  --> Correct!

Question #4:
What is the correct HTTP Verb to use to perform a search?
  1) POST
  2) GET
  3) PUT
  4) DELETE
Please enter your answer (1, 2, 3 or 4): 1
  --> Correct!

Question #5:
What is the correct URI to use to perform a search?
  1) /alfresco/api/search
  2) /alfresco/api/-default-/versions/1/search
  3) /alfresco/api/-default-/public/search
  4) /alfresco/api/-default-/public/search/versions/1/search
Please enter your answer (1, 2, 3 or 4): 4
  --> Correct!

Question #6:
How can you create a Node with content in a single API call?
  1) Using the content API and a multipart body
  2) Using the content API and a json body
  3) Using the children API and a multipart body
  4) Using the children API and a json body
Please enter your answer (1, 2, 3 or 4): 3
  --> Correct!

Question #7:
Is it possible to use Alfresco to play a Trivia Game?
  1) Obviously not, it's just an ECM!
  2) Only if using the Enterprise version...
  3) Of course, Alfresco can do everything
  4) I don't know
Please enter your answer (1, 2, 3 or 4): 3
  --> Correct!

Question #8:
Is this working as it should?
  1) Of course...
  2) Maybe?
  3) Definitively not
  4) Why do you ask me???
Please enter your answer (1, 2, 3 or 4): 1
  --> Correct!

==> Congratulations, your final score is a perfect 8/8!

$

I can also force some wrong answers, just to make sure it detects it properly:

$ ./triviaPlay.sh
...
Question #2:
Why?
  1) Because
  2) Because
  3) Because it is the best
  4) Because
Please enter your answer (1, 2, 3 or 4): 1
  --> Incorrect... The correct answer is: Because it is the best.
...

==> Your final score is an acceptable 7/8. You can still do better!

$
$
$ ./triviaPlay.sh
...
Question #8:
Is this working as it should?
  1) Of course...
  2) Maybe?
  3) Definitively not
  4) Why do you ask me???
Please enter your answer (1, 2, 3 or 4): 4
  --> Incorrect... The correct answer is: Of course....

==> Oops, your final score is 0/8... You will do better next time!

$

As mentioned earlier, you could simply use the first REST-API command to get all the details and then creating arrays using “JQ“, containing all metadata needed for the game. Both approaches are very easy to implement/script and give a funny ending to a presentation about Alfresco REST-API, so it was good enough for me! In case you missed them, the previous parts of this blog can be found here and here.