aliyun-odps 0.1.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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