Skip to content

Commit 9f69abf

Browse files
authored
Merge pull request #5 from Perafan18/feature/v2-api-versioning
feat: Add API versioning for v2
2 parents e137378 + 2dc8c46 commit 9f69abf

4 files changed

Lines changed: 104 additions & 65 deletions

File tree

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ gem 'mongoid', '~> 7.0.5'
88
gem 'rackup', '~> 2.1.0'
99
gem 'rspec', '~> 3.10'
1010
gem 'sinatra', '~> 4.0.0'
11+
gem 'sinatra-contrib', '~> 4.0.0'
1112

1213
# Development & Testing
1314
group :development, :test do

Gemfile.lock

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ GEM
2727
mongoid (7.0.13)
2828
activemodel (>= 5.1, < 6.2)
2929
mongo (>= 2.7.0, < 3.0.0)
30+
multi_json (1.17.0)
3031
mustermann (3.0.0)
3132
ruby2_keywords (~> 0.0.1)
3233
parallel (1.27.0)
@@ -102,6 +103,12 @@ GEM
102103
rack-protection (= 4.0.0)
103104
rack-session (>= 2.0.0, < 3)
104105
tilt (~> 2.0)
106+
sinatra-contrib (4.0.0)
107+
multi_json (>= 0.0.2)
108+
mustermann (~> 3.0)
109+
rack-protection (= 4.0.0)
110+
sinatra (= 4.0.0)
111+
tilt (~> 2.0)
105112
tilt (2.3.0)
106113
tzinfo (2.0.6)
107114
concurrent-ruby (~> 1.0)
@@ -125,6 +132,7 @@ DEPENDENCIES
125132
rubocop-rspec (~> 2.25)
126133
simplecov (~> 0.22)
127134
sinatra (~> 4.0.0)
135+
sinatra-contrib (~> 4.0.0)
128136

129137
RUBY VERSION
130138
ruby 3.2.2p53

main.rb

Lines changed: 65 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require 'sinatra'
4+
require 'sinatra/namespace'
45
require 'json'
56
require 'mongoid'
67
require 'dotenv/load'
@@ -18,66 +19,73 @@
1819
'Hello to ChainForge!'
1920
end
2021

21-
post '/chain' do
22-
blockchain = Blockchain.create
23-
blockchain.save!
24-
{ id: blockchain.id }.to_json
25-
end
26-
27-
post '/chain/:id/block' do
28-
block_data = parse_json_body
29-
chain_id = params[:id]
30-
blockchain = find_block_chain(chain_id)
31-
difficulty = validate_difficulty(block_data['difficulty'])
32-
block = blockchain.add_block(block_data['data'], difficulty: difficulty)
33-
34-
{
35-
chain_id: chain_id,
36-
block_id: block.id.to_s,
37-
block_hash: block._hash,
38-
nonce: block.nonce,
39-
difficulty: block.difficulty
40-
}.to_json
41-
end
22+
# API v1
23+
namespace '/api/v1' do
24+
before do
25+
content_type :json
26+
end
4227

43-
post '/chain/:id/block/:block_id/valid' do
44-
block_data = parse_json_body
45-
chain_id = params[:id]
46-
block_id = params[:block_id]
47-
blockchain = find_block_chain(chain_id)
48-
block = blockchain.blocks.find(block_id)
49-
raise 'Block not found' unless block
50-
51-
valid = block.valid_data?(block_data['data'])
52-
53-
{
54-
chain_id: chain_id,
55-
block_id: block.id.to_s,
56-
valid: valid
57-
}.to_json
58-
end
28+
post '/chain' do
29+
blockchain = Blockchain.create
30+
blockchain.save!
31+
{ id: blockchain.id }.to_json
32+
end
5933

60-
get '/chain/:id/block/:block_id' do
61-
chain_id = params[:id]
62-
block_id = params[:block_id]
63-
blockchain = find_block_chain(chain_id)
64-
block = blockchain.blocks.find(block_id)
65-
raise 'Block not found' unless block
66-
67-
{
68-
chain_id: chain_id,
69-
block: {
70-
id: block.id.to_s,
71-
index: block.index,
72-
data: block.data,
73-
hash: block._hash,
74-
previous_hash: block.previous_hash,
34+
post '/chain/:id/block' do
35+
block_data = parse_json_body
36+
chain_id = params[:id]
37+
blockchain = find_block_chain(chain_id)
38+
difficulty = validate_difficulty(block_data['difficulty'])
39+
block = blockchain.add_block(block_data['data'], difficulty: difficulty)
40+
41+
{
42+
chain_id: chain_id,
43+
block_id: block.id.to_s,
44+
block_hash: block._hash,
7545
nonce: block.nonce,
76-
difficulty: block.difficulty,
77-
timestamp: block.created_at.to_i,
78-
valid_hash: block.valid_hash?
79-
}
80-
}.to_json
46+
difficulty: block.difficulty
47+
}.to_json
48+
end
49+
50+
post '/chain/:id/block/:block_id/valid' do
51+
block_data = parse_json_body
52+
chain_id = params[:id]
53+
block_id = params[:block_id]
54+
blockchain = find_block_chain(chain_id)
55+
block = blockchain.blocks.find(block_id)
56+
raise 'Block not found' unless block
57+
58+
valid = block.valid_data?(block_data['data'])
59+
60+
{
61+
chain_id: chain_id,
62+
block_id: block.id.to_s,
63+
valid: valid
64+
}.to_json
65+
end
66+
67+
get '/chain/:id/block/:block_id' do
68+
chain_id = params[:id]
69+
block_id = params[:block_id]
70+
blockchain = find_block_chain(chain_id)
71+
block = blockchain.blocks.find(block_id)
72+
raise 'Block not found' unless block
73+
74+
{
75+
chain_id: chain_id,
76+
block: {
77+
id: block.id.to_s,
78+
index: block.index,
79+
data: block.data,
80+
hash: block._hash,
81+
previous_hash: block.previous_hash,
82+
nonce: block.nonce,
83+
difficulty: block.difficulty,
84+
timestamp: block.created_at.to_i,
85+
valid_hash: block.valid_hash?
86+
}
87+
}.to_json
88+
end
8189
end
8290

8391
helpers do

spec/api_spec.rb

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ def app
1919
end
2020
end
2121

22-
describe 'POST /chain' do
22+
describe 'POST /api/v1/chain' do
2323
it 'creates a new blockchain' do
24-
post '/chain'
24+
post '/api/v1/chain'
2525
expect(last_response).to be_ok
2626
expect(last_response.content_type).to include('application/json')
2727

@@ -31,12 +31,12 @@ def app
3131
end
3232
end
3333

34-
describe 'POST /chain/:id/block' do
34+
describe 'POST /api/v1/chain/:id/block' do
3535
let(:blockchain) { Blockchain.create! }
3636
let(:block_data) { { data: 'Test Block Data' } }
3737

3838
it 'adds a new block to the blockchain' do
39-
post "/chain/#{blockchain.id}/block", block_data.to_json, { 'CONTENT_TYPE' => 'application/json' }
39+
post "/api/v1/chain/#{blockchain.id}/block", block_data.to_json, { 'CONTENT_TYPE' => 'application/json' }
4040

4141
expect(last_response).to be_ok
4242
expect(last_response.content_type).to include('application/json')
@@ -45,21 +45,23 @@ def app
4545
expect(json['chain_id']).to eq(blockchain.id.to_s)
4646
expect(json['block_id']).not_to be_nil
4747
expect(json['block_hash']).not_to be_nil
48+
expect(json['nonce']).not_to be_nil
49+
expect(json['difficulty']).to eq(2)
4850
end
4951

5052
it 'returns error when chain not found' do
51-
post '/chain/invalid_id/block', block_data.to_json, { 'CONTENT_TYPE' => 'application/json' }
53+
post '/api/v1/chain/invalid_id/block', block_data.to_json, { 'CONTENT_TYPE' => 'application/json' }
5254
expect(last_response.status).to eq(500) # rubocop:disable RSpecRails/HaveHttpStatus
5355
end
5456
end
5557

56-
describe 'POST /chain/:id/block/:block_id/valid' do
58+
describe 'POST /api/v1/chain/:id/block/:block_id/valid' do
5759
let(:blockchain) { Blockchain.create! }
5860
let(:block_data) { { data: 'Validation Test Data' } }
5961
let!(:block) { blockchain.add_block(block_data[:data]) }
6062

6163
it 'validates block with correct data' do
62-
post "/chain/#{blockchain.id}/block/#{block.id}/valid",
64+
post "/api/v1/chain/#{blockchain.id}/block/#{block.id}/valid",
6365
block_data.to_json,
6466
{ 'CONTENT_TYPE' => 'application/json' }
6567

@@ -70,7 +72,7 @@ def app
7072

7173
it 'invalidates block with incorrect data' do
7274
invalid_data = { data: 'Wrong Data' }
73-
post "/chain/#{blockchain.id}/block/#{block.id}/valid",
75+
post "/api/v1/chain/#{blockchain.id}/block/#{block.id}/valid",
7476
invalid_data.to_json,
7577
{ 'CONTENT_TYPE' => 'application/json' }
7678

@@ -79,4 +81,24 @@ def app
7981
expect(json['valid']).to be false
8082
end
8183
end
84+
85+
describe 'GET /api/v1/chain/:id/block/:block_id' do
86+
let(:blockchain) { Blockchain.create! }
87+
let(:block_data) { 'GET Block Test Data' }
88+
let!(:block) { blockchain.add_block(block_data) }
89+
90+
it 'retrieves block details with mining information' do
91+
get "/api/v1/chain/#{blockchain.id}/block/#{block.id}"
92+
93+
expect(last_response).to be_ok
94+
json = JSON.parse(last_response.body)
95+
96+
expect(json['chain_id']).to eq(blockchain.id.to_s)
97+
expect(json['block']['id']).to eq(block.id.to_s)
98+
expect(json['block']['data']).to eq(block_data)
99+
expect(json['block']['nonce']).not_to be_nil
100+
expect(json['block']['difficulty']).to eq(2)
101+
expect(json['block']['valid_hash']).to be true
102+
end
103+
end
82104
end

0 commit comments

Comments
 (0)