[Ruby] Get redmine API key with ruby

6 minute read

History

I was asked to get the API key of redmine by using ruby at work. Since I saw the completion for the time being, I will summarize it instead of my memorandum. ‥ The article was only made by a beginner programmer, a first-year adult who got a job as a programmer just by studying by himself, by repeating search and copy and paste. If there is a person like me, I hope this article alone will succeed this time.

Method

First, I searched and found that the corresponding description was not found. The API key is supposed to be received from redmine’s personal setting screen and entered in the code, so I could not find a way to get the API key programmatically at all. However, as a result of searching excluding the condition of ruby, I found the following article.

How to get various data from Redmine’s JavaScript https://qiita.com/forenoonM/items/7f42701b2ea40353a820

In this article, how to get API key using JavaScript is described. Roughly speaking, the scraping method is used.

Scraping practice

Indeed, scraping? Now, ashamedly, I didn’t even know scraping. Anyway, I’m just a beginner who studied ruby by himself. So, I searched for a scraping method with ruby and arrived at the following site.

[Understanding in 5 minutes] Explain the basics of scraping with Ruby! https://www.sejuku.net/blog/13230

This is the code created in this way

equire'open-uri'
require'nokogiri' # Used to get API key
url = "http://192.168.33.11/my/api_key"

charset = nil

html = open(url) do |page|
 charaset = page.charset
 page.read
end

contents = Nokogiri::HTML.parse(html,nil,charset)
apikey = contents.search("div[@class='box']").search('pre')
puts apikey

Now, when I actually move it, it doesn’t move. As a result of various investigations, I realized that it stopped at the login screen.

That means you have to create a login process

Create login process

Then, as a result of this search, this page was reached.

Login and scrape with Mechanize https://qiita.com/katsuyuki/items/1a78360988d96eec1d54

I understand completely.

agent = Mechanize.new
agent.user_agent ='Mac Safari'
agent.get("http://#{adress}/my/api_key") do |page|
  response = page.form_with(:action =>'/login') do |form|
    formdata = {
       :mail => mail,
       :password => password,
    }
    form.field_with(:name =>'username').value = formdata[:mail]
    form.field_with(:name =>'password').value = formdata[:password]
  end.submit
 end
 url = "http://#{adress}/my/api_key"
 html = agent.get("#{url}").content.toutf8
 contents = Nokogiri::HTML(html, nil,'utf-8')
 apikey = contents.search("div[@class='box']").search('pre')
 puts apikey

I extracted only the additional part.

By the way, regarding #{adress}, “I am testing it locally, but after this I will also work with the company redmine and the client redmine”

require'yaml' # used to read config

set = YAML.load_file("config.yml")
puts set
adress = set["train"]["adress"]
puts adress

It was designed so that you can specify it by entering the address in config.yml like this.

result

> <pre>[API key]</pre>

This is no good.

stringkey = "#{apikey}"
stringkey.slice!("</pre>")
stringkey.slice!("<pre>")
puts "APIKey = #{stringkey}"

Adding this immediately afterwards, the process for acquiring the API key is completed for the time being.

What if you are already logged in?

Actually, I was working on this first, but what if it was not the login screen? I don’t know if there is such a thing, but if there is, an error will occur in the process of logging in conversely.

page = RedmineController.new
pagetitle = page.Myaccount_access(adress)
puts "pagetitle = #{pagetitle}"

if pagetitle == "Personal Settings-redmine" then # Check if you are logged in
     apikey = page.GetAPIkey(adress)
     puts "APIKey = #{apikey}"
else
     apikey = page.Login(adress,mail,password)
end

The title of the personal settings screen after login was “Personal settings-redmine”, so this was supported.

A class called RedmineContoroller is created here, but this class will be described later.

After this, I tried it with the company redmine and noticed. Our company’s redmine is “personal settings-[company name] redmine”. I examined it and found that the top page etc. were all “[company name] redmine”. In other words, it looks like “Personalization-[page name]” style. Let’s correspond.

   page = RedmineController.new
   toptitle = page.toptitle(adress)
   pagetitle = page.Myaccount_access(adress)
   puts "pagetitle = #{pagetitle}"

   if pagetitle == "Personalization-#{toptitle}" then # Check if you are logged in
     apikey = page.GetAPIkey(adress)
     puts "APIKey = #{apikey}"
   else
     apikey = page.Login(adress,mail,password)
   end

This is it. In other words, it is a strategy to grasp the top title first. Now, let’s publish the class “RedmineController”. Yes, I have to introduce the toptitle method.

class RedmineController

 def toptitle(adress)
url = "http://#{adress}/"

charset = nil

html = open(url) do |page|
charaset = page.charset
page.read
end

contents = Nokogiri::HTML.parse(html,nil,charset)
 return contents.title
 end

 def Myaccount_access(adress)
url = "http://#{adress}/my/account"

charset = nil

html = open(url) do |page|
charaset = page.charset
page.read
end

contents = Nokogiri::HTML.parse(html,nil,charset)
 return contents.title
 end

 def Login(adress,mail,password)
agent = Mechanize.new
agent.user_agent ='Mac Safari'
agent.get("http://#{adress}/my/api_key") do |page|
response = page.form_with(:action =>'/login') do |form|
formdata = {
:mail => mail,
:password => password,
}
form.field_with(:name =>'username').value = formdata[:mail]
form.field_with(:name =>'password').value = formdata[:password]
end.submit
end
url = "http://#{adress}/my/api_key"
html = agent.get("#{url}").content.toutf8
         contents = Nokogiri::HTML(html, nil,'utf-8')
         apikey = contents.search("div[@class='box']").search('pre')
         stringkey = "#{apikey}"
stringkey.slice!("</pre>")
stringkey.slice!("<pre>")
puts "APIKey = #{stringkey}"
return stringkey
 end

 def GetAPIkey(adress)
        url = "http://#{adress}/my/api_key"

        charset = nil

        html = open(url) do |page|
         charaset = page.charset
         page.read
        end

        contents = Nokogiri::HTML.parse(html,nil,charset)apikey = contents.search("div[@class='box']").search('pre')
        stringkey = "#{apikey}"
stringkey.slice!("</pre>")
stringkey.slice!("<pre>")
puts "APIKey = #{stringkey}"
return stringkey
 end

end

and to completion

Then, since it is necessary to make some kind of process using the API after this, let’s push the process to get the API key into the method.

class Getter
 def APIKey_Getter(adress,mail,password)
   page = RedmineController.new
   toptitle = page.toptitle(adress)
   pagetitle = page.Myaccount_access(adress)
   puts "pagetitle = #{pagetitle}"

   if pagetitle == "Personalization-#{toptitle}" then # Check if you are logged in
     apikey = page.GetAPIkey(adress)
     puts "APIKey = #{apikey}"
   else
     apikey = page.Login(adress,mail,password)
   end
   return apikey
 end

By the way, after this, we are now adding methods that call various APIs to this getter class.

full picture

Completed with the above. The whole picture is like this.

require'open-uri'
require'nokogiri' # Used to get API key
require'mechanize' # use for login
require'yaml' # used to read config
require'io/console' # Used as a surrender for passoword

class RedmineController

 def toptitle(adress)
url = "http://#{adress}/"

charset = nil

html = open(url) do |page|
charaset = page.charset
page.read
end

contents = Nokogiri::HTML.parse(html,nil,charset)
 return contents.title
 end

 def Myaccount_access(adress)
url = "http://#{adress}/my/account"

charset = nil

html = open(url) do |page|
charaset = page.charset
page.read
end

contents = Nokogiri::HTML.parse(html,nil,charset)
 return contents.title
 end

 def Login(adress,mail,password)
agent = Mechanize.new
agent.user_agent ='Mac Safari'
agent.get("http://#{adress}/my/api_key") do |page|
response = page.form_with(:action =>'/login') do |form|
formdata = {
:mail => mail,
:password => password,
}
form.field_with(:name =>'username').value = formdata[:mail]
form.field_with(:name =>'password').value = formdata[:password]
end.submit
end
url = "http://#{adress}/my/api_key"
html = agent.get("#{url}").content.toutf8
         contents = Nokogiri::HTML(html, nil,'utf-8')
         apikey = contents.search("div[@class='box']").search('pre')
         stringkey = "#{apikey}"
stringkey.slice!("</pre>")
stringkey.slice!("<pre>")
puts "APIKey = #{stringkey}"
return stringkey
 end

 def GetAPIkey(adress)
        url = "http://#{adress}/my/api_key"

        charset = nil

        html = open(url) do |page|
         charaset = page.charset
         page.read
        end

        contents = Nokogiri::HTML.parse(html,nil,charset)
apikey = contents.search("div[@class='box']").search('pre')
        stringkey = "#{apikey}"
stringkey.slice!("</pre>")
stringkey.slice!("<pre>")
puts "APIKey = #{stringkey}"
return stringkey
 end
end

class Getter
 def APIKey_Getter(adress,mail,password)
   page = RedmineController.new
   toptitle = page.toptitle(adress)
   pagetitle = page.Myaccount_access(adress)
   puts "pagetitle = #{pagetitle}"

   if pagetitle == "Personalization-#{toptitle}" then # Check if you are logged in
     apikey = page.GetAPIkey(adress)
     puts "APIKey = #{apikey}"
   else
     apikey = page.Login(adress,mail,password)
   end
   return apikey
 end
end


# Start processing from here. Actually get information by get or post with sinatra
puts "Please enter your account name"
mail = gets
mail.chomp!
puts "Please enter the password (it is not displayed but you can enter it)"
password = STDIN.noecho(&:gets)
password.chomp!
# Actually, it is not acquired using gets, but set once. Eventually all this range will disappear

set = YAML.load_file("config.yml")
puts set
adress = set["train"]["adress"]
puts adress
f = Getter.new
apikey = f.APIKey_Getter(adress,mail,password)
puts "APIKey = #{apikey}"

I’m sorry for the inconvenience.