Try to get redmine API key with ruby

Background

I was asked to be able to get the API key of redmine using ruby at work. I saw the completion for the time being, so I will summarize it instead of my memorandum.   The article was only created by a beginner programmer in the first year of working life who got a job as a programmer just by studying by himself, by repeating search and copy. If there is a person like me, I hope that this article alone will be a success.

Method

I searched for it first, but couldn't find the corresponding description. It is assumed that the API key is received from the personal setting screen of redmine and entered in the code, so I could not find a way to get the API key programmatically. However, as a result of searching excluding the condition of ruby, I found the following article.

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

This article describes how to get the API key using JavaScript. Roughly speaking, it's a method using scraping.

Scraping practice

I see, scraping? Well, I'm ashamed to say that I didn't even know what scraping was. After all, I'm a beginner who just studied ruby by myself. So, I searched for a way to scrape 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

Well, when I actually move it, it doesn't move. As a result of various investigations, I noticed that it stopped at the login screen.

That means you have to create a login process

Login process creation

So, as usual, I arrived at this page by searching.

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

I understand it 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

Only the additional part was extracted.

By the way, regarding # {adress}, "I am testing locally now, but after that I will also run it on the company redmine and the client redmine."

require 'yaml' #Used to read the config

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

You can specify it by entering the address in config.yml like this.

result

> <pre>[APIkey]</pre>

This is no good.

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

Immediately after adding this, the process of acquiring the API key is completed for the time being.

What if you are logged in?

Actually, I was working on this first, but what if it wasn't the login screen? I don't know if that happens, but if it does, it will cause an error in the process of logging in.

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 I corresponded to this.

A class called RedmineContoroller is created here, and 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". As a result of checking, all the top pages etc. are "[company name] redmine". In other words, it seems to be a style of "Personal settings- [Page name]". Let's deal with it.

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

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

It's like this. In other words, it is a strategy to grasp the top title first. Now, let's publish the class "RedmineController". Yes, if I don't introduce the toptitle method, it will be troublesome twice, so I introduced it.

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

After that, it is necessary to create a process to do something using the API, so 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 == "Personal Settings- #{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, I am now adding methods to call various APIs to this getter class.

The whole 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 the config
require 'io/console' #Used for the words of the pasoward

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 == "Personal Settings- #{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


#Processing starts from here. Actually receive information by get or post with sinatra
puts "Please enter your account name"
mail = gets
mail.chomp!
puts "Please enter the password (not displayed but you can enter it)"
password = STDIN.noecho(&:gets)
password.chomp!
#Actually, it is not acquired using gets, but it is set for the time being. Eventually all this range disappears

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}"

Excuse me for your disgrace.

Recommended Posts

Try to get redmine API key with ruby
Try to link Ruby and Java with Dapr
[swift5] Try to make an API client with various methods
What I was addicted to with the Redmine REST API
Now is the time to get started with the Stream API
[Ruby on Rails] Read try (: [],: key)
How to get started with slim
Try to implement Yubaba in Ruby
Memo to get with Struts2 + Ajax
I get a Ruby version error when I try to start Rails.
Add foreign key to column with migrate
I tried to get started with WebAssembly
Rubocop gets angry when I try to fill zeros (fill 0s) with Ruby
[Note] How to get started with Rspec
Try to issue or get a card from Jave to Trello using API
Try to implement login function with Spring-Boot
Get started with "Introduction to Practical Rust Programming" (Day 4) Call Rust from Ruby
Android development-WEB access (GET) Try to get data by communicating with the outside. ~
How to delete data with foreign key
Use MyBatis to get Map with key as identifier and value as Entity
Convert JSON to TSV and TSV to JSON with Ruby
[Ruby] Basic key to be strong in refactoring
Try to implement login function with Spring Boot
How to use Java API with lambda expression
[Java] Get images with Google Custom Search API
How to get started with Eclipse Micro Profile
AtCoder ABC127 D hash to solve with Ruby 2.7.1
Get data with api created by curl command
How to build API with GraphQL and Rails
Try to implement TCP / IP + NIO with JAVA
Rails beginners tried to get started with RSpec
Try to automate migration with Spring Boot Flyway
How to get resource files out with spring-boot
Try to display hello world with spring + gradle
Getting Started with Doma-Introduction to the Criteria API
Try to summarize the common layout with rails
I want to get the value in Ruby
Introduction to Ruby 2
ruby API problem
ruby API problem
Try Ruby Minitest
I tried what I wanted to try with Stream softly.
How to make LINE messaging function made with Ruby
Rails / Ruby: How to get HTML text for Mail
Converting TSV files to CSV files (with BOM) in Ruby
I tried to get started with Spring Data JPA
A story about trying to get along with Mockito
Where to get lost in specifications with Spark routing
I tried to draw animation with Blazor + canvas API
Try to implement using Rakuten product search API (easy)
Get started with "Introduction to Practical Rust Programming" (Day 3)
Try connecting to AzureCosmosDB Emulator for Docker with Java
Get S3 object size with AWS SDK for Ruby
Try to work with Keycloak using Spring Security SAML (Spring 5)
How to get jdk etc from oracle with cli