vapi-ruby 0.1.0
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/CHANGELOG.md +23 -0
- data/LICENSE.txt +21 -0
- data/README.md +305 -0
- data/lib/vapi/analytics.rb +9 -0
- data/lib/vapi/assistant.rb +25 -0
- data/lib/vapi/base.rb +11 -0
- data/lib/vapi/call.rb +25 -0
- data/lib/vapi/client.rb +90 -0
- data/lib/vapi/configuration.rb +16 -0
- data/lib/vapi/errors.rb +51 -0
- data/lib/vapi/file.rb +25 -0
- data/lib/vapi/knowledge_base.rb +25 -0
- data/lib/vapi/log.rb +9 -0
- data/lib/vapi/phone_number.rb +25 -0
- data/lib/vapi/squad.rb +25 -0
- data/lib/vapi/tool.rb +25 -0
- data/lib/vapi/version.rb +5 -0
- data/lib/vapi.rb +39 -0
- data/spec/examples.txt +60 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/vapi/analytics_spec.rb +21 -0
- data/spec/vapi/assistant_spec.rb +73 -0
- data/spec/vapi/call_spec.rb +81 -0
- data/spec/vapi/client_spec.rb +130 -0
- data/spec/vapi/configuration_spec.rb +36 -0
- data/spec/vapi/file_spec.rb +63 -0
- data/spec/vapi/knowledge_base_spec.rb +64 -0
- data/spec/vapi/log_spec.rb +29 -0
- data/spec/vapi/phone_number_spec.rb +64 -0
- data/spec/vapi/squad_spec.rb +64 -0
- data/spec/vapi/tool_spec.rb +64 -0
- metadata +190 -0
data/lib/vapi/tool.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vapi
|
|
4
|
+
class Tool < Base
|
|
5
|
+
def list(**params)
|
|
6
|
+
client.get("/tool", params)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def find(id)
|
|
10
|
+
client.get("/tool/#{id}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create(params)
|
|
14
|
+
client.post("/tool", params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update(id, params)
|
|
18
|
+
client.patch("/tool/#{id}", params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete(id)
|
|
22
|
+
client.delete("/tool/#{id}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/vapi/version.rb
ADDED
data/lib/vapi.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'httparty'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
require_relative 'vapi/version'
|
|
7
|
+
require_relative 'vapi/errors'
|
|
8
|
+
require_relative 'vapi/configuration'
|
|
9
|
+
require_relative 'vapi/client'
|
|
10
|
+
require_relative 'vapi/base'
|
|
11
|
+
require_relative 'vapi/assistant'
|
|
12
|
+
require_relative 'vapi/call'
|
|
13
|
+
require_relative 'vapi/phone_number'
|
|
14
|
+
require_relative 'vapi/squad'
|
|
15
|
+
require_relative 'vapi/tool'
|
|
16
|
+
require_relative 'vapi/file'
|
|
17
|
+
require_relative 'vapi/knowledge_base'
|
|
18
|
+
require_relative 'vapi/log'
|
|
19
|
+
require_relative 'vapi/analytics'
|
|
20
|
+
|
|
21
|
+
module Vapi
|
|
22
|
+
class << self
|
|
23
|
+
attr_accessor :configuration
|
|
24
|
+
|
|
25
|
+
def configure
|
|
26
|
+
self.configuration ||= Configuration.new
|
|
27
|
+
yield(configuration) if block_given?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def client
|
|
31
|
+
@client ||= Client.new(configuration)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def reset!
|
|
35
|
+
@client = nil
|
|
36
|
+
@configuration = nil
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/spec/examples.txt
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
example_id | status | run_time |
|
|
2
|
+
----------------------------------------- | ------ | --------------- |
|
|
3
|
+
./spec/vapi/analytics_spec.rb[1:1:1] | passed | 0.00027 seconds |
|
|
4
|
+
./spec/vapi/assistant_spec.rb[1:1:1] | passed | 0.00032 seconds |
|
|
5
|
+
./spec/vapi/assistant_spec.rb[1:1:2] | passed | 0.00038 seconds |
|
|
6
|
+
./spec/vapi/assistant_spec.rb[1:2:1] | passed | 0.0004 seconds |
|
|
7
|
+
./spec/vapi/assistant_spec.rb[1:3:1] | passed | 0.00027 seconds |
|
|
8
|
+
./spec/vapi/assistant_spec.rb[1:4:1] | passed | 0.00022 seconds |
|
|
9
|
+
./spec/vapi/assistant_spec.rb[1:5:1] | passed | 0.00021 seconds |
|
|
10
|
+
./spec/vapi/call_spec.rb[1:1:1] | passed | 0.0002 seconds |
|
|
11
|
+
./spec/vapi/call_spec.rb[1:1:2] | passed | 0.00038 seconds |
|
|
12
|
+
./spec/vapi/call_spec.rb[1:2:1] | passed | 0.00021 seconds |
|
|
13
|
+
./spec/vapi/call_spec.rb[1:3:1] | passed | 0.00037 seconds |
|
|
14
|
+
./spec/vapi/call_spec.rb[1:4:1] | passed | 0.00024 seconds |
|
|
15
|
+
./spec/vapi/call_spec.rb[1:5:1] | passed | 0.00027 seconds |
|
|
16
|
+
./spec/vapi/client_spec.rb[1:1:1] | passed | 0.00004 seconds |
|
|
17
|
+
./spec/vapi/client_spec.rb[1:1:2:1] | passed | 0.00004 seconds |
|
|
18
|
+
./spec/vapi/client_spec.rb[1:2:1] | passed | 0.00021 seconds |
|
|
19
|
+
./spec/vapi/client_spec.rb[1:2:2] | passed | 0.00029 seconds |
|
|
20
|
+
./spec/vapi/client_spec.rb[1:2:3] | passed | 0.00033 seconds |
|
|
21
|
+
./spec/vapi/client_spec.rb[1:3:1] | passed | 0.00032 seconds |
|
|
22
|
+
./spec/vapi/client_spec.rb[1:4:1] | passed | 0.00022 seconds |
|
|
23
|
+
./spec/vapi/client_spec.rb[1:5:1] | passed | 0.00019 seconds |
|
|
24
|
+
./spec/vapi/client_spec.rb[1:6:1] | passed | 0.00028 seconds |
|
|
25
|
+
./spec/vapi/client_spec.rb[1:6:2] | passed | 0.00033 seconds |
|
|
26
|
+
./spec/vapi/client_spec.rb[1:6:3] | passed | 0.00114 seconds |
|
|
27
|
+
./spec/vapi/client_spec.rb[1:6:4] | passed | 0.00034 seconds |
|
|
28
|
+
./spec/vapi/client_spec.rb[1:6:5] | passed | 0.0003 seconds |
|
|
29
|
+
./spec/vapi/configuration_spec.rb[1:1:1] | passed | 0.00004 seconds |
|
|
30
|
+
./spec/vapi/configuration_spec.rb[1:1:2] | passed | 0.00004 seconds |
|
|
31
|
+
./spec/vapi/configuration_spec.rb[1:2:1] | passed | 0.00004 seconds |
|
|
32
|
+
./spec/vapi/configuration_spec.rb[1:2:2] | passed | 0.00043 seconds |
|
|
33
|
+
./spec/vapi/configuration_spec.rb[1:2:3] | passed | 0.00003 seconds |
|
|
34
|
+
./spec/vapi/file_spec.rb[1:1:1] | passed | 0.0002 seconds |
|
|
35
|
+
./spec/vapi/file_spec.rb[1:2:1] | passed | 0.00019 seconds |
|
|
36
|
+
./spec/vapi/file_spec.rb[1:3:1] | passed | 0.00032 seconds |
|
|
37
|
+
./spec/vapi/file_spec.rb[1:4:1] | passed | 0.00032 seconds |
|
|
38
|
+
./spec/vapi/file_spec.rb[1:5:1] | passed | 0.00018 seconds |
|
|
39
|
+
./spec/vapi/knowledge_base_spec.rb[1:1:1] | passed | 0.00022 seconds |
|
|
40
|
+
./spec/vapi/knowledge_base_spec.rb[1:2:1] | passed | 0.00033 seconds |
|
|
41
|
+
./spec/vapi/knowledge_base_spec.rb[1:3:1] | passed | 0.00035 seconds |
|
|
42
|
+
./spec/vapi/knowledge_base_spec.rb[1:4:1] | passed | 0.00048 seconds |
|
|
43
|
+
./spec/vapi/knowledge_base_spec.rb[1:5:1] | passed | 0.0002 seconds |
|
|
44
|
+
./spec/vapi/log_spec.rb[1:1:1] | passed | 0.00024 seconds |
|
|
45
|
+
./spec/vapi/log_spec.rb[1:1:2] | passed | 0.00035 seconds |
|
|
46
|
+
./spec/vapi/phone_number_spec.rb[1:1:1] | passed | 0.0003 seconds |
|
|
47
|
+
./spec/vapi/phone_number_spec.rb[1:2:1] | passed | 0.00021 seconds |
|
|
48
|
+
./spec/vapi/phone_number_spec.rb[1:3:1] | passed | 0.00022 seconds |
|
|
49
|
+
./spec/vapi/phone_number_spec.rb[1:4:1] | passed | 0.00032 seconds |
|
|
50
|
+
./spec/vapi/phone_number_spec.rb[1:5:1] | passed | 0.0002 seconds |
|
|
51
|
+
./spec/vapi/squad_spec.rb[1:1:1] | passed | 0.00026 seconds |
|
|
52
|
+
./spec/vapi/squad_spec.rb[1:2:1] | passed | 0.0002 seconds |
|
|
53
|
+
./spec/vapi/squad_spec.rb[1:3:1] | passed | 0.00038 seconds |
|
|
54
|
+
./spec/vapi/squad_spec.rb[1:4:1] | passed | 0.00021 seconds |
|
|
55
|
+
./spec/vapi/squad_spec.rb[1:5:1] | passed | 0.00028 seconds |
|
|
56
|
+
./spec/vapi/tool_spec.rb[1:1:1] | passed | 0.02301 seconds |
|
|
57
|
+
./spec/vapi/tool_spec.rb[1:2:1] | passed | 0.00033 seconds |
|
|
58
|
+
./spec/vapi/tool_spec.rb[1:3:1] | passed | 0.00026 seconds |
|
|
59
|
+
./spec/vapi/tool_spec.rb[1:4:1] | passed | 0.00051 seconds |
|
|
60
|
+
./spec/vapi/tool_spec.rb[1:5:1] | passed | 0.00021 seconds |
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "vapi"
|
|
5
|
+
require "webmock/rspec"
|
|
6
|
+
|
|
7
|
+
RSpec.configure do |config|
|
|
8
|
+
config.expect_with :rspec do |expectations|
|
|
9
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
config.mock_with :rspec do |mocks|
|
|
13
|
+
mocks.verify_partial_doubles = true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
17
|
+
config.filter_run_when_matching :focus
|
|
18
|
+
config.example_status_persistence_file_path = "spec/examples.txt"
|
|
19
|
+
config.disable_monkey_patching!
|
|
20
|
+
config.warnings = true
|
|
21
|
+
|
|
22
|
+
if config.files_to_run.one?
|
|
23
|
+
config.default_formatter = "doc"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
config.order = :random
|
|
27
|
+
Kernel.srand config.seed
|
|
28
|
+
|
|
29
|
+
config.before(:each) do
|
|
30
|
+
WebMock.disable_net_connect!
|
|
31
|
+
Vapi.reset!
|
|
32
|
+
Vapi.configure do |c|
|
|
33
|
+
c.api_key = "test_api_key"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
config.after(:each) do
|
|
38
|
+
WebMock.reset!
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Vapi::Analytics do
|
|
6
|
+
let(:resource) { described_class.new(Vapi.client) }
|
|
7
|
+
let(:base_url) { "https://api.vapi.ai" }
|
|
8
|
+
|
|
9
|
+
describe "#query" do
|
|
10
|
+
it "queries analytics" do
|
|
11
|
+
stub_request(:post, "#{base_url}/analytics")
|
|
12
|
+
.with(body: { queries: [{ name: "calls", table: "call" }] }.to_json)
|
|
13
|
+
.to_return(status: 200, body: [{ name: "calls", result: [] }].to_json,
|
|
14
|
+
headers: { "Content-Type" => "application/json" })
|
|
15
|
+
|
|
16
|
+
result = resource.query(queries: [{ name: "calls", table: "call" }])
|
|
17
|
+
expect(result).to be_an(Array)
|
|
18
|
+
expect(result.first["name"]).to eq("calls")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Vapi::Assistant do
|
|
6
|
+
let(:resource) { described_class.new(Vapi.client) }
|
|
7
|
+
let(:base_url) { "https://api.vapi.ai" }
|
|
8
|
+
|
|
9
|
+
describe "#list" do
|
|
10
|
+
it "lists assistants" do
|
|
11
|
+
stub_request(:get, "#{base_url}/assistant")
|
|
12
|
+
.to_return(status: 200, body: [{ id: "asst_1", name: "My Assistant" }].to_json,
|
|
13
|
+
headers: { "Content-Type" => "application/json" })
|
|
14
|
+
|
|
15
|
+
result = resource.list
|
|
16
|
+
expect(result).to be_an(Array)
|
|
17
|
+
expect(result.first["id"]).to eq("asst_1")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "passes filter params" do
|
|
21
|
+
stub_request(:get, "#{base_url}/assistant")
|
|
22
|
+
.with(query: { limit: "10" })
|
|
23
|
+
.to_return(status: 200, body: [].to_json,
|
|
24
|
+
headers: { "Content-Type" => "application/json" })
|
|
25
|
+
|
|
26
|
+
resource.list(limit: "10")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "#find" do
|
|
31
|
+
it "gets an assistant by id" do
|
|
32
|
+
stub_request(:get, "#{base_url}/assistant/asst_1")
|
|
33
|
+
.to_return(status: 200, body: { id: "asst_1", name: "My Assistant" }.to_json,
|
|
34
|
+
headers: { "Content-Type" => "application/json" })
|
|
35
|
+
|
|
36
|
+
result = resource.find("asst_1")
|
|
37
|
+
expect(result["id"]).to eq("asst_1")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe "#create" do
|
|
42
|
+
it "creates an assistant" do
|
|
43
|
+
stub_request(:post, "#{base_url}/assistant")
|
|
44
|
+
.with(body: { name: "New Assistant", model: { provider: "openai", model: "gpt-4" } }.to_json)
|
|
45
|
+
.to_return(status: 201, body: { id: "asst_2", name: "New Assistant" }.to_json,
|
|
46
|
+
headers: { "Content-Type" => "application/json" })
|
|
47
|
+
|
|
48
|
+
result = resource.create(name: "New Assistant", model: { provider: "openai", model: "gpt-4" })
|
|
49
|
+
expect(result["id"]).to eq("asst_2")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe "#update" do
|
|
54
|
+
it "updates an assistant" do
|
|
55
|
+
stub_request(:patch, "#{base_url}/assistant/asst_1")
|
|
56
|
+
.with(body: { name: "Updated" }.to_json)
|
|
57
|
+
.to_return(status: 200, body: { id: "asst_1", name: "Updated" }.to_json,
|
|
58
|
+
headers: { "Content-Type" => "application/json" })
|
|
59
|
+
|
|
60
|
+
result = resource.update("asst_1", name: "Updated")
|
|
61
|
+
expect(result["name"]).to eq("Updated")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe "#delete" do
|
|
66
|
+
it "deletes an assistant" do
|
|
67
|
+
stub_request(:delete, "#{base_url}/assistant/asst_1")
|
|
68
|
+
.to_return(status: 204, body: nil)
|
|
69
|
+
|
|
70
|
+
resource.delete("asst_1")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Vapi::Call do
|
|
6
|
+
let(:resource) { described_class.new(Vapi.client) }
|
|
7
|
+
let(:base_url) { "https://api.vapi.ai" }
|
|
8
|
+
|
|
9
|
+
describe "#list" do
|
|
10
|
+
it "lists calls" do
|
|
11
|
+
stub_request(:get, "#{base_url}/call")
|
|
12
|
+
.to_return(status: 200, body: [{ id: "call_1", type: "outboundPhoneCall" }].to_json,
|
|
13
|
+
headers: { "Content-Type" => "application/json" })
|
|
14
|
+
|
|
15
|
+
result = resource.list
|
|
16
|
+
expect(result).to be_an(Array)
|
|
17
|
+
expect(result.first["id"]).to eq("call_1")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "filters by assistantId" do
|
|
21
|
+
stub_request(:get, "#{base_url}/call")
|
|
22
|
+
.with(query: { assistantId: "asst_1" })
|
|
23
|
+
.to_return(status: 200, body: [].to_json,
|
|
24
|
+
headers: { "Content-Type" => "application/json" })
|
|
25
|
+
|
|
26
|
+
resource.list(assistantId: "asst_1")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "#find" do
|
|
31
|
+
it "gets a call by id" do
|
|
32
|
+
stub_request(:get, "#{base_url}/call/call_1")
|
|
33
|
+
.to_return(status: 200, body: { id: "call_1", status: "ended" }.to_json,
|
|
34
|
+
headers: { "Content-Type" => "application/json" })
|
|
35
|
+
|
|
36
|
+
result = resource.find("call_1")
|
|
37
|
+
expect(result["status"]).to eq("ended")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe "#create" do
|
|
42
|
+
it "creates an outbound call" do
|
|
43
|
+
stub_request(:post, "#{base_url}/call")
|
|
44
|
+
.with(body: {
|
|
45
|
+
assistantId: "asst_1",
|
|
46
|
+
phoneNumberId: "pn_1",
|
|
47
|
+
customer: { number: "+1234567890" }
|
|
48
|
+
}.to_json)
|
|
49
|
+
.to_return(status: 201, body: { id: "call_2", type: "outboundPhoneCall" }.to_json,
|
|
50
|
+
headers: { "Content-Type" => "application/json" })
|
|
51
|
+
|
|
52
|
+
result = resource.create(
|
|
53
|
+
assistantId: "asst_1",
|
|
54
|
+
phoneNumberId: "pn_1",
|
|
55
|
+
customer: { number: "+1234567890" }
|
|
56
|
+
)
|
|
57
|
+
expect(result["id"]).to eq("call_2")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe "#update" do
|
|
62
|
+
it "updates a call" do
|
|
63
|
+
stub_request(:patch, "#{base_url}/call/call_1")
|
|
64
|
+
.with(body: { name: "Updated Call" }.to_json)
|
|
65
|
+
.to_return(status: 200, body: { id: "call_1", name: "Updated Call" }.to_json,
|
|
66
|
+
headers: { "Content-Type" => "application/json" })
|
|
67
|
+
|
|
68
|
+
result = resource.update("call_1", name: "Updated Call")
|
|
69
|
+
expect(result["name"]).to eq("Updated Call")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe "#delete" do
|
|
74
|
+
it "deletes a call" do
|
|
75
|
+
stub_request(:delete, "#{base_url}/call/call_1")
|
|
76
|
+
.to_return(status: 204, body: nil)
|
|
77
|
+
|
|
78
|
+
resource.delete("call_1")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Vapi::Client do
|
|
6
|
+
let(:client) { Vapi.client }
|
|
7
|
+
let(:base_url) { "https://api.vapi.ai" }
|
|
8
|
+
|
|
9
|
+
describe "#initialize" do
|
|
10
|
+
it "creates a client instance" do
|
|
11
|
+
expect(client).to be_a(described_class)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context "with missing api_key" do
|
|
15
|
+
it "raises a ConfigurationError" do
|
|
16
|
+
config = Vapi::Configuration.new
|
|
17
|
+
expect { described_class.new(config) }.to raise_error(Vapi::ConfigurationError)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "#get" do
|
|
23
|
+
it "makes authenticated GET request" do
|
|
24
|
+
stub_request(:get, "#{base_url}/v1/test")
|
|
25
|
+
.to_return(status: 200, body: { data: "ok" }.to_json,
|
|
26
|
+
headers: { "Content-Type" => "application/json" })
|
|
27
|
+
|
|
28
|
+
result = client.get("/v1/test")
|
|
29
|
+
expect(result).to eq("data" => "ok")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "sends Bearer token in Authorization header" do
|
|
33
|
+
stub_request(:get, "#{base_url}/v1/test")
|
|
34
|
+
.to_return(status: 200, body: {}.to_json,
|
|
35
|
+
headers: { "Content-Type" => "application/json" })
|
|
36
|
+
|
|
37
|
+
client.get("/v1/test")
|
|
38
|
+
expect(WebMock).to have_requested(:get, "#{base_url}/v1/test")
|
|
39
|
+
.with(headers: { "Authorization" => "Bearer test_api_key" })
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "passes query parameters" do
|
|
43
|
+
stub_request(:get, "#{base_url}/v1/test")
|
|
44
|
+
.with(query: { limit: "10" })
|
|
45
|
+
.to_return(status: 200, body: {}.to_json,
|
|
46
|
+
headers: { "Content-Type" => "application/json" })
|
|
47
|
+
|
|
48
|
+
client.get("/v1/test", limit: "10")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe "#post" do
|
|
53
|
+
it "makes authenticated POST request with JSON body" do
|
|
54
|
+
stub_request(:post, "#{base_url}/v1/test")
|
|
55
|
+
.with(body: { name: "test" }.to_json,
|
|
56
|
+
headers: { "Content-Type" => "application/json" })
|
|
57
|
+
.to_return(status: 201, body: { id: "123" }.to_json,
|
|
58
|
+
headers: { "Content-Type" => "application/json" })
|
|
59
|
+
|
|
60
|
+
result = client.post("/v1/test", name: "test")
|
|
61
|
+
expect(result).to eq("id" => "123")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe "#patch" do
|
|
66
|
+
it "makes authenticated PATCH request" do
|
|
67
|
+
stub_request(:patch, "#{base_url}/v1/test")
|
|
68
|
+
.to_return(status: 200, body: { success: true }.to_json,
|
|
69
|
+
headers: { "Content-Type" => "application/json" })
|
|
70
|
+
|
|
71
|
+
result = client.patch("/v1/test", name: "updated")
|
|
72
|
+
expect(result).to eq("success" => true)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe "#delete" do
|
|
77
|
+
it "makes authenticated DELETE request" do
|
|
78
|
+
stub_request(:delete, "#{base_url}/v1/test")
|
|
79
|
+
.to_return(status: 204, body: nil)
|
|
80
|
+
|
|
81
|
+
client.delete("/v1/test")
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe "error handling" do
|
|
86
|
+
it "raises AuthenticationError on 401" do
|
|
87
|
+
stub_request(:get, "#{base_url}/v1/expired")
|
|
88
|
+
.to_return(status: 401, body: "Unauthorized")
|
|
89
|
+
|
|
90
|
+
expect { client.get("/v1/expired") }.to raise_error(Vapi::AuthenticationError)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "raises NotFoundError on 404" do
|
|
94
|
+
stub_request(:get, "#{base_url}/v1/missing")
|
|
95
|
+
.to_return(status: 404, body: "Not Found")
|
|
96
|
+
|
|
97
|
+
expect { client.get("/v1/missing") }.to raise_error(Vapi::NotFoundError)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "raises RateLimitError on 429 with retry-after" do
|
|
101
|
+
stub_request(:get, "#{base_url}/v1/limited")
|
|
102
|
+
.to_return(status: 429, body: "Too Many Requests",
|
|
103
|
+
headers: { "retry-after" => "30" })
|
|
104
|
+
|
|
105
|
+
expect { client.get("/v1/limited") }.to raise_error(Vapi::RateLimitError) do |error|
|
|
106
|
+
expect(error.retry_after).to eq("30")
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "raises ValidationError on 400" do
|
|
111
|
+
stub_request(:post, "#{base_url}/v1/bad")
|
|
112
|
+
.to_return(
|
|
113
|
+
status: 400,
|
|
114
|
+
body: { message: "Bad Request", errors: { "name" => ["is required"] } }.to_json,
|
|
115
|
+
headers: { "Content-Type" => "application/json" }
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
expect { client.post("/v1/bad", {}) }.to raise_error(Vapi::ValidationError) do |error|
|
|
119
|
+
expect(error.errors).to eq("name" => ["is required"])
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "raises APIError on 500" do
|
|
124
|
+
stub_request(:get, "#{base_url}/v1/error")
|
|
125
|
+
.to_return(status: 500, body: "Internal Server Error")
|
|
126
|
+
|
|
127
|
+
expect { client.get("/v1/error") }.to raise_error(Vapi::APIError)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Vapi::Configuration do
|
|
6
|
+
describe "#initialize" do
|
|
7
|
+
it "sets default base_url" do
|
|
8
|
+
config = described_class.new
|
|
9
|
+
expect(config.base_url).to eq("https://api.vapi.ai")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "sets default timeout" do
|
|
13
|
+
config = described_class.new
|
|
14
|
+
expect(config.timeout).to eq(30)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "#valid?" do
|
|
19
|
+
it "returns true when api_key is set" do
|
|
20
|
+
config = described_class.new
|
|
21
|
+
config.api_key = "test_key"
|
|
22
|
+
expect(config.valid?).to be true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "returns false when api_key is nil" do
|
|
26
|
+
config = described_class.new
|
|
27
|
+
expect(config.valid?).to be false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "returns false when api_key is empty" do
|
|
31
|
+
config = described_class.new
|
|
32
|
+
config.api_key = ""
|
|
33
|
+
expect(config.valid?).to be false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Vapi::File do
|
|
6
|
+
let(:resource) { described_class.new(Vapi.client) }
|
|
7
|
+
let(:base_url) { "https://api.vapi.ai" }
|
|
8
|
+
|
|
9
|
+
describe "#list" do
|
|
10
|
+
it "lists files" do
|
|
11
|
+
stub_request(:get, "#{base_url}/file")
|
|
12
|
+
.to_return(status: 200, body: [{ id: "file_1", name: "doc.pdf" }].to_json,
|
|
13
|
+
headers: { "Content-Type" => "application/json" })
|
|
14
|
+
|
|
15
|
+
result = resource.list
|
|
16
|
+
expect(result).to be_an(Array)
|
|
17
|
+
expect(result.first["name"]).to eq("doc.pdf")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe "#find" do
|
|
22
|
+
it "gets a file by id" do
|
|
23
|
+
stub_request(:get, "#{base_url}/file/file_1")
|
|
24
|
+
.to_return(status: 200, body: { id: "file_1", name: "doc.pdf" }.to_json,
|
|
25
|
+
headers: { "Content-Type" => "application/json" })
|
|
26
|
+
|
|
27
|
+
result = resource.find("file_1")
|
|
28
|
+
expect(result["id"]).to eq("file_1")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "#create" do
|
|
33
|
+
it "creates a file" do
|
|
34
|
+
stub_request(:post, "#{base_url}/file")
|
|
35
|
+
.to_return(status: 201, body: { id: "file_2", name: "new.pdf" }.to_json,
|
|
36
|
+
headers: { "Content-Type" => "application/json" })
|
|
37
|
+
|
|
38
|
+
result = resource.create(name: "new.pdf")
|
|
39
|
+
expect(result["id"]).to eq("file_2")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "#update" do
|
|
44
|
+
it "updates a file" do
|
|
45
|
+
stub_request(:patch, "#{base_url}/file/file_1")
|
|
46
|
+
.with(body: { name: "renamed.pdf" }.to_json)
|
|
47
|
+
.to_return(status: 200, body: { id: "file_1", name: "renamed.pdf" }.to_json,
|
|
48
|
+
headers: { "Content-Type" => "application/json" })
|
|
49
|
+
|
|
50
|
+
result = resource.update("file_1", name: "renamed.pdf")
|
|
51
|
+
expect(result["name"]).to eq("renamed.pdf")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "#delete" do
|
|
56
|
+
it "deletes a file" do
|
|
57
|
+
stub_request(:delete, "#{base_url}/file/file_1")
|
|
58
|
+
.to_return(status: 204, body: nil)
|
|
59
|
+
|
|
60
|
+
resource.delete("file_1")
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Vapi::KnowledgeBase do
|
|
6
|
+
let(:resource) { described_class.new(Vapi.client) }
|
|
7
|
+
let(:base_url) { "https://api.vapi.ai" }
|
|
8
|
+
|
|
9
|
+
describe "#list" do
|
|
10
|
+
it "lists knowledge bases" do
|
|
11
|
+
stub_request(:get, "#{base_url}/knowledge-base")
|
|
12
|
+
.to_return(status: 200, body: [{ id: "kb_1", name: "FAQ" }].to_json,
|
|
13
|
+
headers: { "Content-Type" => "application/json" })
|
|
14
|
+
|
|
15
|
+
result = resource.list
|
|
16
|
+
expect(result).to be_an(Array)
|
|
17
|
+
expect(result.first["name"]).to eq("FAQ")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe "#find" do
|
|
22
|
+
it "gets a knowledge base by id" do
|
|
23
|
+
stub_request(:get, "#{base_url}/knowledge-base/kb_1")
|
|
24
|
+
.to_return(status: 200, body: { id: "kb_1", name: "FAQ" }.to_json,
|
|
25
|
+
headers: { "Content-Type" => "application/json" })
|
|
26
|
+
|
|
27
|
+
result = resource.find("kb_1")
|
|
28
|
+
expect(result["id"]).to eq("kb_1")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "#create" do
|
|
33
|
+
it "creates a knowledge base" do
|
|
34
|
+
stub_request(:post, "#{base_url}/knowledge-base")
|
|
35
|
+
.with(body: { name: "Product Docs", provider: "trieve" }.to_json)
|
|
36
|
+
.to_return(status: 201, body: { id: "kb_2", name: "Product Docs" }.to_json,
|
|
37
|
+
headers: { "Content-Type" => "application/json" })
|
|
38
|
+
|
|
39
|
+
result = resource.create(name: "Product Docs", provider: "trieve")
|
|
40
|
+
expect(result["id"]).to eq("kb_2")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "#update" do
|
|
45
|
+
it "updates a knowledge base" do
|
|
46
|
+
stub_request(:patch, "#{base_url}/knowledge-base/kb_1")
|
|
47
|
+
.with(body: { name: "Updated FAQ" }.to_json)
|
|
48
|
+
.to_return(status: 200, body: { id: "kb_1", name: "Updated FAQ" }.to_json,
|
|
49
|
+
headers: { "Content-Type" => "application/json" })
|
|
50
|
+
|
|
51
|
+
result = resource.update("kb_1", name: "Updated FAQ")
|
|
52
|
+
expect(result["name"]).to eq("Updated FAQ")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "#delete" do
|
|
57
|
+
it "deletes a knowledge base" do
|
|
58
|
+
stub_request(:delete, "#{base_url}/knowledge-base/kb_1")
|
|
59
|
+
.to_return(status: 204, body: nil)
|
|
60
|
+
|
|
61
|
+
resource.delete("kb_1")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Vapi::Log do
|
|
6
|
+
let(:resource) { described_class.new(Vapi.client) }
|
|
7
|
+
let(:base_url) { "https://api.vapi.ai" }
|
|
8
|
+
|
|
9
|
+
describe "#list" do
|
|
10
|
+
it "lists logs" do
|
|
11
|
+
stub_request(:get, "#{base_url}/logs")
|
|
12
|
+
.to_return(status: 200, body: [{ id: "log_1", type: "call" }].to_json,
|
|
13
|
+
headers: { "Content-Type" => "application/json" })
|
|
14
|
+
|
|
15
|
+
result = resource.list
|
|
16
|
+
expect(result).to be_an(Array)
|
|
17
|
+
expect(result.first["type"]).to eq("call")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "filters by call id" do
|
|
21
|
+
stub_request(:get, "#{base_url}/logs")
|
|
22
|
+
.with(query: { callId: "call_1" })
|
|
23
|
+
.to_return(status: 200, body: [].to_json,
|
|
24
|
+
headers: { "Content-Type" => "application/json" })
|
|
25
|
+
|
|
26
|
+
resource.list(callId: "call_1")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|