aliyun-odps 0.1.0 → 0.4.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +31 -0
  4. data/Gemfile +3 -0
  5. data/README.md +55 -12
  6. data/Rakefile +15 -5
  7. data/aliyun-odps.gemspec +22 -11
  8. data/bin/console +10 -3
  9. data/lib/aliyun/odps.rb +69 -2
  10. data/lib/aliyun/odps/authorization.rb +90 -0
  11. data/lib/aliyun/odps/client.rb +40 -0
  12. data/lib/aliyun/odps/configuration.rb +16 -0
  13. data/lib/aliyun/odps/error.rb +97 -0
  14. data/lib/aliyun/odps/http.rb +138 -0
  15. data/lib/aliyun/odps/list.rb +40 -0
  16. data/lib/aliyun/odps/model/function.rb +16 -0
  17. data/lib/aliyun/odps/model/functions.rb +113 -0
  18. data/lib/aliyun/odps/model/instance.rb +130 -0
  19. data/lib/aliyun/odps/model/instance_task.rb +30 -0
  20. data/lib/aliyun/odps/model/instances.rb +119 -0
  21. data/lib/aliyun/odps/model/projects.rb +73 -0
  22. data/lib/aliyun/odps/model/resource.rb +26 -0
  23. data/lib/aliyun/odps/model/resources.rb +144 -0
  24. data/lib/aliyun/odps/model/table.rb +37 -0
  25. data/lib/aliyun/odps/model/table_column.rb +13 -0
  26. data/lib/aliyun/odps/model/table_partition.rb +9 -0
  27. data/lib/aliyun/odps/model/table_partitions.rb +90 -0
  28. data/lib/aliyun/odps/model/table_schema.rb +13 -0
  29. data/lib/aliyun/odps/model/tables.rb +125 -0
  30. data/lib/aliyun/odps/model/task_result.rb +9 -0
  31. data/lib/aliyun/odps/modelable.rb +16 -0
  32. data/lib/aliyun/odps/project.rb +47 -0
  33. data/lib/aliyun/odps/service_object.rb +27 -0
  34. data/lib/aliyun/odps/struct.rb +126 -0
  35. data/lib/aliyun/odps/tunnel/download_session.rb +98 -0
  36. data/lib/aliyun/odps/tunnel/router.rb +15 -0
  37. data/lib/aliyun/odps/tunnel/snappy_reader.rb +19 -0
  38. data/lib/aliyun/odps/tunnel/snappy_writer.rb +45 -0
  39. data/lib/aliyun/odps/tunnel/table_tunnels.rb +81 -0
  40. data/lib/aliyun/odps/tunnel/upload_block.rb +9 -0
  41. data/lib/aliyun/odps/tunnel/upload_session.rb +132 -0
  42. data/lib/aliyun/odps/utils.rb +102 -0
  43. data/lib/aliyun/odps/version.rb +1 -1
  44. data/requirements.png +0 -0
  45. data/wiki/error.md +188 -0
  46. data/wiki/functions.md +39 -0
  47. data/wiki/get_start.md +34 -0
  48. data/wiki/installation.md +15 -0
  49. data/wiki/instances.md +32 -0
  50. data/wiki/projects.md +51 -0
  51. data/wiki/resources.md +62 -0
  52. data/wiki/ssl.md +7 -0
  53. data/wiki/tables.md +75 -0
  54. data/wiki/tunnels.md +80 -0
  55. metadata +195 -13
  56. data/requirements.mindnode/QuickLook/Preview.jpg +0 -0
  57. data/requirements.mindnode/contents.xml +0 -10711
  58. data/requirements.mindnode/viewState.plist +0 -0
@@ -0,0 +1,9 @@
1
+ module Aliyun
2
+ module Odps
3
+ class TablePartition < Struct::Base
4
+ property :name, String, required: true
5
+ property :type, String, required: true, within: %w(bigint double boolean datetime string)
6
+ property :comment, String
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,90 @@
1
+ module Aliyun
2
+ module Odps
3
+ class TablePartitions < ServiceObject
4
+ # List partitions for table
5
+ #
6
+ # @see http://repo.aliyun.com/api-doc/Table/get_table_partition/index.html Get table partitions
7
+ #
8
+ # @param options [Hash] options
9
+ # @option options [String] :marker specify marker for paginate
10
+ # @option options [String] :maxitems (1000) specify maxitems in this request
11
+ #
12
+ # @return [List]
13
+ def list(options = {})
14
+ Utils.stringify_keys!(options)
15
+ path = "/projects/#{project.name}/tables/#{master.name}"
16
+ query = {
17
+ partitions: true,
18
+ expectmarker: true
19
+ }.merge(Utils.hash_slice(options, 'marker', 'maxitems'))
20
+
21
+ result = client.get(path, query: query).parsed_response
22
+ Aliyun::Odps::List.build(result, %w(Partitions Partition)) do |hash|
23
+ build_table_partition(hash)
24
+ end
25
+ end
26
+
27
+ # Create Partition
28
+ #
29
+ # @param partition [Hash<name, value>] specify the partition you want to create
30
+ #
31
+ # @raise InstanceTaskNotSuccessError if instance task failed
32
+ #
33
+ # @return [Hash] the partition you create
34
+ def create(partition)
35
+ sql = generate_create_sql(partition)
36
+
37
+ task = InstanceTask.new(
38
+ name: 'SQLCreatePartitionTask',
39
+ type: 'SQL',
40
+ query: sql
41
+ )
42
+
43
+ instance = project.instances.create([task])
44
+
45
+ instance.wait_for_success
46
+
47
+ partition
48
+ end
49
+
50
+ # Delete Partition
51
+ #
52
+ # @params partitions [Hash<name, value>] specify the partition you want to delete
53
+ #
54
+ # @raise InstanceTaskNotSuccessError if instance task failed
55
+ #
56
+ # @return [true]
57
+ def delete(partition)
58
+ sql = generate_drop_sql(partition)
59
+
60
+ task = InstanceTask.new(
61
+ name: 'SQLDropPartitionTask',
62
+ type: 'SQL',
63
+ query: sql
64
+ )
65
+
66
+ instance = project.instances.create([task])
67
+
68
+ instance.wait_for_success
69
+
70
+ true
71
+ end
72
+
73
+ private
74
+
75
+ def generate_create_sql(partition)
76
+ spec = partition.map { |k, v| "#{k} = '#{v}'" }.join(',')
77
+ "ALTER TABLE #{project.name}.`#{master.name}` ADD PARTITION (#{spec});"
78
+ end
79
+
80
+ def generate_drop_sql(partition)
81
+ spec = partition.map { |k, v| "#{k} = '#{v}'" }.join(',')
82
+ "ALTER TABLE #{project.name}.`#{master.name}`DROP IF EXISTS PARTITION (#{spec});"
83
+ end
84
+
85
+ def build_table_partition(hash)
86
+ Hash[Utils.wrap(hash['Column']).map(&:values)]
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,13 @@
1
+ module Aliyun
2
+ module Odps
3
+ class TableSchema < Struct::Base
4
+ property :columns, Array, init_with: ->(value) do
5
+ value.map { |v| TableColumn.new(v) }
6
+ end
7
+
8
+ property :partitions, Array, init_with: ->(value) do
9
+ value.map { |v| TablePartition.new(v) }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,125 @@
1
+ module Aliyun
2
+ module Odps
3
+ # Methods for Tables
4
+ class Tables < ServiceObject
5
+ # List tables in this project
6
+ #
7
+ # @see http://repo.aliyun.com/api-doc/Table/get_tables/index.html Get tables
8
+ #
9
+ # @params options [Hash] options
10
+ # @option options [String] :name specify the table name
11
+ # @option options [String] :owner specify the table owner
12
+ # @option options [String] :marker
13
+ # @option options [String] :maxitems (1000)
14
+ def list(options = {})
15
+ Utils.stringify_keys!(options)
16
+ path = "/projects/#{project.name}/tables"
17
+ query = Utils.hash_slice(options, 'name', 'owner', 'marker', 'maxitems')
18
+ query.merge!(tables: true, expectmarker: true)
19
+ result = client.get(path, query: query).parsed_response
20
+
21
+ Aliyun::Odps::List.build(result, %w(Tables Table)) do |hash|
22
+ build_table(hash)
23
+ end
24
+ end
25
+
26
+ # Get Table
27
+ #
28
+ # @see http://repo.aliyun.com/api-doc/Table/get_table/index.html Get Table
29
+ #
30
+ # @params name [String] specify the table name
31
+ def get(name)
32
+ path = "/projects/#{project.name}/tables/#{name}"
33
+ resp = client.get(path)
34
+
35
+ build_table(
36
+ Utils.dig_value(resp.parsed_response, 'Table')
37
+ .merge(
38
+ 'creation_time' => resp.headers['x-odps-creation-time'],
39
+ 'last_modified' => resp.headers['Last-Modified'],
40
+ 'owner' => resp.headers['x-odps-owner']
41
+ )
42
+ )
43
+ end
44
+ alias_method :table, :get
45
+
46
+ # Create Table
47
+ #
48
+ # @params name [String] specify table name
49
+ # @params schema [Struct::TableSchema] specify table schema
50
+ # @params options [Hash] options
51
+ # @option options [String] :comment specify table comment
52
+ #
53
+ # @raise InstanceTaskNotSuccessError if instance task failed
54
+ #
55
+ # @return Table
56
+ def create(name, schema, options = {})
57
+ Utils.stringify_keys!(options)
58
+ table = Table.new(name: name, schema: schema, project: project)
59
+ table.comment = options['comment'] if options.key?('comment')
60
+
61
+ task = InstanceTask.new(name: 'SQLCreateTableTask', type: 'SQL', query: generate_create_sql(table))
62
+
63
+ instance = project.instances.create([task])
64
+ instance.wait_for_success
65
+ table
66
+ end
67
+
68
+ # Delete Table
69
+ #
70
+ # @params name [String]
71
+ #
72
+ # @raise InstanceTaskNotSuccessError if instance task failed
73
+ #
74
+ # @return true
75
+ def delete(name)
76
+ table = Table.new(name: name, project: project)
77
+
78
+ task = InstanceTask.new(
79
+ name: 'SQLDropTableTask',
80
+ type: 'SQL',
81
+ query: generate_drop_sql(table)
82
+ )
83
+
84
+ instance = project.instances.create([task])
85
+ instance.wait_for_success
86
+ true
87
+ end
88
+
89
+ private
90
+
91
+ def generate_create_sql(table)
92
+ sql = "CREATE TABLE #{project.name}.`#{table.name}`"
93
+ sql += generate_table_coumns_sql(table.schema.columns) if table.schema && table.schema.columns
94
+ sql += " COMMENT '#{table.comment}'" if table.comment
95
+ sql += generate_table_partitions_sql(table.schema.partitions) if table.schema && table.schema.partitions
96
+ sql += ';'
97
+ sql
98
+ end
99
+
100
+ def generate_table_coumns_sql(columns)
101
+ ' (' + columns.map do |column|
102
+ generate_table_column_sql(column)
103
+ end.join(', ') + ')'
104
+ end
105
+
106
+ def generate_table_partitions_sql(partitions)
107
+ ' PARTITIONED BY (' + partitions.map do |column|
108
+ generate_table_column_sql(column)
109
+ end.join(', ') + ')'
110
+ end
111
+
112
+ def generate_table_column_sql(column)
113
+ "`#{column.name}` #{column.type}" + (column.comment ? " COMMENT '#{column.comment}'" : '')
114
+ end
115
+
116
+ def generate_drop_sql(table)
117
+ "DROP TABLE IF EXISTS #{project.name}.`#{table.name}`;"
118
+ end
119
+
120
+ def build_table(result)
121
+ Table.new(result.merge(project: project))
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,9 @@
1
+ module Aliyun
2
+ module Odps
3
+ class TaskResult < Struct::Base
4
+ property :name, String, required: true
5
+ property :type, String, required: true
6
+ property :result, Hash, required: true
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ require 'active_support/all'
2
+ require 'aliyun/odps/service_object'
3
+
4
+ module Aliyun
5
+ module Odps
6
+ module Modelable
7
+ def has_many(models, _opts = {})
8
+ mod = models.to_s.singularize
9
+ klass = "Aliyun::Odps::#{mod.camelize}s".constantize
10
+ define_method(models) do
11
+ klass.build(self)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,47 @@
1
+ require 'aliyun/odps/model/instances'
2
+ require 'aliyun/odps/model/tables'
3
+ require 'aliyun/odps/model/functions'
4
+ require 'aliyun/odps/model/resources'
5
+ require 'aliyun/odps/tunnel/table_tunnels'
6
+ require 'aliyun/odps/client'
7
+
8
+ module Aliyun
9
+ module Odps
10
+ class Project < Struct::Base
11
+ extend Aliyun::Odps::Modelable
12
+
13
+ # @!method instances
14
+ # @return [Instances]
15
+ has_many :instances
16
+
17
+ # @!method functions
18
+ # @return [Functions]
19
+ has_many :functions
20
+
21
+ # @!method tables
22
+ # @return [Tables]
23
+ has_many :tables
24
+
25
+ # @!method resources
26
+ # @return [Resources]
27
+ has_many :resources
28
+
29
+ # @!method table_tunnels
30
+ # @return [TableTunnels]
31
+ has_many :table_tunnels
32
+
33
+ property :client, Client, required: true
34
+
35
+ property :name, String, required: true
36
+ property :comment, String
37
+ property :project_group_name, String
38
+ property :state, String
39
+ property :clusters, Hash
40
+ property :property, String
41
+ property :properties, Hash
42
+ end
43
+ end
44
+ end
45
+
46
+ Dir[File.join(File.dirname(__FILE__), 'model/*.rb')].each { |f| require f }
47
+ Dir[File.join(File.dirname(__FILE__), 'tunnel/*.rb')].each { |f| require f }
@@ -0,0 +1,27 @@
1
+ module Aliyun
2
+ module Odps
3
+ class ServiceObject
4
+ def client
5
+ master.is_a?(Client) ? master : project.client
6
+ end
7
+
8
+ def project
9
+ @master.is_a?(Project) ? @master : @master.try(:project)
10
+ end
11
+
12
+ attr_reader :master
13
+
14
+ def initialize(master, _options = {})
15
+ @master = master
16
+ end
17
+
18
+ def self.service_pool
19
+ @service_pool ||= {}
20
+ end
21
+
22
+ def self.build(master, options = {})
23
+ service_pool[master] ||= new(master, options)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,126 @@
1
+ module Aliyun
2
+ module Odps
3
+ module Struct
4
+ class Base
5
+ def initialize(attributes = {})
6
+ attributes.each do |key, value|
7
+ m = "#{Utils.underscore(key)}=".to_sym
8
+ send(m, value) if self.respond_to?(m)
9
+ end
10
+
11
+ validate_required
12
+ end
13
+
14
+ def validate_required
15
+ missing_attrs = []
16
+ (self.class.required_attrs || []).each do |attr|
17
+ missing_attrs.push(attr) if send(attr).nil?
18
+ end
19
+ fail "Missing attribute: #{missing_attrs.join(',')}" unless missing_attrs.empty?
20
+ end
21
+ private :validate_required
22
+
23
+ def update_attrs(attrs)
24
+ attrs.each do |k, v|
25
+ send("#{Utils.underscore(k)}=", v)
26
+ end
27
+ self
28
+ end
29
+
30
+ def client
31
+ project.client
32
+ end
33
+
34
+ class << self
35
+ attr_reader :required_attrs
36
+
37
+ # @!macro [attach] property
38
+ # @!attribute [rw] $1
39
+ # @return [$2]
40
+ #
41
+ # @example
42
+ #
43
+ # property :name, String, required: true, init_with: proc {|value| value.upcase }, within: %w{value1 value2}
44
+ #
45
+ # @params options [Hash] options
46
+ # @option options [Boolean] :required required or optional
47
+ # @option options [block] :init_with block used to init attr
48
+ def property(attr, type, options = {})
49
+ @required_attrs ||= []
50
+ @required_attrs << attr.to_s if options[:required]
51
+
52
+ define_reader_method(attr)
53
+ define_writer_method(attr, type, options)
54
+ end
55
+
56
+ private
57
+
58
+ def define_reader_method(attr)
59
+ attr_reader attr
60
+ end
61
+
62
+ def define_writer_method(attr, type, options)
63
+ init_with_block =
64
+ if options.key?(:init_with) && options[:init_with].respond_to?(:call)
65
+ options[:init_with]
66
+ else
67
+ build_block_with_type(attr, type, options)
68
+ end
69
+ define_writer_method_with_block(attr, init_with_block)
70
+ end
71
+
72
+ def build_block_with_type(attr, type, options)
73
+ case type.to_s
74
+ when 'Integer'
75
+ build_integer_block(attr, options)
76
+ when 'DateTime'
77
+ build_datetime_block(attr, options)
78
+ when 'String'
79
+ build_string_block(attr, options)
80
+ else
81
+ build_default_block(attr, options)
82
+ end
83
+ end
84
+
85
+ def build_string_block(attr, options)
86
+ if options.key?(:within) && options[:within].is_a?(Array)
87
+ build_block_with_within(attr, options)
88
+ else
89
+ build_default_block(attr, options)
90
+ end
91
+ end
92
+
93
+ def build_block_with_within(attr, options)
94
+ proc do |value|
95
+ if options[:within].include?(value.to_s)
96
+ value
97
+ else
98
+ fail ValueNotSupportedError.new(attr, options[:within])
99
+ end
100
+ end
101
+ end
102
+
103
+ def build_integer_block(attr, options)
104
+ proc { |value| value.to_i }
105
+ end
106
+
107
+ def build_datetime_block(attr, options)
108
+ proc { |value| DateTime.parse(value) }
109
+ end
110
+
111
+ def build_default_block(attr, options)
112
+ proc { |value| value }
113
+ end
114
+
115
+ def define_writer_method_with_block(attr, block)
116
+ define_method("#{attr}=") do |value|
117
+ instance_eval do
118
+ instance_variable_set("@#{attr}", block.call(value))
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end