aurora-data-api 0.1.0 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 344043ce8593c877dc10b721e2b1724ea96265ed5411fbef7828a7c670a3d40e
4
- data.tar.gz: b3b440e30ef5b87abb26d987c8a35951cdb5e4c135181fc20ef9d59142a1f625
3
+ metadata.gz: b1c7df96cdee98eea8565914c4918918c98c1f917d91e32a143330d03b5b9d0c
4
+ data.tar.gz: 63c65eb4a6d6448f5f0ebd5902c6edb71d990ba8e20b01846156eed53fe6af64
5
5
  SHA512:
6
- metadata.gz: dd445671cf4c71684bcc20f24e0eea75d82218dd6959d338cc87e6faafd59a02644fd95fa31e8310645b2796114ea3d541ffd4fa71eb92c81846fd775fcba56e
7
- data.tar.gz: dcdcf6717773ed3bc9921193eddac358496fc19aa90a4e4b7e3ba41c87096ecdc932e69fdd968f37b269e06e6a3ccf0812f755e87e79ee24af167bb65766cbea
6
+ metadata.gz: '08580f3de132c79bec933f95fd8fc384c79f7cdf7cdc68063c128d848809625932a3790e58c6501a68c066f9fa6173759e613e3c16d3870b166c9aa5ed6986b2'
7
+ data.tar.gz: f4597f745ffb462a498d586b582e1360c3bb79c0f6a0276f3e265e3789334b83b7fe7ed868fe7b32a9ef5781b27c3cefd3cb772e6d3f526179767c14de328dca
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.1.3 - 2022-06-02
2
+
3
+ - Bundle tzinfo 😅
4
+
5
+ ## 0.1.2 - 2022-06-02
6
+
7
+ - Fix inconsistency of version in Gemfile.lock
8
+
9
+ ## 0.1.1 - 2022-06-02
10
+
11
+ - Fix some issues (experimental)
12
+
1
13
  ## 0.1.0 - 2022-05-31
2
14
 
3
15
  - Initial release (experimental)
data/Gemfile.lock CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- aurora-data-api (0.1.0)
4
+ aurora-data-api (0.1.3)
5
5
  aws-sdk-rdsdataservice (~> 1.35.0)
6
6
  thor
7
+ tzinfo
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
@@ -15,7 +16,7 @@ GEM
15
16
  tzinfo (~> 2.0)
16
17
  ast (2.4.2)
17
18
  aws-eventstream (1.2.0)
18
- aws-partitions (1.591.0)
19
+ aws-partitions (1.595.0)
19
20
  aws-sdk-core (3.131.1)
20
21
  aws-eventstream (~> 1, >= 1.0.2)
21
22
  aws-partitions (~> 1, >= 1.525.0)
@@ -46,7 +47,7 @@ GEM
46
47
  rb-inotify (0.10.1)
47
48
  ffi (~> 1.0)
48
49
  rbs (2.5.0)
49
- regexp_parser (2.4.0)
50
+ regexp_parser (2.5.0)
50
51
  rexml (3.2.5)
51
52
  rr (3.0.9)
52
53
  rubocop (1.29.1)
@@ -100,4 +101,4 @@ DEPENDENCIES
100
101
  test-unit-rr
101
102
 
102
103
  BUNDLED WITH
103
- 2.3.14
104
+ 2.3.15
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 the database adapter.
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
@@ -1,5 +1,5 @@
1
1
  target :lib do
2
2
  signature "sig"
3
3
  check "lib"
4
- library "time"
4
+ library "time", "date"
5
5
  end
@@ -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 = "Assuming you are using AWS Lambda as a backend, Aurora Serverless v1 (NOT v2) as a database, and Data API as an adapter"
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"
@@ -31,4 +31,5 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.add_dependency "aws-sdk-rdsdataservice", "~> 1.35.0"
33
33
  spec.add_dependency "thor"
34
+ spec.add_dependency "tzinfo"
34
35
  end
data/example/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'aurora-data-api', github: 'hasumikin/aurora-data-api'
3
+ gem 'aurora-data-api'
4
4
  gem 'aws-sdk-lambda'
5
5
 
6
6
  group :test, :development do
data/example/Gemfile.lock CHANGED
@@ -1,14 +1,9 @@
1
- GIT
2
- remote: https://github.com/hasumikin/aurora-data-api.git
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
- - Lamdda functions (outside VPC)
7
- - RDS Aurora Serverless v1 (inside VPC) +
7
+ - Lambda functions outside VPC
8
+ - RDS Aurora Serverless v1 inside VPC + Data API (HTTP endpoint)
9
+ - Secrets Manager, IAM, etc.
8
10
 
9
- ### Getting started
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 :create_database do
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 :drop_database do
13
+ task :drop do
14
14
  sql = <<~SQL
15
15
  DROP DATABASE #{ENV['PGDATABASE']};
16
16
  SQL
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Dir.glob("/var/runtime/bundler/gems/**").each do |dir|
4
- $LOAD_PATH.unshift("#{dir}/lib/")
5
- end
6
-
7
3
  require "json"
8
4
  require "aurora-data-api"
9
5
  require_relative "../depots/user_depot"
@@ -6,6 +6,7 @@ class Entry < AuroraDataApi::Model
6
6
  col :user, :User, table: :users, null: false
7
7
  col :title, String
8
8
  col :body, String
9
+ col :publish_date, Date
9
10
  timestamp
10
11
  end
11
12
  end
Binary file
@@ -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: 900 # 15 min for example
111
+ SecondsUntilAutoPause: 3600 # 60 min for example
112
112
  DBSubnetGroupName:
113
113
  Ref: DBSubnetGroup
114
114
  DBSecret:
data/exe/aurora-data-api CHANGED
@@ -2,9 +2,8 @@
2
2
 
3
3
  require "aurora-data-api/version"
4
4
  require "thor"
5
-
6
- class Date
7
- end
5
+ require "date"
6
+ require "fileutils"
8
7
 
9
8
  module AuroraDataApi
10
9
  class Error < StandardError; end
@@ -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) + 9 * 60 * 60 # workaround
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[name&.to_sym].select { |_k, v|
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.name&.to_sym][:cols].dig(method_name, :type)
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.name&.to_sym][:table_name]
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.name&.to_sym][:cols][member][:type].is_a? Symbol
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AuroraDataApi
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.3"
5
5
  end
@@ -9,6 +9,3 @@ module AuroraDataApi
9
9
  class Error < StandardError; end
10
10
  # Your code goes here...
11
11
  end
12
-
13
- class Date
14
- end
@@ -2,9 +2,6 @@ module AuroraDataApi
2
2
  Error: StandardError
3
3
  end
4
4
 
5
- class Date
6
- end
7
-
8
5
  module Aws
9
6
  def self.config: -> Hash[Symbol, untyped]
10
7
 
data/sig/depot.rbs CHANGED
@@ -7,6 +7,7 @@ module AuroraDataApi
7
7
  @data_service: DataService
8
8
  @table_name: String
9
9
  @literal_id: Symbol
10
+ @observed_utc_offset: Integer
10
11
 
11
12
  def self.[]: (singleton(Model) model) -> Depot
12
13
  def initialize: (singleton(Model) model, Proc block) -> void
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[untyped, untyped]
7
- STRUCTS: Hash[untyped, untyped]
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, singleton(String) | singleton(Time) | singleton(Date) | singleton(Integer) | Symbol type, ?Hash[Symbol, Symbol | bool] opt) -> void
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, untyped]) -> void
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: (untyped method_name, *untyped args) -> nil
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, untyped]
32
- def attributes: -> Hash[Symbol, untyped]
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
data/sig/tzinfo.rbs ADDED
@@ -0,0 +1,9 @@
1
+ module TZInfo
2
+ class Timezone
3
+ def self.get: (String) -> DataTimezone
4
+ end
5
+
6
+ class DataTimezone
7
+ def observed_utc_offset: -> Integer
8
+ end
9
+ 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.0
4
+ version: 0.1.3
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-05-31 00:00:00.000000000 Z
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,22 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
- description: Assuming you are using AWS Lambda as a backend, Aurora Serverless v1
42
- (NOT v2) as a database, and Data API as an adapter
41
+ - !ruby/object:Gem::Dependency
42
+ name: tzinfo
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Doesn't depend on ActiveRecord and takes advantage of PORO (plain old
56
+ Ruby object) like Array, Hash, and Struct internally so you can easily hack.
43
57
  email:
44
58
  - hasumikin@gmail.com
45
59
  executables:
@@ -75,6 +89,7 @@ files:
75
89
  - example/compose.yml
76
90
  - example/db/.gitignore
77
91
  - example/db/.keep
92
+ - example/doc/query-edidor.png
78
93
  - example/package-lock.json
79
94
  - example/package.json
80
95
  - example/serverless.yml
@@ -90,6 +105,7 @@ files:
90
105
  - sig/depot.rbs
91
106
  - sig/environment.rbs
92
107
  - sig/model.rbs
108
+ - sig/tzinfo.rbs
93
109
  - sig/version.rbs
94
110
  homepage: https://github.com/hasumikin/aurora-data-api
95
111
  licenses: