aurora-data-api 0.1.0 → 0.1.1
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 +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +2 -2
- data/README.md +8 -1
- data/Steepfile +1 -1
- data/aurora-data-api.gemspec +1 -1
- data/example/Gemfile +1 -1
- data/example/Gemfile.lock +3 -8
- data/example/README.md +83 -8
- data/example/Rakefile +2 -2
- data/example/app/handlers/main.rb +0 -4
- data/example/app/models/entry.rb +1 -0
- data/example/doc/query-edidor.png +0 -0
- data/example/serverless.yml +1 -1
- data/exe/aurora-data-api +2 -3
- data/lib/aurora-data-api/depot.rb +7 -1
- data/lib/aurora-data-api/model.rb +4 -4
- data/lib/aurora-data-api/version.rb +1 -1
- data/lib/aurora-data-api.rb +0 -3
- data/sig/aurora-data-api.rbs +0 -3
- data/sig/model.rbs +19 -7
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e9e12802725256d0bdc9d33f440906b71ae20924215188db1f65b7be77e5474
|
4
|
+
data.tar.gz: baaf18e32b395d14ae044ece3c23cad990b9bee6a199819a49e423bd99996e18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 303da4d601927f8419139e23b21f8d5b42a218a7ff6cea2132f89e2a80aea7074563e1f9826b5419e90a9da6812bf46e1490a9444ca50a9ad19a22a2ac23f29a
|
7
|
+
data.tar.gz: 6c08a977905060290ae36b5bc2abdb6c94dda4bbaabd68915037052cec4cd9b8c6dbb4be92d9edc9c2cb055ffa71e96e77b73bd692b213794493d6725baf7638
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
aurora-data-api (0.1.
|
4
|
+
aurora-data-api (0.1.1)
|
5
5
|
aws-sdk-rdsdataservice (~> 1.35.0)
|
6
6
|
thor
|
7
7
|
|
@@ -15,7 +15,7 @@ GEM
|
|
15
15
|
tzinfo (~> 2.0)
|
16
16
|
ast (2.4.2)
|
17
17
|
aws-eventstream (1.2.0)
|
18
|
-
aws-partitions (1.
|
18
|
+
aws-partitions (1.592.0)
|
19
19
|
aws-sdk-core (3.131.1)
|
20
20
|
aws-eventstream (~> 1, >= 1.0.2)
|
21
21
|
aws-partitions (~> 1, >= 1.525.0)
|
data/README.md
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
A kind of ORM for Amazon Aurora Serverless v1.
|
4
4
|
|
5
|
-
Supposing that you are using Ruby (typically on AWS Lambda) as an application, Aurora Serverless v1 (NOT v2) as a database, and Data API as
|
5
|
+
Supposing that you are using Ruby (typically on AWS Lambda) as an application, Aurora Serverless v1 (NOT v2) as a database, and Data API as a database adapter.
|
6
|
+
|
7
|
+
This gem doesn't depend on ActiveRecord and takes advantage of PORO (plain old Ruby object) like Array, Hash, and Struct internally so you can easily hack.
|
6
8
|
|
7
9
|
PostgreSQL is the only target as of now.
|
8
10
|
|
@@ -213,10 +215,15 @@ The following variables should be defined:
|
|
213
215
|
ENV['PGDATABASE'] # Database name
|
214
216
|
ENV['RDS_RESOURCE_ARN'] # Resource ARN of RDS Aurora Serverless
|
215
217
|
ENV['RDS_SECRET_ARN'] # Secret ARN that is stored in AWS Secrets Manager
|
218
|
+
ENV['TZ'] # (Optional) Timezone. Internal default is "UTC"
|
216
219
|
```
|
217
220
|
|
218
221
|
RDS_SECRET_ARN has to be attached to an IAM role of the "application".
|
219
222
|
|
223
|
+
## Example project with Serverless Project
|
224
|
+
|
225
|
+
See [example](https://github.com/hasumikin/aurora-data-api/tree/master/example)
|
226
|
+
|
220
227
|
## Development
|
221
228
|
|
222
229
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/Steepfile
CHANGED
data/aurora-data-api.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.email = ["hasumikin@gmail.com"]
|
10
10
|
|
11
11
|
spec.summary = "A kind of ORM for Amazon Aurora Serverless v1"
|
12
|
-
spec.description = "
|
12
|
+
spec.description = "Doesn't depend on ActiveRecord and takes advantage of PORO (plain old Ruby object) like Array, Hash, and Struct internally so you can easily hack."
|
13
13
|
spec.homepage = "https://github.com/hasumikin/aurora-data-api"
|
14
14
|
spec.license = "MIT"
|
15
15
|
spec.required_ruby_version = ">= 2.7.0"
|
data/example/Gemfile
CHANGED
data/example/Gemfile.lock
CHANGED
@@ -1,14 +1,9 @@
|
|
1
|
-
|
2
|
-
remote: https://
|
3
|
-
revision: 70ae5070c8391c6be2ac9b4209425308ed01f2e7
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
4
3
|
specs:
|
5
4
|
aurora-data-api (0.1.0)
|
6
5
|
aws-sdk-rdsdataservice (~> 1.35.0)
|
7
6
|
thor
|
8
|
-
|
9
|
-
GEM
|
10
|
-
remote: https://rubygems.org/
|
11
|
-
specs:
|
12
7
|
aws-eventstream (1.2.0)
|
13
8
|
aws-partitions (1.594.0)
|
14
9
|
aws-sdk-core (3.131.1)
|
@@ -39,7 +34,7 @@ PLATFORMS
|
|
39
34
|
x86_64-linux
|
40
35
|
|
41
36
|
DEPENDENCIES
|
42
|
-
aurora-data-api
|
37
|
+
aurora-data-api
|
43
38
|
aws-sdk-lambda
|
44
39
|
rake
|
45
40
|
test-unit-rr
|
data/example/README.md
CHANGED
@@ -2,44 +2,119 @@
|
|
2
2
|
|
3
3
|
### Stack
|
4
4
|
|
5
|
+
- Ruby + aurora-data-api gem
|
5
6
|
- Serverless Framework
|
6
|
-
|
7
|
-
|
7
|
+
- Lambda functions outside VPC
|
8
|
+
- RDS Aurora Serverless v1 inside VPC + Data API (HTTP endpoint)
|
9
|
+
- Secrets Manager, IAM, etc.
|
8
10
|
|
9
|
-
###
|
11
|
+
### Try in your local computer with docker
|
10
12
|
|
11
13
|
```sh
|
12
14
|
docker compose build
|
13
|
-
docker compose run --rm serverless rake db:create_database
|
14
|
-
docker compose run --rm serverless bundle exec aurora-data-api export
|
15
|
-
docker compose run --rm serverless rake db:migrate_dry_run
|
16
|
-
docker compose run --rm serverless rake db:migrate
|
17
15
|
```
|
16
|
+
|
17
|
+
#### Create database
|
18
|
+
|
19
|
+
```sh
|
20
|
+
docker compose run --rm serverless rake db:create
|
21
|
+
```
|
22
|
+
|
23
|
+
#### Confirm the SQL of migration
|
24
|
+
|
18
25
|
```sh
|
19
26
|
docker compose run --rm serverless rake db:migrate_dry_run
|
20
|
-
=> -- Nothing is modified --
|
21
27
|
```
|
28
|
+
|
29
|
+
#### Migrate
|
30
|
+
|
31
|
+
```sh
|
32
|
+
docker compose run --rm serverless rake db:migrate
|
33
|
+
```
|
34
|
+
|
35
|
+
#### Start containers
|
36
|
+
|
22
37
|
```sh
|
23
38
|
docker compose up
|
24
39
|
```
|
40
|
+
|
41
|
+
### APIs
|
42
|
+
|
25
43
|
```sh
|
26
44
|
curl http://localhost:4000/offline/hello
|
27
45
|
```
|
28
46
|
|
47
|
+
#### Create a user
|
48
|
+
|
29
49
|
```sh
|
30
50
|
curl -X POST http://localhost:4000/offline/create_user \
|
31
51
|
-H 'Content-Type: application/json' \
|
32
52
|
-d '{"name":"HASUMI Hitoshi", "internet_account":"hasumikin"}'
|
53
|
+
```
|
54
|
+
|
55
|
+
#### List users
|
33
56
|
|
57
|
+
```sh
|
58
|
+
curl http://localhost:4000/offline/users
|
59
|
+
```
|
60
|
+
|
61
|
+
#### Update the user
|
62
|
+
|
63
|
+
```sh
|
34
64
|
curl -X PUT http://localhost:4000/offline/update_user \
|
35
65
|
-H 'Content-Type: application/json' \
|
36
66
|
-d '{"id":1, "name":"anonymous", "internet_account":"hasumikin"}'
|
67
|
+
```
|
37
68
|
|
69
|
+
#### Create an entry
|
70
|
+
|
71
|
+
```sh
|
38
72
|
curl -X POST http://localhost:4000/offline/create_entry \
|
39
73
|
-H 'Content-Type: application/json' \
|
40
74
|
-d '{"title":"My first atrticle", "user_id":1}'
|
75
|
+
```
|
76
|
+
|
77
|
+
#### List entries
|
78
|
+
|
79
|
+
```sh
|
80
|
+
curl http://localhost:4000/offline/entries
|
81
|
+
```
|
41
82
|
|
83
|
+
#### Delete the entry
|
84
|
+
|
85
|
+
```sh
|
42
86
|
curl -X POST http://localhost:4000/offline/delete_entry \
|
43
87
|
-H 'Content-Type: application/json' \
|
44
88
|
-d '{"entry_id":1}'
|
45
89
|
```
|
90
|
+
|
91
|
+
### Deploy to AWS
|
92
|
+
|
93
|
+
Supposing you have an AWS credential in $HOME/.aws/credentials.
|
94
|
+
It has to have almost administrative permission in order to provision a Serverless Framework app:
|
95
|
+
|
96
|
+
```
|
97
|
+
[YourAWSProfileName]
|
98
|
+
aws_access_key_id=AKIAxxxxxxxxxxxxxxxxx
|
99
|
+
aws_secret_access_key=xxxxxxxxxxxxxxxxxxxxxxxxxx
|
100
|
+
```
|
101
|
+
|
102
|
+
```sh
|
103
|
+
serverless deploy --stage production --aws-profile [YourAWSProfileName]
|
104
|
+
```
|
105
|
+
|
106
|
+
About 30 minutes later, all stacks should've been provisioned in AWS.
|
107
|
+
|
108
|
+
- You can see API endpoints by `sls info --stage production --aws-profile [YourAWSProfileName]`
|
109
|
+
- Open the AWS management console
|
110
|
+
- Find *RDS_SECRET_ARN* value lools like `arn:aws:secretsmanager:ap-northeast-1:01234567890:secret:aurora-data-api-example-production-AuroraUserSecret-xxxxxx` in "Secrets Manager"
|
111
|
+
|
112
|
+
<img src="https://raw.githubusercontent.com/hasumikin/aurora-data-api/master/example/doc/query-edidor.png" width="400" />
|
113
|
+
|
114
|
+
- Go to "RDS" > "Query Editor" of and connect with the secret ARN above, then run the content of `db/schema.sql` to create tables
|
115
|
+
|
116
|
+
Now that you can call APIs like:
|
117
|
+
|
118
|
+
```sh
|
119
|
+
curl https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/production/hello
|
120
|
+
```
|
data/example/Rakefile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
namespace "db" do
|
3
3
|
desc "Create database"
|
4
|
-
task :
|
4
|
+
task :create do
|
5
5
|
sql = <<~SQL
|
6
6
|
CREATE DATABASE #{ENV['PGDATABASE']}
|
7
7
|
TEMPLATE template0 ENCODING 'UTF-8' LC_COLLATE 'C' LC_CTYPE 'C';
|
@@ -10,7 +10,7 @@ namespace "db" do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
desc "Drop database"
|
13
|
-
task :
|
13
|
+
task :drop do
|
14
14
|
sql = <<~SQL
|
15
15
|
DROP DATABASE #{ENV['PGDATABASE']};
|
16
16
|
SQL
|
data/example/app/models/entry.rb
CHANGED
Binary file
|
data/example/serverless.yml
CHANGED
@@ -108,7 +108,7 @@ resources:
|
|
108
108
|
AutoPause: true
|
109
109
|
MaxCapacity: ${self:custom.myEnvironment.DBMaxCapacity.${sls:stage}, 2}
|
110
110
|
MinCapacity: 2
|
111
|
-
SecondsUntilAutoPause:
|
111
|
+
SecondsUntilAutoPause: 3600 # 60 min for example
|
112
112
|
DBSubnetGroupName:
|
113
113
|
Ref: DBSubnetGroup
|
114
114
|
DBSecret:
|
data/exe/aurora-data-api
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "date"
|
4
|
+
require "tzinfo"
|
5
|
+
|
3
6
|
module AuroraDataApi
|
4
7
|
class Depot
|
5
8
|
def self.[](model, &block)
|
@@ -13,6 +16,7 @@ module AuroraDataApi
|
|
13
16
|
@model = model
|
14
17
|
instance_eval { block.call }
|
15
18
|
@data_service = DataService.new
|
19
|
+
@observed_utc_offset = TZInfo::Timezone.get(ENV['TZ'] || "UTC").observed_utc_offset
|
16
20
|
end
|
17
21
|
|
18
22
|
def table_name
|
@@ -103,7 +107,9 @@ module AuroraDataApi
|
|
103
107
|
when "text"
|
104
108
|
col.value.gsub("''", "'")
|
105
109
|
when "timestamptz"
|
106
|
-
Time.parse(col.value) +
|
110
|
+
Time.parse(col.value) + @observed_utc_offset
|
111
|
+
when "date"
|
112
|
+
Date.parse(col.value)
|
107
113
|
else
|
108
114
|
col.value
|
109
115
|
end
|
@@ -44,7 +44,7 @@ module AuroraDataApi
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def self.relationship_by(table_sym)
|
47
|
-
result = Model::SCHEMA[
|
47
|
+
result = Model::SCHEMA[to_s.to_sym].select { |_k, v|
|
48
48
|
v.is_a? Hash
|
49
49
|
}.select { |_k, v|
|
50
50
|
v.dig(:opt, :table) == table_sym
|
@@ -94,7 +94,7 @@ module AuroraDataApi
|
|
94
94
|
if @struct[method_name].nil?
|
95
95
|
col_id = "#{method_name}_#{literal_id}".to_sym
|
96
96
|
if members.include?(col_id) && @struct[col_id]
|
97
|
-
col_model = SCHEMA[self.class.
|
97
|
+
col_model = SCHEMA[self.class.to_s.to_sym][:cols].dig(method_name, :type)
|
98
98
|
return nil unless col_model
|
99
99
|
@struct[method_name] = AuroraDataApi.const_get("#{col_model}Depot".to_sym).select(
|
100
100
|
%(where "#{literal_id}" = :id), id: @struct[col_id]
|
@@ -109,7 +109,7 @@ module AuroraDataApi
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def table_name
|
112
|
-
SCHEMA[self.class.
|
112
|
+
SCHEMA[self.class.to_s.to_sym][:table_name]
|
113
113
|
end
|
114
114
|
|
115
115
|
def literal_id
|
@@ -120,7 +120,7 @@ module AuroraDataApi
|
|
120
120
|
{}.tap do |hash|
|
121
121
|
members.each do |member|
|
122
122
|
next if member == literal_id && !include_id
|
123
|
-
next if SCHEMA[self.class.
|
123
|
+
next if SCHEMA[self.class.to_s.to_sym][:cols][member][:type].is_a? Symbol
|
124
124
|
send(member).then { |v| hash[member] = v }
|
125
125
|
end
|
126
126
|
end
|
data/lib/aurora-data-api.rb
CHANGED
data/sig/aurora-data-api.rbs
CHANGED
data/sig/model.rbs
CHANGED
@@ -2,20 +2,32 @@
|
|
2
2
|
|
3
3
|
# Classes
|
4
4
|
module AuroraDataApi
|
5
|
+
type attribute = Symbol | Time | Date | Integer | Symbol | bool | nil
|
6
|
+
type attribute_type = singleton(String)
|
7
|
+
| singleton(Time)
|
8
|
+
| singleton(Date)
|
9
|
+
| singleton(Integer)
|
10
|
+
| Symbol
|
11
|
+
|
5
12
|
class Model
|
6
|
-
SCHEMA: Hash[
|
7
|
-
STRUCTS: Hash[
|
13
|
+
SCHEMA: Hash[Symbol, untyped]
|
14
|
+
STRUCTS: Hash[Symbol, untyped]
|
8
15
|
@struct: untyped
|
9
16
|
|
17
|
+
type col_opt = Hash[:table, Symbol]
|
18
|
+
| Hash[:null, bool]
|
19
|
+
| Hash[:unique, bool]
|
20
|
+
| Hash[:default, untyped]
|
21
|
+
|
10
22
|
def self.model_name: -> Symbol
|
11
23
|
def self.literal_id: (Symbol lit) -> Symbol
|
12
24
|
def self.table: (Symbol name) -> Symbol
|
13
25
|
def self.table_name: -> Symbol
|
14
26
|
def self.schema: () { () -> void } -> void
|
15
|
-
def self.col: (Symbol name,
|
27
|
+
def self.col: (Symbol name, attribute_type type, ?col_opt opt) -> void
|
16
28
|
def self.timestamp: -> void
|
17
29
|
def self.relationship_by: (Symbol table_sym) -> [untyped, untyped]?
|
18
|
-
def initialize: (**Hash[Symbol,
|
30
|
+
def initialize: (**Hash[Symbol, attribute]) -> void
|
19
31
|
|
20
32
|
attr_reader id: Integer?
|
21
33
|
attr_reader timestamp: bool
|
@@ -25,11 +37,11 @@ module AuroraDataApi
|
|
25
37
|
def set_timestamp: (at: :update | :create) -> bool
|
26
38
|
def members: -> Array[Symbol]
|
27
39
|
def respond_to_missing?: (untyped symbol, untyped include_private) -> true
|
28
|
-
def method_missing: (
|
40
|
+
def method_missing: (Symbol method_name, *untyped args) -> nil
|
29
41
|
def table_name: -> Symbol
|
30
42
|
def literal_id: -> Symbol
|
31
|
-
def build_params: (?include_id: bool) -> Hash[Symbol,
|
32
|
-
def attributes: -> Hash[Symbol,
|
43
|
+
def build_params: (?include_id: bool) -> Hash[Symbol, attribute]
|
44
|
+
def attributes: -> Hash[Symbol, attribute]
|
33
45
|
def _set_id: (Integer? id) -> void
|
34
46
|
def _destroy: -> bool
|
35
47
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aurora-data-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HASUMI Hitoshi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-rdsdataservice
|
@@ -38,8 +38,8 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
description:
|
42
|
-
|
41
|
+
description: Doesn't depend on ActiveRecord and takes advantage of PORO (plain old
|
42
|
+
Ruby object) like Array, Hash, and Struct internally so you can easily hack.
|
43
43
|
email:
|
44
44
|
- hasumikin@gmail.com
|
45
45
|
executables:
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- example/compose.yml
|
76
76
|
- example/db/.gitignore
|
77
77
|
- example/db/.keep
|
78
|
+
- example/doc/query-edidor.png
|
78
79
|
- example/package-lock.json
|
79
80
|
- example/package.json
|
80
81
|
- example/serverless.yml
|