42API Projects

over 1 year

2023 03

In this tutorial we will explore how to fetch information from 42API using Ruby.

We just launched an easter Hackathon, for that you will need to use 42API and fetch data from there!
In this tutorial you will learn how to integrate 42API into your project, or download the data to your computer and later parse it as you want.

We are lucky to have an API available, which is made possible by 42dev team, so shoutout to them! 😊
[Few schools make that possible]
❹❷ 🫶

What is even an API?

APIs enable a commmunication mechanism using defined protocols in order to interact with the application.
Okay okay, that is a very wordy and rigurous explanation, in simple words; an API is a tool that helps you interact with an application.

For example, let's say you want to create a bot for your Discord Server, then you can use Discord's API to automate your bot answers and process the input.
Another example, which is the case for this project. You want to know 42 projects and you want to retrieve all the projects that a certain user already compleated.

API stands for Application Programming Interface. In the context of APIs, the word Application refers to any software with a distinct function. Interface can be thought of as a contract of service between two applications. This contract defines how the two communicate with each other using requests and responses. Their API documentation contains information on how developers are to structure those requests and responses. "https://aws.amazon.com/what-is/api/"

42API is a RESTful kind of API and it uses JSON over HTTPS.
What are REST APIs and what does it mean JSON over HTTPS???

HTTPS is a secure protocole, and JSON is an acronym for JS Object Notation, it is a format pattern to exchange data.

REST stands for Representational State Transfer. REST defines a set of functions like GET, PUT, DELETE, etc. that clients can use to access server data. Clients and servers exchange data using HTTP.

We only need to use GET, because we will only fetch data from 42, we don't want to update data create or delete any data.

Setup

42 protects the data and it is not supposed to be open to the public, that is why you must generate some credentials to access 42API. You can get your credentials on intra.
Go to settings ⚙️ → API → REGISTER NEW APP → fill the form → Submit.

How to fill the FORM:
  • Name: Choose whatever you want
  • Image: [Optional] If you want you can choose one
  • Description: The description about your project and why you need 42API
  • Website: [Optional] You can put a link for other people to visit your app
  • Redirect URI: You don't need it now for this example, just put https://profile.intra.42.fr or something like that.
  • Scopes: Don't change it.

Then you wil have access to the UID and SECRET, you need this two strings to access the API.
[NEVER EVER EVER EVER SHARE YOUR SECRET OR PUBLISH IT TO GITHUB!]

You also need to install ruby and oauth2
Most OS's have ruby preinstalled ;)

Once you've installed ruby just do: 
gem install oauth2

GET all common-core projects from 42API

42 API


We must use the Endpoint /v2/cursus/:cursus_id/projects. It will return exactly what we want, but there is something we must concider first...
What is the :cursus_id? And why does it have ":"? When you see that format it means that you must replace that with the actuall id, of course the cursus id. So we should find out what is the common core cursus_id so we can retrieve all projects from there.

You will find that there is a section on the API cursus and there we can consult the 42 common core id, the name might change but it's common sense to find the correct id.

RUBY


Tested with ruby versions:
  • ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin20]
  • ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-darwin20]

cursus.rb

require "oauth2"

puts "What is your UID?"
UID = gets.chomp
puts "What is your SECRET?"
SECRET = gets.chomp

client = OAuth2::Client.new(UID, SECRET, site: "https://api.intra.42.fr")
if (not client)
        puts "Sorry, could't resolve any client with those credentials"
end

token = client.client_credentials.get_token
puts "Generated the token 🔑, now fetching v2/cursus"

response = token.get("/v2/cursus")
puts "Reponse Status: #{response.status}"
# => 200 SUCCESS
puts response.parsed

Execute the script:

ruby cursus.rb

JSON 
{"id"=>67, "created_at"=>"2023-03-22T07:11:41.847Z", "name"=>"42 events", "slug"=>"42-events", "kind"=>"external"}
{"id"=>66, "created_at"=>"2023-03-08T08:57:17.181Z", "name"=>"C-Piscine-Reloaded", "slug"=>"c-piscine-reloaded", "kind"=>"external"}
{"id"=>65, "created_at"=>"2022-12-20T13:37:48.590Z", "name"=>"C Piscine Antwerp", "slug"=>"c-piscine-antwerp", "kind"=>"piscine"}
{"id"=>64, "created_at"=>"2022-12-20T13:36:59.815Z", "name"=>"C Piscine Brussels", "slug"=>"c-piscine-brussels", "kind"=>"piscine"}
{"id"=>63, "created_at"=>"2022-06-15T15:51:56.477Z", "name"=>"Discovery | Nice", "slug"=>"discovery-nice", "kind"=>"external"}
{"id"=>62, "created_at"=>"2022-04-22T05:21:02.828Z", "name"=>"Abu Dhabi", "slug"=>"abu-dhabi", "kind"=>"external"}
{"id"=>61, "created_at"=>"2022-04-11T09:23:58.639Z", "name"=>"CodingInActionLabs", "slug"=>"codinginactionlabs", "kind"=>"external"}
{"id"=>60, "created_at"=>"2022-03-16T13:54:08.992Z", "name"=>"42Cursus-WarmUp-SP", "slug"=>"42cursus-warmup-sp", "kind"=>"piscine_community"}
{"id"=>59, "created_at"=>"2022-03-14T17:33:32.942Z", "name"=>"Turbo 4.2", "slug"=>"turbo-4-2", "kind"=>"external"}
{"id"=>58, "created_at"=>"2022-03-02T12:43:51.471Z", "name"=>"Bootcamp Cybersecurity", "slug"=>"bootcamp-cybersecurity", "kind"=>"external"}
{"id"=>57, "created_at"=>"2022-02-18T21:15:10.672Z", "name"=>"42 Labs São Paulo", "slug"=>"42-labs-sao-paulo", "kind"=>"external"}
{"id"=>56, "created_at"=>"2021-12-22T00:58:23.589Z", "name"=>"Seoul Labs", "slug"=>"seoul-labs", "kind"=>"external"}
{"id"=>54, "created_at"=>"2021-10-21T19:02:56.177Z", "name"=>"NetworkTest", "slug"=>"networktest", "kind"=>"test"}
{"id"=>53, "created_at"=>"2021-10-20T07:14:44.406Z", "name"=>"42.zip", "slug"=>"42-zip", "kind"=>"main"}
{"id"=>52, "created_at"=>"2021-08-18T15:46:14.648Z", "name"=>"Basecamp Warm Up| Rio", "slug"=>"basecamp-warm-up-rio", "kind"=>"piscine_community"}
{"id"=>51, "created_at"=>"2021-08-16T21:50:53.100Z", "name"=>"Basecamp | Rio", "slug"=>"basecamp-rio", "kind"=>"piscine_community"}
{"id"=>50, "created_at"=>"2021-07-01T09:58:06.110Z", "name"=>"Road to", "slug"=>"road-to", "kind"=>"external"}
{"id"=>49, "created_at"=>"2021-04-15T05:09:11.919Z", "name"=>"Hive Basecamp", "slug"=>"hive-basecamp", "kind"=>"piscine_community"}
{"id"=>48, "created_at"=>"2021-04-06T04:53:28.009Z", "name"=>"problem-solving", "slug"=>"problem-solving", "kind"=>"external"}
{"id"=>47, "created_at"=>"2021-03-11T18:45:50.082Z", "name"=>"Bocom test", "slug"=>"bocom-test", "kind"=>"test"}
{"id"=>46, "created_at"=>"2021-02-13T19:34:26.027Z", "name"=>"GBRE", "slug"=>"gbre", "kind"=>"external"}
{"id"=>44, "created_at"=>"2021-01-21T15:03:04.380Z", "name"=>"Paris Basecamp", "slug"=>"paris-basecamp", "kind"=>"piscine_community"}
{"id"=>43, "created_at"=>"2021-01-11T16:38:46.008Z", "name"=>"Brussels Basecamp", "slug"=>"brussels-basecamp", "kind"=>"piscine_community"}
{"id"=>42, "created_at"=>"2021-01-06T16:09:07.027Z", "name"=>"Germany Basecamp", "slug"=>"germany-basecamp", "kind"=>"piscine_community"}
{"id"=>41, "created_at"=>"2021-01-04T20:55:03.543Z", "name"=>"Basecamp Warm Up Germany", "slug"=>"basecamp-warm-up-germany", "kind"=>"piscine_community"}
{"id"=>40, "created_at"=>"2021-01-04T20:54:25.011Z", "name"=>"test-gb-v1", "slug"=>"test-gb-v1", "kind"=>"test"}
{"id"=>39, "created_at"=>"2020-12-04T06:47:52.980Z", "name"=>"101", "slug"=>"101", "kind"=>"external"}
{"id"=>38, "created_at"=>"2020-10-19T20:19:23.526Z", "name"=>"Basecamp WarmUp", "slug"=>"basecamp-warmup", "kind"=>"piscine_community"}
{"id"=>37, "created_at"=>"2020-10-08T12:43:22.188Z", "name"=>"msk-test", "slug"=>"msk-test", "kind"=>"test"}
{"id"=>36, "created_at"=>"2020-09-28T20:18:44.438Z", "name"=>"Basecamp", "slug"=>"basecamp", "kind"=>"piscine_community"}

In this case I didn't find my school cursus, and that is because this fetch is paginated. This means that not all the cursus will be returned, only X elements will be returned, and you can provide a number of the page to see more results.

Usually that is mentioned in the documentation, and in this case it was, it says:

This resource is paginated by 30 items

Although you can also just handle the data and count the number of elements with something like:
puts "The number of cursus in this page is: #{response.parsed.length}"

Let's review the Parameters listed on the Cursus 42API Reference Guide.
Let's say I want to display 10 elements maximum and I want to see any page, then you should change the code to the following example:
response = token.get("/v2/cursus", params: {page: {number: page_number, size: max_elements}})


Here it is an interactive script which produces the video bellow:

require "oauth2"

puts "What is your UID?"
UID = gets.chomp
puts "What is your SECRET?"
SECRET = gets.chomp

client = OAuth2::Client.new(UID, SECRET, site: "https://api.intra.42.fr")

begin
	token = client.client_credentials.get_token
rescue
	puts "Sorry, could't resolve any client with those credentials"
	exit
else
	puts "Generated the token 🔑, now fetching v2/cursus"
end

puts "How many elements do you want to see for each page?"
max_elements = gets.chomp.to_i || 5

while (true)
	puts "Please input the page you want to see"
	page_number = gets.chomp.to_i
	begin
		response = token.get("/v2/cursus", params: {page: {number: page_number, size: max_elements}})
		puts "Reponse Status: #{response.status}"
	rescue
		puts "Sorry something went wrong with the API or params"
	else
		info = response.parsed.map{|cursus| [cursus["id"], cursus["name"]]}
		puts "\e[H\e[2J"
		puts " #  ID | NAME "
		puts "-------|------"
		info.each_with_index do |c, i|
			puts "[#{i + 1}] #{c.first} | #{c.last}"
		end
	end

	puts "The number of cursus for page #{page_number} is: #{response.parsed.length}"

end
Now that we can see any cursus ID we can combine that to retrieve any project we want.
You might be overwhelmed by the amount of information that one single. project returns, but there are tools that can help you visualize the data easy so you can fetch anything you want.

In ruby you can convert a list to json with the to_json method, and that's what I did after fetching the project from curssus_id = 1. I printed the json result and then I used this tool to view the json easy. https://jsonviewer.com/ 
Now you can fetch the projects for a cursus! That is a great start, also if you want you can download my script to generate and see all the code.

There is also a GET fetch which returns a USER projects that one is quite good for the portfolio hackathon I think. With what you´ve lernt you should be able to retrieve that one!