cpr-client 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +20 -0
- data/lib/cpr_client.rb +20 -0
- data/lib/cpr_client/client.rb +131 -0
- data/lib/cpr_client/errors.rb +19 -0
- data/lib/cpr_client/record.rb +63 -0
- data/lib/cpr_client/version.rb +3 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 370bb6e5adf2275a7517d5cd4e16a7a3238f6a8c
|
4
|
+
data.tar.gz: f343164a5508cfa3e94fd493fce9402c08c93de5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 23fabf0b46d29328714286fc0679d44eca2389d1310123c81e851e49a2cdf7978d40cee6119d667972a1804ef982b0750d9b3a9611c31f60c8b037adeca36f5f
|
7
|
+
data.tar.gz: a6920f3334801db2193dd894ec4f11f2db0e975cdc4ea376121b0be11b4e4e07b1356d0bd627a4b31376992da62e6c9a748e3adbb3c2ea841d1d13454d0c0424
|
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
CPR Client
|
2
|
+
===
|
3
|
+
|
4
|
+
A client for looking up people in the CPR registry (as a private company).
|
5
|
+
It is backed by HTTPClient.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
# The client is connecting to the demo environment
|
9
|
+
client = CPRClient.new(username, password, true)
|
10
|
+
record = client.lookup('0707614285')
|
11
|
+
|
12
|
+
if record.nil?
|
13
|
+
puts "The record was not found"
|
14
|
+
elsif record.protected?
|
15
|
+
puts "The record has name and address protection"
|
16
|
+
else
|
17
|
+
puts record[:adrnvn]
|
18
|
+
puts record[:adrnvn, :t]
|
19
|
+
end
|
20
|
+
```
|
data/lib/cpr_client.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'cpr_client/client'
|
2
|
+
require 'cpr_client/record'
|
3
|
+
require 'cpr_client/errors'
|
4
|
+
require 'cpr_client/version'
|
5
|
+
|
6
|
+
module CPRClient
|
7
|
+
|
8
|
+
ENDPOINT_DEMO = 'https://gctp-demo.cpr.dk/cpr-online-gctp/gctp'
|
9
|
+
ENDPOINT_PROD = 'https://gctp.cpr.dk/cpr-online-gctp/gctp'
|
10
|
+
|
11
|
+
# Returns a new Client.
|
12
|
+
#
|
13
|
+
# @param user your cpr username
|
14
|
+
# @param pass your current cpr password
|
15
|
+
# @param demo use demo endpoint
|
16
|
+
def self.new(user, pass, demo = false)
|
17
|
+
Client.new(user, pass, demo ? ENDPOINT_DEMO : ENDPOINT_PROD)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'httpclient'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'cpr_client/record'
|
4
|
+
require 'cpr_client/errors'
|
5
|
+
require 'cpr_client/version'
|
6
|
+
|
7
|
+
module CPRClient
|
8
|
+
|
9
|
+
class Client
|
10
|
+
|
11
|
+
# Returns a new Client.
|
12
|
+
#
|
13
|
+
# @param user your cpr username
|
14
|
+
# @param pass your current cpr password
|
15
|
+
# @param endpoint the full URI to the cpr gctp service
|
16
|
+
def initialize(user, pass, endpoint)
|
17
|
+
@user, @pass, @endpoint = user, pass, endpoint
|
18
|
+
@http = HTTPClient.new(
|
19
|
+
agent_name: "CPRClient/#{CPRClient::VERSION}",
|
20
|
+
default_header: { 'Content-Type' => 'text/xml' }
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a Record object or nil if the record could not be found.
|
25
|
+
# If the client is not logged in, a login is performed before a retry.
|
26
|
+
#
|
27
|
+
# The cpr parameter is stripped of any non-digits.
|
28
|
+
#
|
29
|
+
# @param cpr a string
|
30
|
+
# @return a #Record or nil if no record was found
|
31
|
+
def lookup(cpr)
|
32
|
+
xml_doc = post_auto_login(stamp_body(digits(cpr)))
|
33
|
+
case receipt_code(xml_doc)
|
34
|
+
when 0
|
35
|
+
Record.new(xml_doc)
|
36
|
+
when 172, 52
|
37
|
+
nil
|
38
|
+
else
|
39
|
+
raise ClientError, "Unexpected STAMP resp: #{xml_doc}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
alias_method :stamp, :lookup
|
44
|
+
|
45
|
+
# Performs login for client.
|
46
|
+
#
|
47
|
+
# @return true
|
48
|
+
# @raise LoginError if login failed
|
49
|
+
def login
|
50
|
+
code = receipt_code(post(login_body))
|
51
|
+
code == 900 or raise LoginError, code
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
# Returns the gctp status code of the given xml_doc.
|
57
|
+
#
|
58
|
+
# If a receipt is not preset then -1 is returned.
|
59
|
+
#
|
60
|
+
# @param xml_doc a Nokogiri::XML object
|
61
|
+
# @return a Fixnum gctp status code
|
62
|
+
def receipt_code(xml_doc)
|
63
|
+
node = xml_doc && xml_doc.at_css('Kvit')
|
64
|
+
node && node['v'] ? node['v'].to_i : -1
|
65
|
+
end
|
66
|
+
|
67
|
+
# Removes all non digit characters from string
|
68
|
+
def digits(arg)
|
69
|
+
arg.to_s.gsub(/\D+/,'')
|
70
|
+
end
|
71
|
+
|
72
|
+
# Posts xml to the server.
|
73
|
+
#
|
74
|
+
# A login is performed if the client is not logged in,
|
75
|
+
# or if the login is expired.
|
76
|
+
#
|
77
|
+
# @param body a string of xml
|
78
|
+
# @return a Nokogiri::XML object
|
79
|
+
# @raise ClientError if the response status was not 200
|
80
|
+
# @raise LoginError if login failed
|
81
|
+
def post_auto_login(body)
|
82
|
+
xml_doc = post(body)
|
83
|
+
xml_doc = login && post(body) if receipt_code(xml_doc) == 901
|
84
|
+
xml_doc
|
85
|
+
end
|
86
|
+
|
87
|
+
# Posts a request to the service with xml as content type.
|
88
|
+
#
|
89
|
+
# @param body a string of xml
|
90
|
+
# @return a Nokogiri::XML object
|
91
|
+
# @raise ClientError if the response status was not 200
|
92
|
+
def post(body)
|
93
|
+
resp = @http.post(@endpoint, body)
|
94
|
+
raise ClientError, 'Bad response' if resp.status != 200
|
95
|
+
Nokogiri::XML(resp.body)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def login_body
|
101
|
+
<<-DATA
|
102
|
+
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
|
103
|
+
<root xmlns="http://www.cpr.dk">
|
104
|
+
<Gctp v=”1.0”>
|
105
|
+
<Sik function="signon" userid="#{@user}" password="#{@pass}"/>
|
106
|
+
</Gctp>
|
107
|
+
</root>
|
108
|
+
DATA
|
109
|
+
end
|
110
|
+
|
111
|
+
def stamp_body(cpr)
|
112
|
+
<<-DATA
|
113
|
+
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
|
114
|
+
<root xmlns="http://www.cpr.dk">
|
115
|
+
<Gctp v="1.0">
|
116
|
+
<System r="CprSoeg">
|
117
|
+
<Service r="STAMP">
|
118
|
+
<CprServiceHeader r="STAMP">
|
119
|
+
<Key>
|
120
|
+
<Field r="PNR" v="#{cpr}"/>
|
121
|
+
</Key>
|
122
|
+
</CprServiceHeader>
|
123
|
+
</Service>
|
124
|
+
</System>
|
125
|
+
</Gctp>
|
126
|
+
</root>
|
127
|
+
DATA
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CPRClient
|
2
|
+
|
3
|
+
class ClientError < StandardError; end
|
4
|
+
|
5
|
+
class LoginError < ClientError
|
6
|
+
|
7
|
+
attr_reader :code
|
8
|
+
|
9
|
+
# Returns a new instance of LoginError.
|
10
|
+
#
|
11
|
+
# @param code login error code
|
12
|
+
def initialize(code)
|
13
|
+
@code = code
|
14
|
+
super("CPRClient login failed [#{code}]")
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module CPRClient
|
2
|
+
class Record
|
3
|
+
|
4
|
+
attr_reader :timestamp, :fields
|
5
|
+
|
6
|
+
# Returns a new Response.
|
7
|
+
#
|
8
|
+
# @param xml_doc a Nokogiri::XML object
|
9
|
+
def initialize(xml_doc)
|
10
|
+
@timestamp = xml_doc.at_css("CprServiceHeader[r='STAMP']")['ts']
|
11
|
+
@fields = extract_fields(xml_doc)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Gets the value of a field with the given name.
|
15
|
+
#
|
16
|
+
# @param name the name of the target field.
|
17
|
+
# @param value the name of the value attribute
|
18
|
+
def get(name, value = 'v')
|
19
|
+
field = @fields[name.to_s.upcase]
|
20
|
+
field[value.to_s.downcase] if field
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :[], :get
|
24
|
+
|
25
|
+
# Returns true if the person has name/address protection.
|
26
|
+
#
|
27
|
+
# @return true if protected, false otherwise
|
28
|
+
def protected?
|
29
|
+
get(:beskyt) == '1'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the birthday as Date.
|
33
|
+
#
|
34
|
+
# @return Date with date of birth
|
35
|
+
def birthday
|
36
|
+
Date.parse(get(:foeddato))
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the record's address if present.
|
40
|
+
#
|
41
|
+
# The address will be a string of the fields
|
42
|
+
# STADR, POSTNR and POSTNR's t attribute.
|
43
|
+
#
|
44
|
+
# Fx. Boulevarden 101,1 mf, 6800 Varde
|
45
|
+
#
|
46
|
+
# @return string with address or nil
|
47
|
+
def address
|
48
|
+
values = [ get(:stadr), get(:postnr), get(:postnr, :t) ]
|
49
|
+
'%s, %s %s' % values if values.all?
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def extract_fields(xml_doc)
|
55
|
+
Hash[xml_doc.css("Praes[r='STAMPNR'] Field").reduce([]) { |a, f|
|
56
|
+
attrs = Hash[f.keys.zip(f.values)]
|
57
|
+
key = attrs.delete('r')
|
58
|
+
attrs.empty? ? a : a << [key, attrs]
|
59
|
+
}]
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cpr-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Andersen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-02-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '10.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.8'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: nokogiri
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.6'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: httpclient
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.7'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.7'
|
69
|
+
description: Client for making requests to the CPR registry (as a private company)
|
70
|
+
email: michael@aion.dk
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- README.md
|
76
|
+
- lib/cpr_client.rb
|
77
|
+
- lib/cpr_client/client.rb
|
78
|
+
- lib/cpr_client/errors.rb
|
79
|
+
- lib/cpr_client/record.rb
|
80
|
+
- lib/cpr_client/version.rb
|
81
|
+
homepage: http://assemblyvoting.dk
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 2.4.5.1
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: CPR Client
|
105
|
+
test_files: []
|
106
|
+
has_rdoc:
|