blufin 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/bf +5 -0
- data/bin/blufin +5 -0
- data/lib/blufin.rb +245 -0
- data/lib/core/code_scanners/common/scanner_common.rb +83 -0
- data/lib/core/code_scanners/common/scanner_java.rb +106 -0
- data/lib/core/code_scanners/scanner_java_embedded_objects.rb +386 -0
- data/lib/core/code_scanners/scanner_java_enums.rb +125 -0
- data/lib/core/code_scanners/scanner_java_source.rb +29 -0
- data/lib/core/code_scanners/scanner_java_tests.rb +157 -0
- data/lib/core/error_handling/schema_error.rb +9 -0
- data/lib/core/error_handling/sql_error.rb +21 -0
- data/lib/core/error_handling/sql_error_handler.rb +149 -0
- data/lib/core/error_handling/yml_error.rb +21 -0
- data/lib/core/error_handling/yml_error_handler.rb +437 -0
- data/lib/core/mysql.rb +347 -0
- data/lib/core/opt.rb +21 -0
- data/lib/core/site/site.rb +26 -0
- data/lib/core/site/site_auth.rb +88 -0
- data/lib/core/site/site_embedded.rb +27 -0
- data/lib/core/site/site_ports.rb +9 -0
- data/lib/core/site/site_resolver.rb +276 -0
- data/lib/core/site/site_services.rb +162 -0
- data/lib/core/site/site_ui.rb +16 -0
- data/lib/core/yml/config/yml_config_validator.rb +219 -0
- data/lib/core/yml/maven/yml_maven_validator.rb +1132 -0
- data/lib/core/yml/resource/yml_resource_validator.rb +154 -0
- data/lib/core/yml/schema/yml_schema_flags.rb +9 -0
- data/lib/core/yml/schema/yml_schema_validator.rb +1850 -0
- data/lib/core/yml/yml_cache_handler.rb +115 -0
- data/lib/core/yml/yml_common.rb +487 -0
- data/lib/core/yml/yml_meta_writer_base.rb +300 -0
- data/lib/core/yml/yml_outputter.rb +307 -0
- data/lib/core/yml/yml_validator_base.rb +630 -0
- data/lib/core/yml_writers/yml_configuration_writer.rb +40 -0
- data/lib/core/yml_writers/yml_java_api_resource_writer.rb +348 -0
- data/lib/core/yml_writers/yml_java_cron_type_writer.rb +113 -0
- data/lib/core/yml_writers/yml_java_css_dependency_writer.rb +59 -0
- data/lib/core/yml_writers/yml_java_dao_writer.rb +364 -0
- data/lib/core/yml_writers/yml_java_dto_writer.rb +251 -0
- data/lib/core/yml_writers/yml_java_embedded_object_writer.rb +968 -0
- data/lib/core/yml_writers/yml_java_enum_writer.rb +161 -0
- data/lib/core/yml_writers/yml_java_js_dependency_writer.rb +59 -0
- data/lib/core/yml_writers/yml_java_message_type_writer.rb +106 -0
- data/lib/core/yml_writers/yml_java_meta_writer.rb +173 -0
- data/lib/core/yml_writers/yml_java_model_writer.rb +510 -0
- data/lib/core/yml_writers/yml_java_pom_writer.rb +1050 -0
- data/lib/core/yml_writers/yml_java_resource_data_writer.rb +251 -0
- data/lib/core/yml_writers/yml_java_sdk_writer.rb +732 -0
- data/lib/core/yml_writers/yml_java_validator_writer.rb +280 -0
- data/lib/core/yml_writers/yml_java_worker_writer.rb +81 -0
- data/lib/core/yml_writers/yml_sql_structure_writer.rb +307 -0
- data/lib/core/yml_writers/yml_sql_template_writer.rb +243 -0
- data/lib/core/yml_writers/yml_vue_service_writer.rb +170 -0
- data/lib/core/yml_writers/yml_writer_base.rb +114 -0
- data/lib/routes/api_list.rb +35 -0
- data/lib/routes/api_meta.rb +59 -0
- data/lib/routes/build.rb +46 -0
- data/lib/routes/create/create_api.rb +35 -0
- data/lib/routes/create/create_ui.rb +84 -0
- data/lib/routes/export.rb +56 -0
- data/lib/routes/generate/generate_api.rb +225 -0
- data/lib/routes/generate/generate_img_favicon.rb +56 -0
- data/lib/routes/generate/generate_img_landing.rb +94 -0
- data/lib/routes/generate/generate_lambda.rb +43 -0
- data/lib/routes/lint.rb +35 -0
- data/lib/routes/mysql_reset.rb +43 -0
- data/lib/routes/release_blufin.rb +351 -0
- data/lib/routes/run.rb +35 -0
- data/lib/version.rb +1 -0
- data/opt/README.MD +2 -0
- data/opt/config/schema.yml +73 -0
- data/opt/config/template.yml +25 -0
- data/opt/sql/data/config/data-client.sql +7 -0
- data/opt/sql/data/config/data-db-configuration-property.sql +47 -0
- data/opt/sql/data/config/data-db-configuration.sql +9 -0
- data/opt/sql/data/config/data-db.sql +175 -0
- data/opt/sql/data/config/data-profile-api.sql +47 -0
- data/opt/sql/data/config/data-profile-cron.sql +0 -0
- data/opt/sql/data/config/data-profile-worker.sql +0 -0
- data/opt/sql/data/config/data-profile.sql +87 -0
- data/opt/sql/data/config/data-project.sql +95 -0
- data/opt/sql/structure/blufin-master-structure-fks.sql +65 -0
- data/opt/sql/structure/blufin-master-structure.sql +97 -0
- data/opt/sql/structure/blufin-mock-structure-fks.sql +38 -0
- data/opt/sql/structure/blufin-mock-structure.sql +98 -0
- data/opt/sql/templates/config/template-client.sql +7 -0
- data/opt/sql/templates/config/template-db-configuration-property.sql +11 -0
- data/opt/sql/templates/config/template-db-configuration.sql +9 -0
- data/opt/sql/templates/config/template-db.sql +21 -0
- data/opt/sql/templates/config/template-profile-api.sql +11 -0
- data/opt/sql/templates/config/template-profile-cron.sql +7 -0
- data/opt/sql/templates/config/template-profile-worker.sql +7 -0
- data/opt/sql/templates/config/template-profile.sql +21 -0
- data/opt/sql/templates/config/template-project.sql +23 -0
- data/opt/yml/api/schema/config/client.yml +14 -0
- data/opt/yml/api/schema/config/db.yml +45 -0
- data/opt/yml/api/schema/config/db_configuration.yml +22 -0
- data/opt/yml/api/schema/config/db_configuration_property.yml +22 -0
- data/opt/yml/api/schema/config/profile.yml +53 -0
- data/opt/yml/api/schema/config/profile_api.yml +22 -0
- data/opt/yml/api/schema/config/profile_cron.yml +14 -0
- data/opt/yml/api/schema/config/profile_worker.yml +14 -0
- data/opt/yml/api/schema/config/project.yml +48 -0
- data/opt/yml/api/schema/mock/mock.yml +99 -0
- data/opt/yml/api/schema/mock/mock_nested_if_enum.yml +16 -0
- data/opt/yml/api/schema/mock/mock_nested_if_enum_system.yml +16 -0
- data/opt/yml/api/schema/mock/mock_nested_linked.yml +43 -0
- data/opt/yml/api/schema/mock/mock_nested_multiple.yml +61 -0
- data/opt/yml/api/schema/mock/mock_nested_single.yml +67 -0
- data/opt/yml/api/schema/mock/mock_nested_single_super_deep.yml +32 -0
- data/opt/yml/api/schema/mock/mock_nested_single_super_super_deep.yml +17 -0
- metadata +240 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
module Blufin
|
2
|
+
|
3
|
+
class YmlResourceValidator < Blufin::YmlValidatorBase
|
4
|
+
|
5
|
+
CONFIG = 'config'
|
6
|
+
CONFIG_INTERNAL = 'internal'
|
7
|
+
CONFIG_OAUTH = 'oauth'
|
8
|
+
|
9
|
+
STRUCTURE = [
|
10
|
+
{
|
11
|
+
:section_name => CONFIG,
|
12
|
+
:section_type => Blufin::YmlValidatorBase::SECTION_TYPE_FIXED,
|
13
|
+
:section_data => {
|
14
|
+
CONFIG_INTERNAL => {
|
15
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_NESTED_DATA,
|
16
|
+
:required => false,
|
17
|
+
:section_data => {
|
18
|
+
'GET' => {
|
19
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
20
|
+
:required => false
|
21
|
+
},
|
22
|
+
'POST' => {
|
23
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
24
|
+
:required => false
|
25
|
+
},
|
26
|
+
'PATCH' => {
|
27
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
28
|
+
:required => false
|
29
|
+
},
|
30
|
+
'DELETE' => {
|
31
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
32
|
+
:required => false
|
33
|
+
}
|
34
|
+
}
|
35
|
+
},
|
36
|
+
CONFIG_OAUTH => {
|
37
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_NESTED_DATA,
|
38
|
+
:required => false,
|
39
|
+
:section_data => {
|
40
|
+
'GET' => {
|
41
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
42
|
+
:required => false
|
43
|
+
},
|
44
|
+
'POST' => {
|
45
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
46
|
+
:required => false
|
47
|
+
},
|
48
|
+
'PATCH' => {
|
49
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
50
|
+
:required => false
|
51
|
+
},
|
52
|
+
'DELETE' => {
|
53
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
54
|
+
:required => false
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
},
|
60
|
+
{ # TODO - 12/20/18 - I'm pretty sure this is all wrong...
|
61
|
+
:section_name => 'request',
|
62
|
+
:section_type => Blufin::YmlValidatorBase::SECTION_TYPE_DYNAMIC,
|
63
|
+
:section_data => {
|
64
|
+
:section_alphabetical => false,
|
65
|
+
:section_regex => /\A(app\.|common\.|config\.|mock\.)*[a-z_]+(\[\]|\[link\])*\z/,
|
66
|
+
:section_fields => {
|
67
|
+
'type' => {
|
68
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
69
|
+
:required => false
|
70
|
+
},
|
71
|
+
'flag' => {
|
72
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
73
|
+
:required => false
|
74
|
+
},
|
75
|
+
'fkey' => {
|
76
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
77
|
+
:required => false
|
78
|
+
},
|
79
|
+
'default' => {
|
80
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
81
|
+
:required => false
|
82
|
+
},
|
83
|
+
'encrypted' => {
|
84
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
85
|
+
:required => false
|
86
|
+
},
|
87
|
+
'description' => {
|
88
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
89
|
+
:required => false
|
90
|
+
},
|
91
|
+
'required_if' => {
|
92
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
93
|
+
:required => false
|
94
|
+
},
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
]
|
99
|
+
|
100
|
+
# Initialize the class.
|
101
|
+
# @return void
|
102
|
+
def initialize(site, error_handler)
|
103
|
+
|
104
|
+
@site = Blufin::SiteResolver::validate_site(site)
|
105
|
+
@site_path = Blufin::SiteResolver::get_site_location(@site)
|
106
|
+
@site_name = Blufin::SiteResolver::get_site_name(@site)
|
107
|
+
|
108
|
+
@error_handler = error_handler
|
109
|
+
|
110
|
+
# Create resource folder if it doesn't exist.
|
111
|
+
resource_folder = "#{Blufin::SiteResolver::get_site_location(@site)}/yml/api/resources"
|
112
|
+
Blufin::Files::create_directory(resource_folder) unless Blufin::Files::path_exists(resource_folder)
|
113
|
+
|
114
|
+
# Loop through the array of files.
|
115
|
+
Blufin::Files.get_files_in_dir(resource_folder).each { |file| scan_file(file) }
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# The main function that does the file scanning.
|
122
|
+
# @return void
|
123
|
+
def scan_file(file)
|
124
|
+
|
125
|
+
return unless file_is_valid(file)
|
126
|
+
|
127
|
+
# Get the YML data from the file.
|
128
|
+
begin
|
129
|
+
data = YAML.load_file(File.expand_path(file))
|
130
|
+
rescue Exception => e
|
131
|
+
@error_handler.add_error(Blufin::YmlErrorHandler::FILE_INVALID, file, nil, nil, file)
|
132
|
+
return
|
133
|
+
end
|
134
|
+
|
135
|
+
# TODO - FINISH/FIX
|
136
|
+
# data = validate_single_file(@site, @config_file_app, alter_structure_for_app(STRUCTURE_APP), error_handler)
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
# Validates the file.
|
141
|
+
# @return List
|
142
|
+
def file_is_valid(file)
|
143
|
+
valid = true
|
144
|
+
# Make sure this is a '.yml.' file.
|
145
|
+
unless Blufin::YmlCommon.is_yml_file(file)
|
146
|
+
@error_handler.add_error(Blufin::YmlErrorHandler::FILE_INVALID, file, nil, nil, 'Expected .yml file.')
|
147
|
+
valid = false
|
148
|
+
end
|
149
|
+
valid
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Blufin
|
2
|
+
|
3
|
+
class YmlSchemaFlags
|
4
|
+
|
5
|
+
attr_accessor :flags_raw, :definition_order, :auto_increment, :auto_increment_sort_order, :auto_increment_amount, :index, :index_sort_order, :nullable, :nullable_sort_order, :primary_key, :primary_key_sort_order, :unique, :unique_sort_order
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
@@ -0,0 +1,1850 @@
|
|
1
|
+
module Blufin
|
2
|
+
|
3
|
+
class YmlSchemaValidator < Blufin::YmlValidatorBase
|
4
|
+
|
5
|
+
ID = 'id'
|
6
|
+
|
7
|
+
TYPE = 'type'
|
8
|
+
TYPE_BOOLEAN = 'BOOLEAN'
|
9
|
+
TYPE_DATE = 'DATE'
|
10
|
+
TYPE_DATETIME = 'DATETIME'
|
11
|
+
TYPE_DATETIME_INSERT = 'DATETIME_INSERT'
|
12
|
+
TYPE_DATETIME_UPDATE = 'DATETIME_UPDATE'
|
13
|
+
TYPE_DECIMAL = 'DECIMAL'
|
14
|
+
TYPE_ENUM = 'ENUM'
|
15
|
+
TYPE_ENUM_CUSTOM = 'ENUM_CUSTOM'
|
16
|
+
TYPE_ENUM_SYSTEM = 'ENUM_SYSTEM'
|
17
|
+
TYPE_INT = 'INT'
|
18
|
+
TYPE_INT_AUTO = 'INT_AUTO'
|
19
|
+
TYPE_INT_TINY = 'INT_TINY'
|
20
|
+
TYPE_INT_SMALL = 'INT_SMALL'
|
21
|
+
TYPE_INT_BIG = 'INT_BIG'
|
22
|
+
TYPE_TEXT = 'TEXT'
|
23
|
+
TYPE_TEXT_LONG = 'TEXT_LONG'
|
24
|
+
TYPE_VARCHAR = 'VARCHAR'
|
25
|
+
|
26
|
+
FLAG = 'flag'
|
27
|
+
FLAG_AUTO_INCREMENT = 'AUTO_INCREMENT'
|
28
|
+
FLAG_INDEX = 'INDEX'
|
29
|
+
FLAG_NULLABLE = 'NULLABLE'
|
30
|
+
FLAG_PRIMARY_KEY = 'PRIMARY_KEY'
|
31
|
+
FLAG_UNIQUE = 'UNIQUE'
|
32
|
+
|
33
|
+
FKEY = 'fkey'
|
34
|
+
LINK = 'link'
|
35
|
+
|
36
|
+
DESCRIPTION = 'description'
|
37
|
+
DESCRIPTION_TEXT = 'description_text'
|
38
|
+
DESCRIPTION_TYPE = 'description_type'
|
39
|
+
DESCRIPTION_HITS = 'description_hits'
|
40
|
+
|
41
|
+
TRANSIENT = 'transient'
|
42
|
+
REQUIRED = 'required'
|
43
|
+
REQUIRED_IF = 'required_if'
|
44
|
+
REQUIRED_IF_ENUM = 'required_if_enum'
|
45
|
+
CHILD_OF = 'child_of'
|
46
|
+
CHILD_TYPE = 'child_type'
|
47
|
+
ENCRYPTED = 'encrypted'
|
48
|
+
CURRENCY = 'currency'
|
49
|
+
AMOUNT = 'amount'
|
50
|
+
|
51
|
+
ERROR_MULTIPLE_FILES = 'Multiple files'
|
52
|
+
|
53
|
+
VALID_SCHEMAS = %w(app common config mock)
|
54
|
+
VALID_SCHEMAS_REGEX = 'app|common|config|mock'
|
55
|
+
VALID_SCHEMAS_GENERATE = %w(app common config)
|
56
|
+
|
57
|
+
APP = 'app'
|
58
|
+
CONFIG = 'config'
|
59
|
+
COMMON = 'common'
|
60
|
+
MOCK = 'mock'
|
61
|
+
|
62
|
+
REGEX_DECIMAL = /\ADECIMAL\(\d{1,2},\d{1,2}\)\z/
|
63
|
+
REGEX_ENUM = /\AENUM\('[A-Za-z_',]+'\)\z/
|
64
|
+
REGEX_ENUM_CUSTOM = /\AENUM_CUSTOM\('[A-Za-z]+'\)\z/
|
65
|
+
REGEX_ENUM_SYSTEM = /\AENUM_SYSTEM\('[A-Za-z]+'\)\z/
|
66
|
+
REGEX_VARCHAR = /\AVARCHAR\(\d+\)\z/
|
67
|
+
|
68
|
+
RESOURCE_TYPE_PARENT = 'PARENT'
|
69
|
+
RESOURCE_TYPE_OBJECT = 'OBJECT'
|
70
|
+
RESOURCE_TYPE_OBJECT_LIST = 'OBJECT_LIST'
|
71
|
+
RESOURCE_TYPE_OBJECT_LINK = 'OBJECT_LINK'
|
72
|
+
|
73
|
+
INT_TYPES = [
|
74
|
+
TYPE_INT,
|
75
|
+
TYPE_INT_AUTO
|
76
|
+
]
|
77
|
+
|
78
|
+
TEXT_TYPES = [
|
79
|
+
TYPE_TEXT,
|
80
|
+
TYPE_TEXT_LONG
|
81
|
+
]
|
82
|
+
|
83
|
+
DATETIME_TYPES = [
|
84
|
+
TYPE_DATETIME,
|
85
|
+
TYPE_DATETIME_INSERT,
|
86
|
+
TYPE_DATETIME_UPDATE,
|
87
|
+
TYPE_DATE
|
88
|
+
]
|
89
|
+
|
90
|
+
RESOURCE_SCHEMA = 'schema'
|
91
|
+
RESOURCE_TABLE = 'table'
|
92
|
+
|
93
|
+
MAX_TABLE_CHARACTERS = 64
|
94
|
+
|
95
|
+
CONFIG_INTERNAL = 'internal'
|
96
|
+
CONFIG_OAUTH = 'oauth'
|
97
|
+
|
98
|
+
PATH_TO_CONFIG_YML = "#{App::Opt::get_base_path}#{App::Opt::OPT_PATH_YML}/api/schema"
|
99
|
+
|
100
|
+
RESERVED_WORDS = %w(abstract assert boolean break byte case catch char class const continue data_type default do double else enum extends false final finally float for goto if implements import instanceof int interface list long native new null package private protected public return short static strictfp super switch synchronized this throw throws transient true try void volatile while)
|
101
|
+
|
102
|
+
STRUCTURE = [
|
103
|
+
{
|
104
|
+
:section_name => 'config',
|
105
|
+
:section_type => Blufin::YmlValidatorBase::SECTION_TYPE_FIXED,
|
106
|
+
:section_data => {
|
107
|
+
CONFIG_INTERNAL => {
|
108
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_NESTED_DATA,
|
109
|
+
:required => false,
|
110
|
+
:section_data => {
|
111
|
+
'GET' => {
|
112
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
113
|
+
:required => false
|
114
|
+
},
|
115
|
+
'POST' => {
|
116
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
117
|
+
:required => false
|
118
|
+
},
|
119
|
+
'PUT' => {
|
120
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
121
|
+
:required => false
|
122
|
+
},
|
123
|
+
'PATCH' => {
|
124
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
125
|
+
:required => false
|
126
|
+
},
|
127
|
+
'DELETE' => {
|
128
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
129
|
+
:required => false
|
130
|
+
}
|
131
|
+
}
|
132
|
+
},
|
133
|
+
CONFIG_OAUTH => {
|
134
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_NESTED_DATA,
|
135
|
+
:required => false,
|
136
|
+
:section_data => {
|
137
|
+
'GET' => {
|
138
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
139
|
+
:required => false
|
140
|
+
},
|
141
|
+
'POST' => {
|
142
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
143
|
+
:required => false
|
144
|
+
},
|
145
|
+
'PUT' => {
|
146
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
147
|
+
:required => false
|
148
|
+
},
|
149
|
+
'PATCH' => {
|
150
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
151
|
+
:required => false
|
152
|
+
},
|
153
|
+
'DELETE' => {
|
154
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BLANK,
|
155
|
+
:required => false
|
156
|
+
}
|
157
|
+
}
|
158
|
+
},
|
159
|
+
'description' => {
|
160
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
161
|
+
:required => false
|
162
|
+
}
|
163
|
+
}
|
164
|
+
},
|
165
|
+
{
|
166
|
+
:section_name => 'schema',
|
167
|
+
:section_type => Blufin::YmlValidatorBase::SECTION_TYPE_DYNAMIC,
|
168
|
+
:section_data => {
|
169
|
+
:section_alphabetical => false,
|
170
|
+
:section_regex => /\A(app\.|common\.|config\.|mock\.|mock\.)*[a-z_]+(\[\]|\[link\])*\z/,
|
171
|
+
:section_fields => {
|
172
|
+
'type' => {
|
173
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
174
|
+
:required => false
|
175
|
+
},
|
176
|
+
'flag' => {
|
177
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
178
|
+
:required => false
|
179
|
+
},
|
180
|
+
'fkey' => {
|
181
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
182
|
+
:required => false
|
183
|
+
},
|
184
|
+
'encrypted' => {
|
185
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
186
|
+
:required => false
|
187
|
+
},
|
188
|
+
'description' => {
|
189
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
190
|
+
:required => false
|
191
|
+
},
|
192
|
+
'required' => {
|
193
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_BOOLEAN,
|
194
|
+
:required => false
|
195
|
+
},
|
196
|
+
'required_if' => {
|
197
|
+
:type => Blufin::YmlValidatorBase::FIELD_TYPE_TEXT,
|
198
|
+
:required => false
|
199
|
+
},
|
200
|
+
}
|
201
|
+
}
|
202
|
+
}
|
203
|
+
]
|
204
|
+
|
205
|
+
# Initialize the class.
|
206
|
+
# @return void
|
207
|
+
def initialize(site, error_handler)
|
208
|
+
|
209
|
+
begin
|
210
|
+
|
211
|
+
@site = Blufin::SiteResolver::validate_site(site)
|
212
|
+
|
213
|
+
@error_handler = error_handler
|
214
|
+
|
215
|
+
@schema_data = {}
|
216
|
+
@schema_descriptions = {}
|
217
|
+
|
218
|
+
@schema_fks = {} # app.person.id (IS REFERENCED BY) => [app.company.primary_person_id:INT, app.invoice.person_id:INT]
|
219
|
+
@schema_fks_dependencies = {} # app.purchase (HAS 'FKEY' FIELDS TO) => [app.person, app.user, app.company]
|
220
|
+
@schema_fks_placeholders = {} # app.product (HAS THE FOLLOWING PLACEHOLDERS) => [app.company[link], app.product_variation[]]
|
221
|
+
@schema_fks_links = {} # app.address (IS LINKED TO FROM) => [app.company, app.person]
|
222
|
+
|
223
|
+
@schema_resources = {}
|
224
|
+
|
225
|
+
@primary_key_count = nil
|
226
|
+
|
227
|
+
@yml_schema_outputter = Blufin::YmlOutputter.new(@site)
|
228
|
+
@yml_enum_scanner = Blufin::ScannerJavaEnums.new(@site)
|
229
|
+
|
230
|
+
# Contains more descriptive error output.
|
231
|
+
@errors_array = []
|
232
|
+
|
233
|
+
# Validate "config:" section. This must be done before looping files because we will need some of the date from it.
|
234
|
+
@schema_config = {}
|
235
|
+
|
236
|
+
validate_config_section
|
237
|
+
|
238
|
+
config_files = Blufin::Files.get_files_in_dir(PATH_TO_CONFIG_YML)
|
239
|
+
schema_files = Blufin::Files.get_files_in_dir("#{Blufin::SiteResolver::get_site_location(@site)}/#{Blufin::Site::PATH_TO_YML_API_SCHEMA}")
|
240
|
+
|
241
|
+
# Loop through the array of files.
|
242
|
+
(config_files + schema_files).each { |file| scan_file(file) }
|
243
|
+
|
244
|
+
validate_table_duplicates
|
245
|
+
validate_foreign_keys
|
246
|
+
validate_foreign_keys_placeholders
|
247
|
+
validate_required_schema_definitions
|
248
|
+
|
249
|
+
build_resources
|
250
|
+
build_fks_dependencies
|
251
|
+
|
252
|
+
validate_resources
|
253
|
+
validate_http_methods
|
254
|
+
|
255
|
+
@yml_schema_outputter.set_schema_fks_links(@schema_fks_links)
|
256
|
+
|
257
|
+
rescue Exception => e
|
258
|
+
|
259
|
+
Blufin::Terminal::print_exception(e)
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
# @return Array
|
266
|
+
def get_errors_array
|
267
|
+
@errors_array
|
268
|
+
end
|
269
|
+
|
270
|
+
# @return Hash
|
271
|
+
def get_schema_data
|
272
|
+
@schema_data
|
273
|
+
end
|
274
|
+
|
275
|
+
# @return Hash
|
276
|
+
def get_schema_config
|
277
|
+
@schema_config
|
278
|
+
end
|
279
|
+
|
280
|
+
# @return Hash
|
281
|
+
def get_schema_descriptions
|
282
|
+
@schema_descriptions
|
283
|
+
end
|
284
|
+
|
285
|
+
# @return Hash
|
286
|
+
def get_schema_fks
|
287
|
+
@schema_fks
|
288
|
+
end
|
289
|
+
|
290
|
+
# @return Hash
|
291
|
+
def get_schema_fks_placeholders
|
292
|
+
@schema_fks_placeholders
|
293
|
+
end
|
294
|
+
|
295
|
+
# @return Hash
|
296
|
+
def get_schema_fks_dependencies
|
297
|
+
@schema_fks_dependencies
|
298
|
+
end
|
299
|
+
|
300
|
+
# @return Hash
|
301
|
+
def get_schema_fks_links
|
302
|
+
@schema_fks_links
|
303
|
+
end
|
304
|
+
|
305
|
+
# @return Hash
|
306
|
+
def get_schema_resources
|
307
|
+
@schema_resources
|
308
|
+
end
|
309
|
+
|
310
|
+
# @return Object
|
311
|
+
def get_yml_schema_outputter
|
312
|
+
@yml_schema_outputter
|
313
|
+
end
|
314
|
+
|
315
|
+
# Use this to extract flags OUTSIDE the validator.
|
316
|
+
# @return Blufin::YmlSchemaFlags
|
317
|
+
def extract_flags(flags)
|
318
|
+
raise RuntimeError, 'Input cannot be nil in YmlSchemaValidator::extract_flags()' if flags.nil? || flags.strip == ''
|
319
|
+
Blufin::YmlCommon::extract_flags(flags)[0]
|
320
|
+
end
|
321
|
+
|
322
|
+
private
|
323
|
+
|
324
|
+
# Scans a file -- validates & extracts data.
|
325
|
+
# @return void
|
326
|
+
def scan_file(file)
|
327
|
+
|
328
|
+
schema, table, valid = validate_file(file)
|
329
|
+
|
330
|
+
# Make sure the table isn't a Java reserved word (or phrase/word that has special meaning within our stack).
|
331
|
+
if RESERVED_WORDS.include?(table)
|
332
|
+
add_error(file, schema, table, nil, 'Table name cannot be a Java reserved word or phrase/word used by our stack.', table)
|
333
|
+
return
|
334
|
+
end
|
335
|
+
|
336
|
+
# Make sure there are no uppercase letters in schema + table..
|
337
|
+
add_error(file, nil, nil, nil, 'Folder contains uppercase letters and/or numbers.', schema) if contains_uppercase_letter_or_number(schema)
|
338
|
+
add_error(file, nil, nil, nil, 'File contains uppercase letters and/or numbers.', table) if contains_uppercase_letter_or_number(table)
|
339
|
+
|
340
|
+
# Make sure that the table name is no longer than 35 characters.
|
341
|
+
add_error(file, nil, nil, nil, "Table name is too long. Maximum allowed characters is #{MAX_TABLE_CHARACTERS}.", "#{table.length} characters") if table.length > MAX_TABLE_CHARACTERS
|
342
|
+
|
343
|
+
# If file is invalid, return NULL.
|
344
|
+
return unless valid
|
345
|
+
|
346
|
+
# Do a manual check for excessive blank lines, etc.
|
347
|
+
return unless check_file_lines_manually(file, schema, table)
|
348
|
+
|
349
|
+
# Get the YML data from the file.
|
350
|
+
begin
|
351
|
+
all_data = YAML.load_file(File.expand_path(file))
|
352
|
+
rescue Exception => e
|
353
|
+
add_error(file, nil, nil, nil, 'Unable to parse file \xe2\x80\x94 invalid YML.', e.message)
|
354
|
+
return
|
355
|
+
end
|
356
|
+
|
357
|
+
data = all_data['schema']
|
358
|
+
|
359
|
+
# Make sure the file has data.
|
360
|
+
if Blufin::YmlCommon::is_empty(data)
|
361
|
+
add_error(file, nil, nil, nil, 'File is empty.', nil)
|
362
|
+
return
|
363
|
+
end
|
364
|
+
|
365
|
+
# Make sure that the first column in every table is ID.
|
366
|
+
add_error(file, schema, table, data.keys.first, "First column in table must be: #{ID.upcase}", data.keys.first) unless data.keys.first == ID
|
367
|
+
|
368
|
+
@primary_key_count = 0
|
369
|
+
@column_count = 0
|
370
|
+
@types = []
|
371
|
+
@transient_fields = []
|
372
|
+
@data = data
|
373
|
+
@booleans_finished = false
|
374
|
+
|
375
|
+
columns_with_name = []
|
376
|
+
|
377
|
+
# Loop the columns.
|
378
|
+
data.each do |column, column_data|
|
379
|
+
extract_and_validate_column(file, schema, table, column, column_data)
|
380
|
+
columns_with_name << column if column =~ /name/
|
381
|
+
end
|
382
|
+
|
383
|
+
# Make sure if there is ONLY 1 "name" field that it matches the table -- IE: user (table) -> user_name OR user_[.*]_name
|
384
|
+
if columns_with_name.length == 1
|
385
|
+
column_name = columns_with_name[0]
|
386
|
+
unless column_name =~ /#{table}_[a-z_]*name/
|
387
|
+
add_error(file, schema, table, column_name, "Name field should match regex \xe2\x86\x92 #{table}_name OR #{table}_[a-z_]name", "Found: #{column_name}")
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
types_more_than_one = []
|
392
|
+
@types.detect { |e| types_more_than_one << [e, @types.count(e)] if @types.count(e) > 1 }
|
393
|
+
|
394
|
+
# Make sure there are ONLY ONE of certain types of column -- INT_AUTO being 1 of them.
|
395
|
+
[TYPE_DATETIME_INSERT, TYPE_DATETIME_UPDATE, TYPE_INT_AUTO].each do |allowed_only_once|
|
396
|
+
types_more_than_one.each do |found_more_than_once|
|
397
|
+
if found_more_than_once[0] == allowed_only_once
|
398
|
+
add_error(file, schema, table, nil, "Can only have one #{allowed_only_once} field per table.", "Found: #{found_more_than_once[1]} times")
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
# Make sure that column names don't conflict with transients.
|
404
|
+
if @transient_fields.length > 0
|
405
|
+
@transient_fields.each do |transient_field|
|
406
|
+
if @schema_data[schema][table].keys.include?(transient_field)
|
407
|
+
unless @schema_data[schema][table][transient_field][TRANSIENT]
|
408
|
+
@error_handler.add_error(Blufin::YmlErrorHandler::FIELD_NAME_TRANSIENT_CONFLICT, "#{schema}/#{table}.yml", 'schema', transient_field, transient_field)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
add_error(file, schema, table, nil, "No #{FLAG_PRIMARY_KEY} found.", "# of PKs: #{@primary_key_count}") if @primary_key_count < 1
|
415
|
+
add_error(file, schema, table, nil, "More than 1 #{FLAG_PRIMARY_KEY} found.", "# of PKs: #{@primary_key_count}") if @primary_key_count > 1
|
416
|
+
|
417
|
+
end
|
418
|
+
|
419
|
+
# Validates the file and extracts 'schema' + 'table' data.
|
420
|
+
# @return List
|
421
|
+
def validate_file(file)
|
422
|
+
valid = true
|
423
|
+
# Make sure file exists.
|
424
|
+
unless Blufin::Files.file_exists(file)
|
425
|
+
add_error(file, nil, nil, nil, 'File not found!', nil)
|
426
|
+
valid = false
|
427
|
+
end
|
428
|
+
# Make sure this is a '.yml.' file.
|
429
|
+
unless Blufin::YmlCommon.is_yml_file(file)
|
430
|
+
add_error(file, nil, nil, nil, 'File must have extension: .yml', nil)
|
431
|
+
valid = false
|
432
|
+
end
|
433
|
+
file_parts = file.split('/')
|
434
|
+
schema = file_parts[file_parts.length - 2]
|
435
|
+
table = File.basename(file, '.yml')
|
436
|
+
# Make sure the schema is valid.
|
437
|
+
unless VALID_SCHEMAS.include? schema
|
438
|
+
add_error(file, schema, nil, nil, "Invalid schema. Must be one of: #{VALID_SCHEMAS.join(', ')}", schema)
|
439
|
+
valid = false
|
440
|
+
end
|
441
|
+
return schema, table, valid
|
442
|
+
end
|
443
|
+
|
444
|
+
# @return void
|
445
|
+
# noinspection RubyUnusedLocalVariable
|
446
|
+
def check_file_lines_manually(file, schema, table)
|
447
|
+
|
448
|
+
file_is_valid = true
|
449
|
+
|
450
|
+
field_name = nil
|
451
|
+
field_type = nil
|
452
|
+
initial_comments_found = false
|
453
|
+
previous_was_comment = false
|
454
|
+
previous_was_blank_line = false
|
455
|
+
|
456
|
+
line_count = 0
|
457
|
+
line_contents = []
|
458
|
+
|
459
|
+
validating_config = false
|
460
|
+
validated_config = false
|
461
|
+
validating_schema = false
|
462
|
+
validated_schema = false
|
463
|
+
|
464
|
+
lines = Blufin::Files::read_file(file)
|
465
|
+
lines.each_with_index do |line, idx|
|
466
|
+
|
467
|
+
line_count = line_count + 1
|
468
|
+
|
469
|
+
if line =~ /\Aconfig:/
|
470
|
+
if validated_config
|
471
|
+
add_error(file, schema, table, nil, 'Found more than one "config:" section.', "Multiple \"config:\" sections found.")
|
472
|
+
file_is_valid = false
|
473
|
+
end
|
474
|
+
validating_config = true
|
475
|
+
validating_schema = false
|
476
|
+
validated_config = true
|
477
|
+
next
|
478
|
+
elsif line =~ /\Aschema:/
|
479
|
+
if validated_schema
|
480
|
+
add_error(file, schema, table, nil, 'Found more than one "schema:" section.', "Multiple \"schema:\" sections found.")
|
481
|
+
file_is_valid = false
|
482
|
+
end
|
483
|
+
validating_config = false
|
484
|
+
validating_schema = true
|
485
|
+
validated_schema = true
|
486
|
+
next
|
487
|
+
end
|
488
|
+
|
489
|
+
if validating_schema
|
490
|
+
|
491
|
+
if line =~ /\A#.?/ || line =~ /\A\s{2}#.?/ || line =~ /\A\s{4}#.?/
|
492
|
+
previous_was_comment = true
|
493
|
+
previous_was_blank_line = false
|
494
|
+
elsif line == "\n"
|
495
|
+
initial_comments_found = true if previous_was_comment && !initial_comments_found
|
496
|
+
if previous_was_blank_line
|
497
|
+
add_error(file, schema, table, nil, 'More than one blank (or possibly invalid) line found.', "Line: #{line_count}")
|
498
|
+
file_is_valid = false
|
499
|
+
end
|
500
|
+
previous_was_blank_line = true
|
501
|
+
elsif line =~ /\A\s{2}[a-z_]+:/
|
502
|
+
line_contents = validate_line_content(line, line_contents, file, schema, table, line_count)
|
503
|
+
field_name = line.gsub(':', '').strip
|
504
|
+
field_type = nil
|
505
|
+
previous_was_blank_line = false
|
506
|
+
elsif line =~ /\A\s{2}[a-z_.]+\[\]:/
|
507
|
+
line_contents = validate_line_content(line, line_contents, file, schema, table, line_count)
|
508
|
+
field_name = line.gsub(':', '').strip
|
509
|
+
field_type = nil
|
510
|
+
previous_was_blank_line = false
|
511
|
+
elsif line =~ /\A\s{2}[a-z_.]+\[#{LINK}\]:/
|
512
|
+
line_contents = validate_line_content(line, line_contents, file, schema, table, line_count)
|
513
|
+
field_name = line.gsub(':', '').strip
|
514
|
+
field_type = nil
|
515
|
+
previous_was_blank_line = false
|
516
|
+
elsif line =~ /\A\s{2}[a-z_.]+:/
|
517
|
+
line_contents = validate_line_content(line, line_contents, file, schema, table, line_count)
|
518
|
+
field_name = line.gsub(':', '').strip
|
519
|
+
field_type = nil
|
520
|
+
previous_was_blank_line = false
|
521
|
+
elsif line =~ /\A\s{4}[a-z_]+:/
|
522
|
+
if line =~ /\A\s{4}type:/
|
523
|
+
field_type = line.dup.split(':')
|
524
|
+
field_type = field_type[1].strip
|
525
|
+
end
|
526
|
+
if previous_was_blank_line
|
527
|
+
add_error(file, schema, table, nil, 'More than one blank (or possibly invalid) line found.', "Line: #{line_count}")
|
528
|
+
file_is_valid = false
|
529
|
+
end
|
530
|
+
previous_was_blank_line = false
|
531
|
+
else
|
532
|
+
add_error_with_multi_content(file, schema, table, nil, 'Something is wrong with this line, perhaps check format, key-capitalization or whitespace?', "Line: #{line_count}", [line])
|
533
|
+
file_is_valid = false
|
534
|
+
end
|
535
|
+
|
536
|
+
if idx == (lines.length - 1) && line[-1, 2] == "\n"
|
537
|
+
add_error(file, schema, table, nil, 'Cannot have blank, trailing lines in YML file.', "Line: #{line_count}")
|
538
|
+
file_is_valid = false
|
539
|
+
end
|
540
|
+
|
541
|
+
return unless line_contents
|
542
|
+
|
543
|
+
end
|
544
|
+
|
545
|
+
end
|
546
|
+
|
547
|
+
# Make sure that each file has a "config:" section.
|
548
|
+
unless validated_config
|
549
|
+
add_error(file, schema, table, nil, '"config:" section is missing.', "No \"config:\" section found.")
|
550
|
+
file_is_valid = false
|
551
|
+
end
|
552
|
+
|
553
|
+
# Make sure that each file has a "schema:" section.
|
554
|
+
unless validated_schema
|
555
|
+
add_error(file, schema, table, nil, '"schema:" section is missing.', "No \"schema:\" section found.")
|
556
|
+
file_is_valid = false
|
557
|
+
end
|
558
|
+
|
559
|
+
file_is_valid
|
560
|
+
end
|
561
|
+
|
562
|
+
# Makes sure when checking lines manually, that we don't have an identical definition (as these won't show up in hashes).
|
563
|
+
# @return Array
|
564
|
+
def validate_line_content(line, line_contents, file, schema, table, line_count)
|
565
|
+
if line_contents.include?(line)
|
566
|
+
add_error(file, schema, table, nil, "Duplicate definition found: #{line}", "Line: #{line_count}")
|
567
|
+
false
|
568
|
+
else
|
569
|
+
line_contents << line
|
570
|
+
line_contents
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
# Validates column data and extracts data to global array. Adds errors if they exist.
|
575
|
+
# @return void
|
576
|
+
def extract_and_validate_column(file, schema, table, column_name, column_data)
|
577
|
+
|
578
|
+
define_count = 0
|
579
|
+
define_order = {}
|
580
|
+
|
581
|
+
@column_count = @column_count + 1
|
582
|
+
|
583
|
+
@type = (!column_data.nil? && column_data.has_key?(TYPE)) ? column_data[TYPE] : nil
|
584
|
+
@types << @type unless @type.nil?
|
585
|
+
|
586
|
+
@booleans_finished = true if @type != TYPE_BOOLEAN && column_name.downcase != ID
|
587
|
+
|
588
|
+
# Cannot have both required && required_if properties.
|
589
|
+
add_error(file, schema, table, column_name, "Cannot have both #{REQUIRED} and #{REQUIRED_IF} properties.", column_name) if !column_data.nil? && !column_data[REQUIRED].nil? && !column_data[REQUIRED_IF].nil?
|
590
|
+
|
591
|
+
# Make sure column_name is lowercase & doesn't contain numbers.
|
592
|
+
if contains_uppercase_letter_or_number(column_name)
|
593
|
+
add_error(file, schema, table, column_name, 'Column name must be lowercase & not contain numbers.', column_name)
|
594
|
+
return
|
595
|
+
end
|
596
|
+
|
597
|
+
# Make sure the column isn't a Java reserved word (or phrase/word that has special meaning within our stack).
|
598
|
+
reserved_words = RESERVED_WORDS
|
599
|
+
reserved_words << 'mock parent_id'
|
600
|
+
if reserved_words.include?(column_name)
|
601
|
+
add_error(file, schema, table, column_name, 'Column name cannot be a Java reserved word or phrase/word used by our stack.', column_name)
|
602
|
+
return
|
603
|
+
end
|
604
|
+
|
605
|
+
# If this is a foreign key placeholder IE: app.ebay_aliases[]: or app.order_ebay: ...
|
606
|
+
if Blufin::YmlSchemaValidator::column_is_object(column_name)
|
607
|
+
|
608
|
+
fkp_key = "#{schema}.#{table}"
|
609
|
+
@schema_fks_placeholders[fkp_key] = [] if @schema_fks_placeholders[fkp_key].nil?
|
610
|
+
@schema_fks_placeholders[fkp_key] << column_name
|
611
|
+
|
612
|
+
column_data = {} unless column_data.is_a?(Hash)
|
613
|
+
|
614
|
+
column_data.each do |key, value|
|
615
|
+
|
616
|
+
case key
|
617
|
+
when DESCRIPTION
|
618
|
+
@type = 'FAKE-TYPE' # Hacky fix
|
619
|
+
validate_description(file, schema, table, column_name, value)
|
620
|
+
when TYPE
|
621
|
+
add_error(file, schema, table, column_name, "Placeholders cannot have #{TYPE} property.", "Found: #{value}")
|
622
|
+
next
|
623
|
+
when FLAG
|
624
|
+
add_error(file, schema, table, column_name, "Placeholders cannot have #{FLAG} property.", "Found: #{value}")
|
625
|
+
next
|
626
|
+
when FKEY
|
627
|
+
add_error(file, schema, table, column_name, "Placeholders cannot have #{FKEY} property.", "Found: #{value}")
|
628
|
+
next
|
629
|
+
when REQUIRED
|
630
|
+
validate_required(file, schema, table, column_name, value)
|
631
|
+
when REQUIRED_IF
|
632
|
+
validate_required_if(file, schema, table, column_name, value)
|
633
|
+
when ENCRYPTED
|
634
|
+
add_error(file, schema, table, column_name, "Placeholders cannot have #{ENCRYPTED} property.", "Found: #{value}")
|
635
|
+
next
|
636
|
+
else
|
637
|
+
add_error(file, schema, table, column_name, 'Invalid key.', key)
|
638
|
+
end
|
639
|
+
|
640
|
+
end
|
641
|
+
|
642
|
+
# Add data to @schema_data (with type, which doesn't exist in the YML file(s) as it's inferred).
|
643
|
+
if column_name =~ /^(#{VALID_SCHEMAS_REGEX})\.[\w]+$/
|
644
|
+
column_data[TYPE] = Blufin::ScannerJavaEmbeddedObjects::OBJECT
|
645
|
+
elsif column_name =~ /^(#{VALID_SCHEMAS_REGEX})\.[\w]+\[\]$/
|
646
|
+
column_data[TYPE] = Blufin::ScannerJavaEmbeddedObjects::OBJECT_LIST
|
647
|
+
elsif column_name =~ /^(#{VALID_SCHEMAS_REGEX})\.[\w]+\[#{LINK}\]$/
|
648
|
+
column_data[TYPE] = Blufin::ScannerJavaEmbeddedObjects::OBJECT_LINK
|
649
|
+
else
|
650
|
+
raise RuntimeError, "Unable to determine object type: #{column_name}"
|
651
|
+
end
|
652
|
+
|
653
|
+
if [Blufin::ScannerJavaEmbeddedObjects::OBJECT_LIST, Blufin::ScannerJavaEmbeddedObjects::OBJECT_LINK].include?(column_data[TYPE])
|
654
|
+
add_error(file, schema, table, column_name, "#{column_data[TYPE]} fields cannot have a #{REQUIRED} property.", column_name) if column_data.has_key?(REQUIRED)
|
655
|
+
add_error(file, schema, table, column_name, "#{column_data[TYPE]} fields cannot have a #{REQUIRED_IF} property.", column_name) if column_data.has_key?(REQUIRED_IF)
|
656
|
+
return if column_data.has_key?(REQUIRED) || column_data.has_key?(REQUIRED_IF)
|
657
|
+
end
|
658
|
+
|
659
|
+
add_schema_data(schema, table, column_name, column_data)
|
660
|
+
|
661
|
+
return
|
662
|
+
|
663
|
+
else
|
664
|
+
# Add data to @schema_data
|
665
|
+
add_schema_data(schema, table, column_name, column_data)
|
666
|
+
end
|
667
|
+
|
668
|
+
# Make sure the column has data.
|
669
|
+
if Blufin::YmlCommon::is_empty(column_data)
|
670
|
+
raise RuntimeError, "Expected Hash, instead got: #{column_data.class}" unless column_data.is_a?(Hash)
|
671
|
+
add_error(file, schema, table, column_name, 'Column has no defining data.', column_name)
|
672
|
+
return
|
673
|
+
end
|
674
|
+
|
675
|
+
# Make sure there are only 1 of each key (although YAML parser should never allow this).
|
676
|
+
if column_data.keys != column_data.keys.uniq
|
677
|
+
add_error(file, schema, table, column_name, 'Found duplicate key.', nil)
|
678
|
+
return
|
679
|
+
end
|
680
|
+
|
681
|
+
# Make sure there is a type. All columns need it.
|
682
|
+
if column_data[TYPE].nil?
|
683
|
+
add_error(file, schema, table, column_name, "No #{TYPE} found. All columns need to have a #{TYPE}.", nil)
|
684
|
+
return
|
685
|
+
end
|
686
|
+
|
687
|
+
# IDs are validated separately & uniquely.
|
688
|
+
if column_name.downcase == ID
|
689
|
+
validate_id_column(file, schema, table, column_name, column_data)
|
690
|
+
return
|
691
|
+
end
|
692
|
+
|
693
|
+
# Fields containing the word 'currency' are validated separately & uniquely.
|
694
|
+
if column_name.downcase =~ /#{CURRENCY}/
|
695
|
+
validate_currency_code_column(file, schema, table, column_name, column_data)
|
696
|
+
return
|
697
|
+
end
|
698
|
+
|
699
|
+
# Fields named 'amount' are validated separately & uniquely.
|
700
|
+
if column_name.downcase =~ /\A#{AMOUNT}\z/
|
701
|
+
validate_amount_column(file, schema, table, column_name, column_data)
|
702
|
+
return
|
703
|
+
end
|
704
|
+
|
705
|
+
@flags = nil
|
706
|
+
@foreign_key = false
|
707
|
+
@column_name = column_name
|
708
|
+
|
709
|
+
column_data.each do |key, value|
|
710
|
+
|
711
|
+
key_invalid = false
|
712
|
+
|
713
|
+
case key
|
714
|
+
when DESCRIPTION
|
715
|
+
validate_description(file, schema, table, column_name, value)
|
716
|
+
when TYPE
|
717
|
+
validate_type(file, schema, table, column_name, value)
|
718
|
+
when FLAG
|
719
|
+
@flags = extract_flags_for_validator(file, schema, table, column_name, value)
|
720
|
+
validate_flags(file, schema, table, column_name)
|
721
|
+
when FKEY
|
722
|
+
@foreign_key = true
|
723
|
+
validate_foreign_key(file, schema, table, column_name, value)
|
724
|
+
when REQUIRED
|
725
|
+
validate_required(file, schema, table, column_name, value)
|
726
|
+
when REQUIRED_IF
|
727
|
+
validate_required_if(file, schema, table, column_name, value)
|
728
|
+
when ENCRYPTED
|
729
|
+
validate_encrypted(file, schema, table, column_name, value)
|
730
|
+
else
|
731
|
+
key_invalid = true
|
732
|
+
add_error(file, schema, table, column_name, 'Invalid key', key)
|
733
|
+
end
|
734
|
+
|
735
|
+
unless key_invalid
|
736
|
+
define_count = define_count + 1
|
737
|
+
define_order[key] = define_count
|
738
|
+
end
|
739
|
+
|
740
|
+
end
|
741
|
+
|
742
|
+
validate_definition_order(file, schema, table, column_name, [TYPE, FLAG, FKEY, ENCRYPTED, DESCRIPTION], define_order, 'Keys')
|
743
|
+
|
744
|
+
end
|
745
|
+
|
746
|
+
# Validate the ID column. This column is special and must be validated separately.
|
747
|
+
# @return void
|
748
|
+
def validate_id_column(file, schema, table, column_name, column_data)
|
749
|
+
|
750
|
+
# Make sure ID is first column.
|
751
|
+
add_error(file, schema, table, column_name, 'ID column must be first column in table.', "Is actually ##{@column_count}") unless @column_count == 1
|
752
|
+
|
753
|
+
# Check for invalid keys.
|
754
|
+
invalid_keys = Blufin::YmlCommon.validate_keys(column_data.keys, [TYPE, FLAG])
|
755
|
+
add_error(file, schema, table, column_name, 'ID column has invalid keys.', invalid_keys.join(',')) unless invalid_keys.nil?
|
756
|
+
|
757
|
+
# Make sure the type is INT_AUTO
|
758
|
+
add_error(file, schema, table, column_name, "ID column is missing type which must be: #{TYPE_INT_AUTO}", nil) if column_data[TYPE].nil?
|
759
|
+
add_error(file, schema, table, column_name, "ID columns must be of type: #{TYPE_INT_AUTO}", column_data[TYPE]) unless column_data[TYPE] == TYPE_INT_AUTO && !column_data[TYPE].nil?
|
760
|
+
|
761
|
+
flag_error_message = "ID column must have the following flags: #{[FLAG_PRIMARY_KEY, "#{FLAG_AUTO_INCREMENT} (optional)"].join(', ')}"
|
762
|
+
|
763
|
+
# Make sure the flags are: (optional) AUTO_INCREMENT(??) & PRIMARY_KEY
|
764
|
+
if !column_data[FLAG].nil?
|
765
|
+
|
766
|
+
flags = extract_flags_for_validator(file, schema, table, column_name, column_data[FLAG])
|
767
|
+
|
768
|
+
# Make sure the 1st flag is either: AUTO_INCREMENT or there is no 1st flag.
|
769
|
+
if flags.auto_increment
|
770
|
+
add_error(file, schema, table, column_name, "ID column first flag can only be: #{FLAG_AUTO_INCREMENT}(??) and/or PRIMARY KEY", flags.flags_raw) unless flags.auto_increment && flags.auto_increment_amount != nil && flags.auto_increment_sort_order == 1
|
771
|
+
pk_order = 2
|
772
|
+
pk_text = 'second'
|
773
|
+
else
|
774
|
+
pk_order = 1
|
775
|
+
pk_text = 'first'
|
776
|
+
end
|
777
|
+
|
778
|
+
# Make sure the 2nd flag is: PRIMARY_KEY
|
779
|
+
add_error(file, schema, table, column_name, "#{ID.upcase} column #{pk_text} flag must be: #{FLAG_PRIMARY_KEY}", nil) unless flags.primary_key == true && flags.primary_key_sort_order == pk_order
|
780
|
+
@primary_key_count = @primary_key_count + 1 if flags.primary_key
|
781
|
+
|
782
|
+
# Make sure there are no other flags.
|
783
|
+
add_error(file, schema, table, column_name, "#{ID.upcase} column should not have flag: #{FLAG_INDEX} as this is already implied.", flags.flags_raw) if flags.index
|
784
|
+
add_error(file, schema, table, column_name, "#{ID.upcase} column cannot have flag: #{FLAG_NULLABLE}", flags.flags_raw) if flags.nullable
|
785
|
+
add_error(file, schema, table, column_name, "#{ID.upcase} column should not have flag: #{FLAG_UNIQUE} as this is already implied.", flags.flags_raw) if flags.unique
|
786
|
+
|
787
|
+
if flags.auto_increment
|
788
|
+
@yml_schema_outputter.add_auto_increment_indexed_table(schema, table, flags.auto_increment_amount)
|
789
|
+
else
|
790
|
+
@yml_schema_outputter.add_auto_increment_table(schema, table)
|
791
|
+
end
|
792
|
+
|
793
|
+
else
|
794
|
+
add_error(file, schema, table, column_name, flag_error_message, nil)
|
795
|
+
end
|
796
|
+
|
797
|
+
# Make sure there is no 'description', 'fkey' or 'required_if'.
|
798
|
+
add_error(file, schema, table, column_name, "#{ID.upcase} column cannot have: #{FKEY}", column_data[FKEY]) unless column_data[FKEY].nil?
|
799
|
+
add_error(file, schema, table, column_name, "#{ID.upcase} column cannot have: #{DESCRIPTION}", column_data[DESCRIPTION]) unless column_data[DESCRIPTION].nil?
|
800
|
+
add_error(file, schema, table, column_name, "#{ID.upcase} column cannot have: #{REQUIRED} (this is inferred).", column_data[REQUIRED]) unless column_data[REQUIRED].nil?
|
801
|
+
add_error(file, schema, table, column_name, "#{ID.upcase} column cannot have: #{REQUIRED_IF}", column_data[REQUIRED_IF]) unless column_data[REQUIRED_IF].nil?
|
802
|
+
|
803
|
+
end
|
804
|
+
|
805
|
+
# Validate any column with the word 'currency' in it. This column is special and must be validated separately.
|
806
|
+
# @return void
|
807
|
+
def validate_currency_code_column(file, schema, table, column_name, column_data)
|
808
|
+
|
809
|
+
# Make sure the type is ENUM_SYSTEM('Money')
|
810
|
+
add_error(file, schema, table, column_name, "#{CURRENCY.upcase} columns must be of type: #{TYPE_ENUM_SYSTEM}('Money')", column_data[TYPE]) unless column_data[TYPE] == "#{TYPE_ENUM_SYSTEM}('Money')" && !column_data[TYPE].nil?
|
811
|
+
|
812
|
+
# Make sure there is no 'fkey' or 'required_if'.
|
813
|
+
add_error(file, schema, table, column_name, "#{CURRENCY.upcase} column cannot have: #{FKEY}", column_data[FKEY]) unless column_data[FKEY].nil?
|
814
|
+
add_error(file, schema, table, column_name, "#{CURRENCY.upcase} column cannot have: #{REQUIRED} (this is inferred).", column_data[REQUIRED]) unless column_data[REQUIRED].nil?
|
815
|
+
add_error(file, schema, table, column_name, "#{CURRENCY.upcase} column cannot have: #{REQUIRED_IF}", column_data[REQUIRED_IF]) unless column_data[REQUIRED_IF].nil?
|
816
|
+
|
817
|
+
end
|
818
|
+
|
819
|
+
# Validates any column called 'amount'. This column is special and must be validated separately.
|
820
|
+
# @return void
|
821
|
+
def validate_amount_column(file, schema, table, column_name, column_data)
|
822
|
+
|
823
|
+
# Make sure 'amount' column is DECIMAL(13,2)
|
824
|
+
if column_data[TYPE].nil? || column_data[TYPE] != "#{TYPE_DECIMAL}(13,2)"
|
825
|
+
add_error(file, schema, table, column_name, "#{AMOUNT.upcase} column preceding #{CURRENCY.upcase} must be of type: #{TYPE_DECIMAL}(13,2)", column_data[TYPE])
|
826
|
+
return
|
827
|
+
end
|
828
|
+
|
829
|
+
# Make sure there is no 'description', 'fkey' or 'required_if'.
|
830
|
+
add_error(file, schema, table, column_name, "#{AMOUNT.upcase} column cannot have: #{FKEY}", column_data[FKEY]) unless column_data[FKEY].nil?
|
831
|
+
add_error(file, schema, table, column_name, "#{AMOUNT.upcase} column cannot have: #{REQUIRED} (this is inferred).", column_data[REQUIRED]) unless column_data[REQUIRED].nil?
|
832
|
+
add_error(file, schema, table, column_name, "#{AMOUNT.upcase} column cannot have: #{REQUIRED_IF}", column_data[REQUIRED_IF]) unless column_data[REQUIRED_IF].nil?
|
833
|
+
|
834
|
+
end
|
835
|
+
|
836
|
+
# @return void
|
837
|
+
def validate_description(file, schema, table, column_name, value)
|
838
|
+
|
839
|
+
if Blufin::YmlCommon::is_empty(value)
|
840
|
+
add_error(file, schema, table, column_name, 'No description text found.', "Found: #{value.nil? || value.strip == '' ? "\x1B[38;5;196mNothing\x1B[0m" : value}")
|
841
|
+
return
|
842
|
+
end
|
843
|
+
|
844
|
+
if @type.nil?
|
845
|
+
add_error(file, schema, table, column_name, "#{TYPE} must be defined before #{DESCRIPTION}, cannot validate #{DESCRIPTION}.", nil)
|
846
|
+
return
|
847
|
+
end
|
848
|
+
|
849
|
+
if @schema_descriptions[column_name].nil?
|
850
|
+
@schema_descriptions[column_name] = [
|
851
|
+
{
|
852
|
+
DESCRIPTION_TEXT => value,
|
853
|
+
DESCRIPTION_TYPE => @type,
|
854
|
+
DESCRIPTION_HITS => 1
|
855
|
+
}
|
856
|
+
]
|
857
|
+
else
|
858
|
+
hash_matched = false
|
859
|
+
new_array_of_hashes = []
|
860
|
+
@schema_descriptions[column_name].each do |hash|
|
861
|
+
if hash[DESCRIPTION_TEXT] == value && hash[DESCRIPTION_TYPE] == @type
|
862
|
+
new_array_of_hashes << {
|
863
|
+
DESCRIPTION_TEXT => value,
|
864
|
+
DESCRIPTION_TYPE => @type,
|
865
|
+
DESCRIPTION_HITS => hash[DESCRIPTION_HITS] + 1
|
866
|
+
}
|
867
|
+
hash_matched = true
|
868
|
+
break
|
869
|
+
else
|
870
|
+
new_array_of_hashes << hash
|
871
|
+
end
|
872
|
+
end
|
873
|
+
unless hash_matched
|
874
|
+
new_array_of_hashes << {
|
875
|
+
DESCRIPTION_TEXT => value,
|
876
|
+
DESCRIPTION_TYPE => @type,
|
877
|
+
DESCRIPTION_HITS => 1
|
878
|
+
}
|
879
|
+
end
|
880
|
+
@schema_descriptions[column_name] = new_array_of_hashes
|
881
|
+
end
|
882
|
+
|
883
|
+
end
|
884
|
+
|
885
|
+
# @return void
|
886
|
+
def validate_type(file, schema, table, column_name, type)
|
887
|
+
|
888
|
+
if type == TYPE_INT_AUTO
|
889
|
+
add_error(file, schema, table, column_name, "Type: #{TYPE_INT_AUTO} should only be used for #{ID.upcase} fields.", type)
|
890
|
+
return
|
891
|
+
end
|
892
|
+
|
893
|
+
if type == TYPE_BOOLEAN && @booleans_finished
|
894
|
+
add_error(file, schema, table, column_name, "Type: #{TYPE_BOOLEAN} must be at the very top of file. Only #{ID.upcase} can come before it.", "Position: #{@column_count}")
|
895
|
+
return
|
896
|
+
end
|
897
|
+
|
898
|
+
types_to_skip = [
|
899
|
+
TYPE_BOOLEAN,
|
900
|
+
TYPE_INT,
|
901
|
+
TYPE_INT_TINY,
|
902
|
+
TYPE_INT_SMALL,
|
903
|
+
TYPE_INT_BIG,
|
904
|
+
TYPE_TEXT,
|
905
|
+
TYPE_TEXT_LONG
|
906
|
+
]
|
907
|
+
|
908
|
+
unless types_to_skip.include?(type)
|
909
|
+
if type =~ REGEX_ENUM
|
910
|
+
begin
|
911
|
+
enum_values = Blufin::YmlCommon::enum_value_extractor(type, @site)
|
912
|
+
rescue
|
913
|
+
add_error(file, schema, table, column_name, "#{TYPE_ENUM} is invalid somehow. Please check your syntax.", type)
|
914
|
+
return
|
915
|
+
end
|
916
|
+
if enum_values.include?(nil) || enum_values.include?('')
|
917
|
+
add_error(file, schema, table, column_name, "#{TYPE_ENUM} contains nil (or blank) values.", type)
|
918
|
+
else
|
919
|
+
add_error(file, schema, table, column_name, "#{TYPE_ENUM} contains duplicate values.", type) unless enum_values.map(&:upcase).uniq.length == enum_values.length
|
920
|
+
# Make sure that system-generated enums are all capital + underscores.
|
921
|
+
enum_values.each do |enum_value|
|
922
|
+
add_error(file, schema, table, column_name, "#{type} can only contain capital letters & underscores.", enum_value) unless enum_value =~ /\A[A-Z_]+\z/
|
923
|
+
end
|
924
|
+
end
|
925
|
+
elsif type =~ REGEX_ENUM_CUSTOM
|
926
|
+
begin
|
927
|
+
enum_values = @yml_enum_scanner.get_enum_custom_values_for(Blufin::YmlCommon::enum_name_extractor(@type))
|
928
|
+
add_error(file, schema, table, column_name, "#{type} has no values defined in Java counterpart.", type) unless enum_values.any?
|
929
|
+
rescue
|
930
|
+
add_error(file, schema, table, column_name, "#{type} has no Java counterpart.", type)
|
931
|
+
end
|
932
|
+
elsif type =~ REGEX_ENUM_SYSTEM
|
933
|
+
begin
|
934
|
+
enum_values = @yml_enum_scanner.get_enum_system_values_for(Blufin::YmlCommon::enum_name_extractor(@type))
|
935
|
+
add_error(file, schema, table, column_name, "#{type} has no values defined in Java counterpart.", type) unless enum_values.any?
|
936
|
+
rescue
|
937
|
+
add_error(file, schema, table, column_name, "#{type} has no Java counterpart.", type)
|
938
|
+
end
|
939
|
+
elsif type =~ REGEX_VARCHAR
|
940
|
+
varchar_amount = Blufin::Strings::extract_using_regex(type, /\(\d+\)\z/, %w{( )})
|
941
|
+
add_error(file, schema, table, column_name, "Invalid #{TYPE_VARCHAR} definition, must be integer between 1 - 4096.", type) if varchar_amount.to_i.to_s != varchar_amount || (varchar_amount.to_i > 4096 || varchar_amount.to_i < 1)
|
942
|
+
elsif type =~ REGEX_DECIMAL
|
943
|
+
decimal_m, decimal_d = Blufin::YmlCommon::decimal_extract_values(type)
|
944
|
+
add_error(file, schema, table, column_name, 'Decimal M (1st number) must be between 1 - 65.', type) unless decimal_m.to_i > 0 && decimal_m.to_i < 66
|
945
|
+
add_error(file, schema, table, column_name, 'Decimal D (2nd number) must be between 1 - 30.', type) unless decimal_d.to_i > 0 && decimal_d.to_i < 31
|
946
|
+
add_error(file, schema, table, column_name, 'Decimal M >= D -- 1st digit must be equal or larger than 2nd.', type) if (decimal_m.to_i < decimal_d.to_i)
|
947
|
+
elsif [
|
948
|
+
Blufin::YmlSchemaValidator::TYPE_DATETIME,
|
949
|
+
Blufin::YmlSchemaValidator::TYPE_DATETIME_INSERT,
|
950
|
+
Blufin::YmlSchemaValidator::TYPE_DATETIME_UPDATE
|
951
|
+
].include?(type)
|
952
|
+
add_error(file, schema, table, column_name, "#{type} fields must end in '_datetime'", column_name) unless column_name =~ /_datetime\z/
|
953
|
+
elsif type == TYPE_DATE
|
954
|
+
add_error(file, schema, table, column_name, "#{type} fields must end in '_date'", column_name) if column_name !~ /_date\z/ && column_name != 'date'
|
955
|
+
else
|
956
|
+
extra = ''
|
957
|
+
types_to_skip.each do |valid_type|
|
958
|
+
if type.downcase == valid_type.downcase
|
959
|
+
extra = " Perhaps you meant: #{valid_type} (capitalized)"
|
960
|
+
break
|
961
|
+
end
|
962
|
+
end
|
963
|
+
extra = " Perhaps you meant: #{TYPE_DECIMAL}(?,?)" if extra == '' && type.downcase == TYPE_DECIMAL.downcase
|
964
|
+
error_message = "Invalid #{TYPE}.#{extra}"
|
965
|
+
error_message << " #{TYPE_ENUM}s definitions cannot be blank an/or have spaces." if type =~ /\AENUM/
|
966
|
+
add_error(file, schema, table, column_name, error_message, type)
|
967
|
+
end
|
968
|
+
end
|
969
|
+
|
970
|
+
# Make sure the INSERT + UPDATE fields are named correctly.
|
971
|
+
add_error(file, schema, table, column_name, "#{TYPE_DATETIME_INSERT} fields must end in: created_datetime", @column_name) if type == TYPE_DATETIME_INSERT && !(@column_name =~ /^([a-z_]+_)?created_datetime$/)
|
972
|
+
add_error(file, schema, table, column_name, "#{TYPE_DATETIME_UPDATE} fields must end in: modified_datetime", @column_name) if type == TYPE_DATETIME_UPDATE && !(@column_name =~ /^([a-z_]+_)?modified_datetime$/)
|
973
|
+
|
974
|
+
end
|
975
|
+
|
976
|
+
# @return void
|
977
|
+
def validate_flags(file, schema, table, column_name)
|
978
|
+
|
979
|
+
return if @flags.nil?
|
980
|
+
|
981
|
+
if @type.nil?
|
982
|
+
add_error(file, schema, table, column_name, "#{TYPE} must be defined before #{FLAG}, cannot validate #{FLAG}.", nil)
|
983
|
+
return
|
984
|
+
end
|
985
|
+
|
986
|
+
raise RuntimeError, "Flags must be of type: Blufin::YmlSchemaFlags. You passed: #{@flags.class}" unless @flags.is_a? Blufin::YmlSchemaFlags
|
987
|
+
|
988
|
+
if @flags.auto_increment
|
989
|
+
add_error(file, schema, table, column_name, "Only #{ID.upcase} fields can have #{FLAG_AUTO_INCREMENT} flags.", @flags.flags_raw)
|
990
|
+
return
|
991
|
+
end
|
992
|
+
|
993
|
+
# Not validating AUTO_INCREMENT flag because that gets handles differently.
|
994
|
+
validate_definition_order(file, schema, table, column_name, [FLAG_PRIMARY_KEY, FLAG_INDEX, FLAG_UNIQUE, FLAG_NULLABLE], @flags.definition_order, 'Flags')
|
995
|
+
|
996
|
+
if @flags.primary_key
|
997
|
+
@primary_key_count = @primary_key_count + 1
|
998
|
+
add_error(file, schema, table, column_name, "#{FLAG_PRIMARY_KEY} cannot also be #{FLAG_INDEX}.", @flags.flags_raw) if @flags.index
|
999
|
+
end
|
1000
|
+
|
1001
|
+
if @flags.primary_key
|
1002
|
+
add_error(file, schema, table, column_name, "#{@type} cannot have a #{FLAG_PRIMARY_KEY} flag.", @flags.flags_raw) unless @type == TYPE_INT && column_name == ID
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
if @flags.nullable && [TYPE_BOOLEAN, TYPE_DATETIME_INSERT, TYPE_DATETIME_UPDATE].include?(@type)
|
1006
|
+
add_error(file, schema, table, column_name, "#{@type} cannot have an #{FLAG_NULLABLE} flag.", @flags.flags_raw)
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
if @flags.unique && [TYPE_BOOLEAN, TYPE_DATETIME_INSERT, TYPE_DATETIME_UPDATE].include?(@type)
|
1010
|
+
add_error(file, schema, table, column_name, "#{@type} cannot have an #{FLAG_UNIQUE} flag.", @flags.flags_raw)
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
if @flags.unique && [TYPE_TEXT, TYPE_TEXT_LONG, TYPE_BOOLEAN].include?(@type)
|
1014
|
+
add_error(file, schema, table, column_name, "#{@type} cannot have an #{FLAG_UNIQUE} flag.", @flags.flags_raw)
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
# ENUMs can only have INDEX, INDEX UNIQUE, or no flags -- nothing else.
|
1018
|
+
if (@type =~ REGEX_ENUM || @type =~ REGEX_ENUM_CUSTOM || @type =~ REGEX_ENUM_SYSTEM) && @flags != nil
|
1019
|
+
unless @flags.flags_raw == "#{FLAG_INDEX} #{FLAG_UNIQUE}" || @flags.flags_raw == "#{FLAG_INDEX}"
|
1020
|
+
add_error(file, schema, table, column_name, "#{TYPE_ENUM}s can only have NO flags, '#{FLAG_INDEX}' or '#{FLAG_INDEX} #{FLAG_UNIQUE}' flags \xe2\x80\x94 nothing else.", @flags.flags_raw)
|
1021
|
+
end
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
# Certain flags require others...
|
1025
|
+
[
|
1026
|
+
[[FLAG_UNIQUE, @flags.unique], [FLAG_INDEX, @flags.index]]
|
1027
|
+
].each do |required_combination|
|
1028
|
+
if required_combination[0][1] && required_combination[1][1].nil?
|
1029
|
+
add_error(file, schema, table, column_name, "If flag: #{required_combination[0][0]} is set then flag: #{required_combination[1][0]} must also be set.", @flags.flags_raw)
|
1030
|
+
end
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
# Certain flag combinations are invalid...
|
1034
|
+
[
|
1035
|
+
[[FLAG_UNIQUE, @flags.unique], [FLAG_NULLABLE, @flags.nullable]]
|
1036
|
+
].each do |invalid_combination|
|
1037
|
+
if invalid_combination[0][1] && invalid_combination[1][1]
|
1038
|
+
add_error(file, schema, table, column_name, "Cannot have flags: #{invalid_combination[0][0]} & #{invalid_combination[1][0]} together.", @flags.flags_raw)
|
1039
|
+
end
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
# @return void
|
1045
|
+
def validate_foreign_key(file, schema, table, column_name, target_column)
|
1046
|
+
|
1047
|
+
if @type.nil?
|
1048
|
+
add_error(file, schema, table, column_name, "#{TYPE} must be defined before #{FKEY}, cannot validate #{FKEY}.", nil)
|
1049
|
+
return
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
# Make sure the column matches regex format.
|
1053
|
+
unless fk_is_valid(target_column)
|
1054
|
+
add_error(file, schema, table, column_name, 'Foreign key target column not in correct format: {schema}.{table}.{column}', target_column)
|
1055
|
+
return
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
target_column_split = target_column.split('.')
|
1059
|
+
|
1060
|
+
unless target_column_split[0] == schema
|
1061
|
+
add_error(file, schema, table, column_name, 'Cannot create cross-schema foreign keys.', "#{FKEY}: #{target_column}")
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
unless target_column_split[2] == ID
|
1065
|
+
add_error(file, schema, table, column_name, "Foreign keys can only be to an #{ID.upcase}", "#{FKEY}: #{target_column}")
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
unless @type == TYPE_INT
|
1069
|
+
add_error(file, schema, table, column_name, "Foreign key source columns must be of type #{TYPE_INT}, not:", @type)
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
referencing_column = "#{schema}.#{table}.#{column_name}"
|
1073
|
+
|
1074
|
+
@schema_fks[target_column] = [] if @schema_fks[target_column].nil?
|
1075
|
+
@schema_fks[target_column] << "#{referencing_column}:#{@type}"
|
1076
|
+
|
1077
|
+
if target_column_split[2] == ID
|
1078
|
+
@transient_fields << target_column_split[1]
|
1079
|
+
# Add transient to @schema_data
|
1080
|
+
add_schema_data(schema, table, column_name.gsub(/_#{ID}$/, ''), {
|
1081
|
+
TYPE => Blufin::ScannerJavaEmbeddedObjects::OBJECT,
|
1082
|
+
TRANSIENT => [target_column_split[0], target_column_split[1]]
|
1083
|
+
})
|
1084
|
+
else
|
1085
|
+
Blufin::Terminal::output("Cannot create @Transient object for #{schema}.#{table}.#{column_name} because field name doesn't end in '#{ID}", Blufin::Terminal::MSG_WARNING)
|
1086
|
+
end
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
# @return void
|
1090
|
+
def validate_required(file, schema, table, column_name, value)
|
1091
|
+
|
1092
|
+
# Make sure this is an OBJECT.
|
1093
|
+
unless column_name =~ /\A(#{VALID_SCHEMAS_REGEX})\.[a-z_]+\z/
|
1094
|
+
|
1095
|
+
add_error(file, schema, table, column_name, "Column cannot have a #{REQUIRED} property because it is not a container.", column_name)
|
1096
|
+
return
|
1097
|
+
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
# Make sure the value is lowercase 'true' (and nothing else)
|
1101
|
+
add_error(file, schema, table, column_name, "#{REQUIRED} value must be lowercase 'true' or omitted.", "Found: #{value}") unless value == true && !!value == value
|
1102
|
+
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
# @return void
|
1106
|
+
def validate_required_if(file, schema, table, column_name, value)
|
1107
|
+
|
1108
|
+
# Make sure value is in format: XX=YY
|
1109
|
+
unless value =~ /\A[a-z_]+=[a-zA-Z_]+\z/
|
1110
|
+
add_error(file, schema, table, column_name, "#{REQUIRED_IF} value must be in form of XX=YY where XX is an ENUM field.", "Found: #{value}")
|
1111
|
+
return
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
value_split = value.split('=')
|
1115
|
+
enum_field = value_split[0]
|
1116
|
+
enum_value = value_split[1]
|
1117
|
+
|
1118
|
+
# Make sure the ENUM field exists.
|
1119
|
+
if @data[enum_field].nil?
|
1120
|
+
add_error(file, schema, table, column_name, "#{REQUIRED_IF} references non-existent ENUM field.", "#{table}.#{enum_field}")
|
1121
|
+
return
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
enum_string = @data[enum_field][TYPE]
|
1125
|
+
|
1126
|
+
# Make sure the ENUM field is actually an ENUM field.
|
1127
|
+
unless enum_string =~ Blufin::YmlSchemaValidator::REGEX_ENUM || enum_string =~ Blufin::YmlSchemaValidator::REGEX_ENUM_CUSTOM || enum_string =~ REGEX_ENUM_SYSTEM
|
1128
|
+
add_error(file, schema, table, column_name, "#{REQUIRED_IF} references a field which is not a valid ENUM.", "#{table}.#{enum_field}")
|
1129
|
+
return
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
# Make sure the ENUM value exists.
|
1133
|
+
add_error(file, schema, table, column_name, "#{REQUIRED_IF} uses a value not found in enum field: #{enum_string}", "#{enum_value}") unless Blufin::YmlCommon::enum_value_extractor(enum_string, @site).include?(enum_value)
|
1134
|
+
|
1135
|
+
unless column_name =~ /\A(#{VALID_SCHEMAS_REGEX})\.[a-z_]+\z/ || column_name =~ /\A(#{VALID_SCHEMAS_REGEX})\.[a-z_]+\[(link)?\]\z/
|
1136
|
+
|
1137
|
+
# Make sure that it has a nullable flag.
|
1138
|
+
add_error(file, schema, table, column_name, "Columns with a #{REQUIRED_IF} property need to have a #{FLAG_NULLABLE} flag because they might be empty.", nil) unless @flags && @flags.nullable
|
1139
|
+
end
|
1140
|
+
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
# @return void
|
1144
|
+
def validate_encrypted(file, schema, table, column_name, value)
|
1145
|
+
|
1146
|
+
# Make sure the value is lowercase 'true' (and nothing else)
|
1147
|
+
add_error(file, schema, table, column_name, "#{ENCRYPTED} value must be lowercase 'true' or omitted.", "Found: #{value}") unless value == true && !!value == value
|
1148
|
+
|
1149
|
+
# Make sure the type is TEXT
|
1150
|
+
add_error(file, schema, table, column_name, "#{ENCRYPTED} field must have type: #{TYPE_TEXT}.", @type) unless @type == TYPE_TEXT
|
1151
|
+
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
# Validates the order in which keys are defined for a column.
|
1155
|
+
# @return void
|
1156
|
+
def validate_definition_order(file, schema, table, column_name, correct_order, definition_order, entities)
|
1157
|
+
found_count = 0
|
1158
|
+
correct_order.each do |current_key|
|
1159
|
+
unless definition_order[current_key].nil?
|
1160
|
+
found_count = found_count + 1
|
1161
|
+
unless definition_order[current_key] == found_count
|
1162
|
+
add_error(file, schema, table, column_name, "#{entities} order must be: #{correct_order.join(',')}", definition_order.keys.join(','))
|
1163
|
+
return
|
1164
|
+
end
|
1165
|
+
end
|
1166
|
+
end
|
1167
|
+
end
|
1168
|
+
|
1169
|
+
# Extracts a YmlSchemaFlags object, throws error if a flag is unsupported.
|
1170
|
+
# @return Blufin::YmlSchemaFlags
|
1171
|
+
def extract_flags_for_validator(file, schema, table, column, flags)
|
1172
|
+
|
1173
|
+
# Check for excessive spaces.
|
1174
|
+
add_error(file, schema, table, column, "Too many spaces between 'flags'.", nil) if flags =~ /\s{2,}/
|
1175
|
+
|
1176
|
+
if flags == '' || flags.nil?
|
1177
|
+
add_error(file, schema, table, column, 'Flags cannot be empty or "NULL". Perhaps you meant "NULLABLE"?', nil)
|
1178
|
+
return
|
1179
|
+
end
|
1180
|
+
|
1181
|
+
flag_result = Blufin::YmlCommon::extract_flags(flags)
|
1182
|
+
|
1183
|
+
if flag_result[1].any?
|
1184
|
+
flag_result[1].each do |error|
|
1185
|
+
add_error(file, schema, table, column, error[0], error[1])
|
1186
|
+
end
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
flag_result[0]
|
1190
|
+
|
1191
|
+
end
|
1192
|
+
|
1193
|
+
# Returns TRUE if string contains an uppercase letter.
|
1194
|
+
# @return boolean
|
1195
|
+
def contains_uppercase_letter_or_number(string)
|
1196
|
+
string =~ /[A-Z]/ || string =~/[0-9]/
|
1197
|
+
end
|
1198
|
+
|
1199
|
+
# Validates a FK string.
|
1200
|
+
# @return boolean
|
1201
|
+
def fk_is_valid(fk)
|
1202
|
+
fk =~ /\A(#{VALID_SCHEMAS_REGEX})\.[a-z_]+\.[a-z_]+\z/
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
# Make sure there are no duplicate endpoint names in the various schemas, IE: app.sale & common.sale would be considered duplicates.
|
1206
|
+
# @return void
|
1207
|
+
def validate_table_duplicates
|
1208
|
+
table_names = {}
|
1209
|
+
@schema_data.each do |schema, schema_data|
|
1210
|
+
schema_data.each do |key, value|
|
1211
|
+
if table_names.keys.include?(key)
|
1212
|
+
add_error(nil, nil, nil, nil, "Duplicate table in separate schemas: #{schema}.#{key}", "Already exists: #{table_names[key]}.#{key}")
|
1213
|
+
else
|
1214
|
+
table_names[key] = schema
|
1215
|
+
end
|
1216
|
+
end
|
1217
|
+
end
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
# Make sure that FKs are correct.
|
1221
|
+
# @return void
|
1222
|
+
def validate_foreign_keys
|
1223
|
+
@schema_fks.each do |fk, fk_data|
|
1224
|
+
|
1225
|
+
# Make sure the column matches regex format.
|
1226
|
+
unless fk_is_valid(fk)
|
1227
|
+
add_error(fk, nil, nil, nil, 'Foreign key target column not in correct format: app.table.column', fk)
|
1228
|
+
return
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
fk_exploded = fk.split('.')
|
1232
|
+
|
1233
|
+
expected_reference_end = "#{fk_exploded[1]}_#{fk_exploded[2]}"
|
1234
|
+
|
1235
|
+
# Make sure referenced schema.table.column actually exists.
|
1236
|
+
unless @schema_data[fk_exploded[0]] != nil && @schema_data[fk_exploded[0]][fk_exploded[1]] != nil && @schema_data[fk_exploded[0]][fk_exploded[1]][fk_exploded[2]] != nil
|
1237
|
+
add_error_with_multi_content(nil, fk_exploded[0], nil, nil, "Referenced #{fk} doesn't exist, referenced by below:", "#{FKEY}: #{fk}", fk_data)
|
1238
|
+
next
|
1239
|
+
end
|
1240
|
+
|
1241
|
+
# Make sure the target column has a type and that it's an TINYINT, INT, INT_AUTO, ENUM or VARCHAR.
|
1242
|
+
if @schema_data[fk_exploded[0]][fk_exploded[1]][fk_exploded[2]][TYPE].nil?
|
1243
|
+
add_error(nil, fk_exploded[0], fk_exploded[1], fk_exploded[2], "#{FKEY} column has no type.", nil)
|
1244
|
+
next
|
1245
|
+
else
|
1246
|
+
fk_type = @schema_data[fk_exploded[0]][fk_exploded[1]][fk_exploded[2]][TYPE]
|
1247
|
+
unless [TYPE_INT_TINY, TYPE_INT, TYPE_INT_AUTO].include?(fk_type) || fk_type =~ /\A(#{TYPE_VARCHAR}|#{TYPE_ENUM}|#{TYPE_ENUM_CUSTOM}|#{TYPE_ENUM_SYSTEM})\(/
|
1248
|
+
|
1249
|
+
# TODO, CAN WE FK STRINGS - TEST THIS OUT IN YML??
|
1250
|
+
add_error(nil, fk_exploded[0], fk_exploded[1], fk_exploded[2], "Column is FK'd so must be: #{[TYPE_INT_TINY, TYPE_INT, TYPE_INT_AUTO, TYPE_ENUM, TYPE_ENUM_CUSTOM, TYPE_ENUM_SYSTEM].join(', ')} or #{TYPE_VARCHAR}.", fk_type)
|
1251
|
+
end
|
1252
|
+
end
|
1253
|
+
|
1254
|
+
fk_data.each do |referencing_column|
|
1255
|
+
|
1256
|
+
referencing_column = referencing_column.split(':') # colon is correct, we're splitting -- > app.message_ebay.message_id:INT
|
1257
|
+
referencing_column_type = referencing_column[1]
|
1258
|
+
referencing_column = referencing_column[0]
|
1259
|
+
referencing_column_parent = "#{fk_exploded[0]}.#{fk_exploded[1]}"
|
1260
|
+
rc_exploded = referencing_column.split('.')
|
1261
|
+
placeholder = nil
|
1262
|
+
|
1263
|
+
unless @schema_fks_placeholders["#{fk_exploded[0]}.#{fk_exploded[1]}"].nil?
|
1264
|
+
@schema_fks_placeholders["#{fk_exploded[0]}.#{fk_exploded[1]}"].each do |n|
|
1265
|
+
if "#{rc_exploded[0]}.#{rc_exploded[1]}" == n.gsub(/\[(link)?\]$/, '')
|
1266
|
+
placeholder = n
|
1267
|
+
break
|
1268
|
+
end
|
1269
|
+
end
|
1270
|
+
end
|
1271
|
+
|
1272
|
+
# Make sure that foreign keys with a placeholder are never null OR required_if.
|
1273
|
+
unless placeholder.nil?
|
1274
|
+
referencing_column_data = @schema_data[rc_exploded[0]][rc_exploded[1]][rc_exploded[2]]
|
1275
|
+
if referencing_column_data[FLAG].is_a?(String)
|
1276
|
+
flag_result = Blufin::YmlCommon::extract_flags(referencing_column_data[FLAG])
|
1277
|
+
add_error(nil, rc_exploded[0], rc_exploded[1], rc_exploded[2], "FK cannot have a #{FLAG_NULLABLE} #{FLAG} because it has a placeholder in: #{referencing_column_parent}", placeholder) if flag_result[0].nullable
|
1278
|
+
add_error(nil, rc_exploded[0], rc_exploded[1], rc_exploded[2], "FK cannot have a #{REQUIRED_IF} property because it has a placeholder in: #{referencing_column_parent}", placeholder) if referencing_column_data.has_key?(REQUIRED_IF)
|
1279
|
+
end
|
1280
|
+
end
|
1281
|
+
|
1282
|
+
# Make sure the reference name matches.. IE app.ebay_user.id is referenced by ebay_user_id
|
1283
|
+
unless rc_exploded[2] =~ /[a-z_]*#{expected_reference_end}/
|
1284
|
+
add_error(nil, rc_exploded[0], rc_exploded[1], rc_exploded[2], "FK naming convention not respected, expected field to end in: #{expected_reference_end}", "Got: #{rc_exploded[2]}")
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
# Make sure the column matches regex format.
|
1288
|
+
unless fk_is_valid(referencing_column)
|
1289
|
+
add_error(nil, rc_exploded[0], rc_exploded[1], rc_exploded[2], 'Foreign key source column not in correct format: app.table.column', referencing_column)
|
1290
|
+
return
|
1291
|
+
end
|
1292
|
+
|
1293
|
+
# Make sure the type matches that of the parent column.
|
1294
|
+
if referencing_column_type != fk_type
|
1295
|
+
unless referencing_column_type == TYPE_INT && fk_type == TYPE_INT_AUTO
|
1296
|
+
add_error(nil, rc_exploded[0], rc_exploded[1], rc_exploded[2], "FK type mismatch. Expected: #{fk_type}", "got: #{referencing_column_type}")
|
1297
|
+
end
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
unless @schema_data[rc_exploded[0]] != nil && @schema_data[rc_exploded[0]][rc_exploded[1]] != nil && @schema_data[rc_exploded[0]][rc_exploded[1]][rc_exploded[2]] != nil
|
1301
|
+
raise RuntimeError, "#{referencing_column} @schema_data not found."
|
1302
|
+
end
|
1303
|
+
end
|
1304
|
+
end
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
# Validate all he Foreign Key placeholders.
|
1308
|
+
# @return void
|
1309
|
+
def validate_foreign_keys_placeholders
|
1310
|
+
if !@schema_fks_placeholders.nil? && @schema_fks_placeholders.any?
|
1311
|
+
placeholder_error = false
|
1312
|
+
placeholder_error_view = []
|
1313
|
+
placeholder_circular_error = false
|
1314
|
+
placeholder_circular_dependencies = []
|
1315
|
+
# Make sure that FK place-holders are correct.
|
1316
|
+
@schema_fks_placeholders.each do |parent, placeholders|
|
1317
|
+
parent_split = parent.split('.')
|
1318
|
+
parent_schema = parent_split[0]
|
1319
|
+
parent_table = parent_split[1]
|
1320
|
+
# Loop Placeholders.
|
1321
|
+
placeholders.each do |placeholder|
|
1322
|
+
placeholder_type = RESOURCE_TYPE_OBJECT
|
1323
|
+
placeholder_type = RESOURCE_TYPE_OBJECT_LIST if placeholder =~ /\A[a-z_.]+\[\]/
|
1324
|
+
placeholder_type = RESOURCE_TYPE_OBJECT_LINK if placeholder =~ /\A[a-z_.]+\[#{LINK}\]/
|
1325
|
+
# Remove '[]' or '[link]' for multi-nested placeholders.
|
1326
|
+
placeholder_dup = remove_placeholder_trailing_braces(placeholder)
|
1327
|
+
child = placeholder_dup.split('.')
|
1328
|
+
child_schema = child[0]
|
1329
|
+
child_table = child[1]
|
1330
|
+
if @schema_data[child_schema] != nil && @schema_data[child_schema][child_table] != nil
|
1331
|
+
if placeholder =~ /\A[a-z_.]+\[#{LINK}\]/
|
1332
|
+
|
1333
|
+
# TODO - FINISH (OR REMOVE)
|
1334
|
+
|
1335
|
+
else
|
1336
|
+
# Add error if CHILD TABLE doesn't match PARENT TABLE (IE: person_customer → ebay_user = NO MATCH)
|
1337
|
+
if child_table =~ /\A#{parent_table}_/
|
1338
|
+
placeholder_error_view << " \x1B[38;5;240m#{parent_table} \xe2\x86\x92 #{child_table}\x1B[0m"
|
1339
|
+
else
|
1340
|
+
placeholder_error = true
|
1341
|
+
placeholder_error_view << " \x1B[38;5;196m#{parent_table} \xe2\x86\x92 #{child_table}\x1B[0m"
|
1342
|
+
next
|
1343
|
+
end
|
1344
|
+
fk_found = false
|
1345
|
+
@schema_data[child_schema][child_table].each do |referenced_table_data|
|
1346
|
+
# Skip Placeholders (in referenced table)
|
1347
|
+
next if referenced_table_data[0] =~ /\A(#{VALID_SCHEMAS_REGEX})\.[a-z_]+\[\]\z/ || referenced_table_data[0] =~ /\A(#{VALID_SCHEMAS_REGEX})\.[a-z_]+\z/
|
1348
|
+
unless referenced_table_data[1][FKEY].nil?
|
1349
|
+
if "#{parent}.#{ID}" == referenced_table_data[1][FKEY]
|
1350
|
+
fk_found = true
|
1351
|
+
# If OBJECT or OBJECT_LIST, add CHILD_OF and CHILD_TYPE properties (that will end up in metadata).
|
1352
|
+
if [RESOURCE_TYPE_OBJECT, RESOURCE_TYPE_OBJECT_LIST].include?(placeholder_type)
|
1353
|
+
@schema_data[child_schema][child_table][referenced_table_data[0]][CHILD_OF] = "#{parent_table}"
|
1354
|
+
@schema_data[child_schema][child_table][referenced_table_data[0]][CHILD_TYPE] = "DataType.#{placeholder_type}"
|
1355
|
+
end
|
1356
|
+
end
|
1357
|
+
end
|
1358
|
+
end
|
1359
|
+
# Add error if FK not found.
|
1360
|
+
add_error(nil, parent_schema, parent_table, nil, "Unreferenced placeholder. #{child_schema}.#{child_table} doesn't have FK to #{parent}", "#{placeholder}:") unless fk_found
|
1361
|
+
end
|
1362
|
+
else
|
1363
|
+
add_error(nil, parent_schema, parent_table, nil, "Placeholder reference doesn't exist, no such table.", "#{placeholder}:")
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
# Checks for circular dependencies.
|
1367
|
+
unless @schema_fks_placeholders[placeholder_dup].nil?
|
1368
|
+
@schema_fks_placeholders[placeholder_dup].each do |check_value|
|
1369
|
+
check_value = remove_placeholder_trailing_braces(check_value)
|
1370
|
+
if check_value == parent
|
1371
|
+
placeholder_circular_error = true
|
1372
|
+
placeholder_circular_dependencies << " \x1B[38;5;196m#{check_value} \xe2\x86\x92 #{placeholder_dup} \xe2\x86\x92 #{parent}\x1B[0m"
|
1373
|
+
else
|
1374
|
+
placeholder_circular_dependencies << " \x1B[38;5;240m#{check_value} \xe2\x86\x92 #{placeholder_dup} \xe2\x86\x92 #{parent}\x1B[0m"
|
1375
|
+
end
|
1376
|
+
end
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
end
|
1382
|
+
@errors_array.push({"Incorrect Placeholders! Child tables must follow convention: \x1B[38;5;220mparent \xe2\x86\x92 parent_child\x1B[0m" => placeholder_error_view}) if placeholder_error
|
1383
|
+
@errors_array.push({"You have circular dependencies within your placeholders: \x1B[38;5;220mparent \xe2\x86\x92 child \xe2\x86\x92 parent\x1B[0m" => placeholder_circular_dependencies}) if placeholder_circular_error
|
1384
|
+
end
|
1385
|
+
end
|
1386
|
+
|
1387
|
+
# Checks if there are any hard-coded schema definitions required and validates they exist (correctly).
|
1388
|
+
# @return void
|
1389
|
+
def validate_required_schema_definitions
|
1390
|
+
|
1391
|
+
begin
|
1392
|
+
|
1393
|
+
embedded_data = Blufin::SiteEmbedded::get_data
|
1394
|
+
|
1395
|
+
auth_level = Blufin::SiteAuth::get_auth_level
|
1396
|
+
auth_level_objects = Blufin::SiteAuth::AUTHENTICATION_LEVELS[auth_level]
|
1397
|
+
auth_level_objects.each do |auth_level_object|
|
1398
|
+
|
1399
|
+
embedded_object = embedded_data[auth_level_object]
|
1400
|
+
expected_schema = embedded_object[:schema]
|
1401
|
+
expected_table = embedded_object[:table]
|
1402
|
+
expected_data = embedded_object[:data]
|
1403
|
+
|
1404
|
+
# Must check here if embedded objects actually exist.
|
1405
|
+
if @schema_data[expected_schema].nil? || @schema_data[expected_schema][expected_table].nil?
|
1406
|
+
@yml_error_handler.add_error(Blufin::YmlErrorHandler::EMBEDDED_OBJECT_NO_TABLE, nil, nil, nil, "#{expected_schema}.#{expected_table}")
|
1407
|
+
next
|
1408
|
+
end
|
1409
|
+
|
1410
|
+
actual_data = @schema_data[expected_schema][expected_table]
|
1411
|
+
|
1412
|
+
expected_data.each do |field, data|
|
1413
|
+
|
1414
|
+
field_type = data[:type]
|
1415
|
+
field_key = field
|
1416
|
+
field_detail = "#{expected_schema}/#{expected_table}.yml - #{field_key}"
|
1417
|
+
field_is_object = false
|
1418
|
+
|
1419
|
+
case field_type
|
1420
|
+
when Blufin::ScannerJavaEmbeddedObjects::OBJECT
|
1421
|
+
field_key = "#{expected_schema}.#{field}"
|
1422
|
+
field_is_object = true
|
1423
|
+
when Blufin::ScannerJavaEmbeddedObjects::OBJECT_LIST
|
1424
|
+
field_key = "#{expected_schema}.#{field}[]"
|
1425
|
+
field_is_object = true
|
1426
|
+
when Blufin::ScannerJavaEmbeddedObjects::OBJECT_LINK
|
1427
|
+
field_key = "#{expected_schema}.#{field}[link]"
|
1428
|
+
field_is_object = true
|
1429
|
+
when Blufin::YmlSchemaValidator::TYPE_ENUM_SYSTEM
|
1430
|
+
field_type = "#{data[:type]}('#{data[:type_java]}')"
|
1431
|
+
else
|
1432
|
+
|
1433
|
+
end
|
1434
|
+
|
1435
|
+
# Check field exists.
|
1436
|
+
unless actual_data.keys.include?(field_key)
|
1437
|
+
# If field doesn't exist.
|
1438
|
+
@yml_error_handler.add_error(Blufin::YmlErrorHandler::EMBEDDED_FIELD_MISSING, "#{expected_schema}/#{expected_table}.yml", nil, nil, "#{field_key} \xe2\x86\x92 #{data.inspect}}")
|
1439
|
+
next
|
1440
|
+
end
|
1441
|
+
|
1442
|
+
unless field_is_object
|
1443
|
+
|
1444
|
+
actual_type = actual_data[field_key]['type']
|
1445
|
+
actual_flag = actual_data[field_key]['flag']
|
1446
|
+
actual_fkey = actual_data[field_key]['fkey']
|
1447
|
+
|
1448
|
+
# Check type.
|
1449
|
+
@yml_error_handler.add_error(Blufin::YmlErrorHandler::EMBEDDED_FIELD_WRONG_TYPE, field_detail, nil, nil, Blufin::YmlErrorHandler::error_expected_actual(data[:type], actual_type)) unless actual_type == field_type
|
1450
|
+
|
1451
|
+
# Check flags.
|
1452
|
+
if data[:flag].nil?
|
1453
|
+
@yml_error_handler.add_error(Blufin::YmlErrorHandler::EMBEDDED_FLAGS_INVALID, field_detail, nil, nil, Blufin::YmlErrorHandler::error_expected_actual('[NO FLAGS]', actual_flag)) unless actual_flag.nil?
|
1454
|
+
else
|
1455
|
+
unless actual_flag == data[:flag].join(' ')
|
1456
|
+
@yml_error_handler.add_error(Blufin::YmlErrorHandler::EMBEDDED_FLAGS_INVALID, field_detail, nil, nil, Blufin::YmlErrorHandler::error_expected_actual(data[:flag].join(' '), actual_flag))
|
1457
|
+
end
|
1458
|
+
end
|
1459
|
+
|
1460
|
+
# Check fkey.
|
1461
|
+
if data[:fkey].nil?
|
1462
|
+
@yml_error_handler.add_error(Blufin::YmlErrorHandler::EMBEDDED_FKEY_INVALID, field_detail, nil, nil, Blufin::YmlErrorHandler::error_expected_actual('[NO FKEY]', actual_fkey)) unless actual_fkey.nil?
|
1463
|
+
else
|
1464
|
+
@yml_error_handler.add_error(Blufin::YmlErrorHandler::EMBEDDED_FKEY_INVALID, field_detail, nil, nil, Blufin::YmlErrorHandler::error_expected_actual(data[:fkey], actual_fkey)) unless actual_fkey == data[:fkey]
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
# Check encrypted.
|
1468
|
+
if data[:encrypted]
|
1469
|
+
@yml_error_handler.add_error(Blufin::YmlErrorHandler::EMBEDDED_MUST_BE_ENCRYPTED, field_detail, nil, nil, 'Currently not encrypted.') if data[:encrypted] && (actual_data[field_key]['encrypted'].nil? || !actual_data[field_key]['encrypted'])
|
1470
|
+
end
|
1471
|
+
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
end
|
1475
|
+
|
1476
|
+
end
|
1477
|
+
|
1478
|
+
rescue Exception => e
|
1479
|
+
|
1480
|
+
# TODO - Was originally (intentionally) left blank but now re-activated even though it throws a "Double" Exception. - 6/6/17.
|
1481
|
+
# Hits here when config/config.yml has errors but needs to be handled better.
|
1482
|
+
# Maybe work on this once this exception hits again and you re-read this comment :) - 3/17/19.
|
1483
|
+
Blufin::Terminal::print_exception(e)
|
1484
|
+
|
1485
|
+
end
|
1486
|
+
|
1487
|
+
end
|
1488
|
+
|
1489
|
+
# Checks HTTP methods are correct.
|
1490
|
+
# @return void
|
1491
|
+
def validate_http_methods
|
1492
|
+
|
1493
|
+
# Make sure that REQUIRED and REQUIRED_IF objects don't have a POST.
|
1494
|
+
@schema_resources.each do |key, resource|
|
1495
|
+
schema = resource[:schema]
|
1496
|
+
table = resource[:table]
|
1497
|
+
parent = resource[:parent]
|
1498
|
+
methods_internal = resource[:methods_internal].keys
|
1499
|
+
methods_oauth = resource[:methods_oauth].keys
|
1500
|
+
if resource[:type] == RESOURCE_TYPE_OBJECT
|
1501
|
+
parent_definition_data = @schema_data[schema][parent]["#{schema}.#{table}"]
|
1502
|
+
raise RuntimeError, 'parent_definition_data not found.' if parent_definition_data.nil? || parent_definition_data == ''
|
1503
|
+
if parent_definition_data.has_key?(REQUIRED) || parent_definition_data.has_key?(REQUIRED_IF)
|
1504
|
+
@error_handler.add_error(Blufin::YmlErrorHandler::API_METHOD_INVALID_FOR_REQUIRED_OBJECT, "#{schema}/#{table}.yml", 'config', 'internal', methods_internal.join(', ')) if methods_internal.include?(Blufin::YmlConfigValidator::POST) || methods_internal.include?(Blufin::YmlConfigValidator::DELETE)
|
1505
|
+
@error_handler.add_error(Blufin::YmlErrorHandler::API_METHOD_INVALID_FOR_REQUIRED_OBJECT, "#{schema}/#{table}.yml", 'config', 'oauth', methods_internal.join(', ')) if methods_oauth.include?(Blufin::YmlConfigValidator::POST) || methods_oauth.include?(Blufin::YmlConfigValidator::DELETE)
|
1506
|
+
end
|
1507
|
+
end
|
1508
|
+
end
|
1509
|
+
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
# Validate config: section.
|
1513
|
+
# @return void
|
1514
|
+
def validate_config_section
|
1515
|
+
result = validate(@site, %W(#{Blufin::Site::PATH_TO_YML_API_SCHEMA}/#{APP} #{Blufin::Site::PATH_TO_YML_API_SCHEMA}/#{COMMON}), STRUCTURE, @error_handler)
|
1516
|
+
result.each do |key, data|
|
1517
|
+
key_split = key.split('/')
|
1518
|
+
schema = key_split[key_split.length - 2]
|
1519
|
+
table = key_split[key_split.length - 1].gsub(/(\.)[A-Za-z0-9]+\z/, '')
|
1520
|
+
@schema_config[schema] = {} if @schema_config[schema].nil?
|
1521
|
+
@schema_config[schema][table] = data['config'].nil? ? {} : data['config']
|
1522
|
+
end
|
1523
|
+
result = validate(@site, %W(#{PATH_TO_CONFIG_YML}), STRUCTURE, @error_handler, true)
|
1524
|
+
result.each do |key, data|
|
1525
|
+
if data.is_a?(Hash) && !data[CONFIG].nil?
|
1526
|
+
key_split = key.split('/')
|
1527
|
+
schema = key_split[key_split.length - 2]
|
1528
|
+
table = key_split[key_split.length - 1].gsub(/(\.)[A-Za-z0-9]+\z/, '')
|
1529
|
+
@schema_config[schema] = {} if @schema_config[schema].nil?
|
1530
|
+
@schema_config[schema][table] = data[CONFIG].nil? ? {} : data[CONFIG]
|
1531
|
+
end
|
1532
|
+
end
|
1533
|
+
end
|
1534
|
+
|
1535
|
+
# Builds the final end-point hash that is used to generate all the end-points, etc.
|
1536
|
+
# @return void
|
1537
|
+
def build_resources
|
1538
|
+
|
1539
|
+
schema_table_array = []
|
1540
|
+
@schema_data.each do |schema, schema_data|
|
1541
|
+
unless schema_data.nil?
|
1542
|
+
schema_data.each do |table_name, table_data|
|
1543
|
+
1 == 1 if table_data # Stop IDE complaining.
|
1544
|
+
unless table_name.nil?
|
1545
|
+
schema_table_array << "#{schema}.#{table_name}"
|
1546
|
+
end
|
1547
|
+
end
|
1548
|
+
end
|
1549
|
+
end
|
1550
|
+
|
1551
|
+
# List of tables which have "parent-tables".
|
1552
|
+
tables_with_parents = {}
|
1553
|
+
|
1554
|
+
# Start populating @schema_end_points with tables that are CHILD or CHILD_MULTI.
|
1555
|
+
# This also generates the 'resource' string: IE -> sale/shipment/component
|
1556
|
+
@schema_fks.each do |key, value|
|
1557
|
+
key_split = key.split('.')
|
1558
|
+
key_schema = key_split[0]
|
1559
|
+
key_table = key_split[1]
|
1560
|
+
value.each do |fk_source|
|
1561
|
+
fks_split = fk_source.split(':')
|
1562
|
+
fks_split = fks_split[0].split('.')
|
1563
|
+
fks_schema = fks_split[0]
|
1564
|
+
fks_table = fks_split[1]
|
1565
|
+
# Checks if [sale_ebay] has [sale_] in it...
|
1566
|
+
if fks_table =~ /\A#{key_table}_/
|
1567
|
+
# Turns [sale_ebay] into [ebay]...
|
1568
|
+
child_table = fks_table.gsub(/\A#{key_table}_/, '')
|
1569
|
+
# Turns [sale_ebay] into [sale]...
|
1570
|
+
parent_table = fks_table.gsub(/_#{child_table}\z/, '')
|
1571
|
+
# Figures out what the end-point source is (to put in resource hash)...
|
1572
|
+
end_point_source = (tables_with_parents.keys.include?(parent_table)) ? tables_with_parents[parent_table][:resource] : key_table.gsub('_', '-')
|
1573
|
+
placeholder_key = "#{fks_schema}.#{parent_table}"
|
1574
|
+
if schema_table_array.include?(placeholder_key)
|
1575
|
+
placeholder_type = nil
|
1576
|
+
placeholder_matches = []
|
1577
|
+
if !@schema_fks_placeholders[placeholder_key].nil? && @schema_fks_placeholders[placeholder_key].any?
|
1578
|
+
@schema_fks_placeholders[placeholder_key].each do |placeholder|
|
1579
|
+
schema_table = "#{fks_schema}.#{fks_table}"
|
1580
|
+
if placeholder =~ /\A#{schema_table}\z/
|
1581
|
+
placeholder_matches << placeholder
|
1582
|
+
placeholder_type = RESOURCE_TYPE_OBJECT
|
1583
|
+
elsif placeholder =~ /\A#{schema_table}\[\]\z/
|
1584
|
+
placeholder_matches << placeholder
|
1585
|
+
placeholder_type = RESOURCE_TYPE_OBJECT_LIST
|
1586
|
+
end
|
1587
|
+
end
|
1588
|
+
end
|
1589
|
+
# Something like: sale/shipment/component ...
|
1590
|
+
resource_string = "#{end_point_source}/#{child_table.gsub('_', '-')}"
|
1591
|
+
add_error_with_multi_content(nil, key_schema, key_table, nil, "More than one type of placeholder found within #{key_schema}.#{key_table} for: #{resource_string}", 'Can only have 1!', placeholder_matches) if placeholder_matches.length > 1
|
1592
|
+
# Adds [sale_ebay] to an Array of tables which have "parent-tables".
|
1593
|
+
tables_with_parents[fks_table] = build_resource_hash_start(placeholder_type, fks_schema, fks_table, resource_string) unless placeholder_type.nil?
|
1594
|
+
end
|
1595
|
+
end
|
1596
|
+
end
|
1597
|
+
end
|
1598
|
+
|
1599
|
+
# Add LINKED and PARENT tables to @schema_end_points.
|
1600
|
+
@schema_data.each do |schema, table_data|
|
1601
|
+
table_data.each do |table, data|
|
1602
|
+
1 == 1 if data # Suppresses IDE error.
|
1603
|
+
st = "#{schema}.#{table}"
|
1604
|
+
if tables_with_parents.keys.include?(table)
|
1605
|
+
@schema_resources[st] = tables_with_parents[table].dup
|
1606
|
+
else
|
1607
|
+
if !@schema_fks_placeholders.nil? && @schema_fks_placeholders.any?
|
1608
|
+
@schema_fks_placeholders.each do |key, data|
|
1609
|
+
if !data.nil? && data.any?
|
1610
|
+
data.each do |ph|
|
1611
|
+
if ph =~ /#{schema}.#{table}\[#{LINK}\]/
|
1612
|
+
unless @schema_resources[st].nil?
|
1613
|
+
if @schema_resources[st][:type] != RESOURCE_TYPE_OBJECT_LINK
|
1614
|
+
raise RuntimeError, "This statement should never be reached. It means that table '#{st}' was expected to be be 'LINKED' but was also defined as: #{@schema_resources[st][:type]}"
|
1615
|
+
end
|
1616
|
+
end
|
1617
|
+
@schema_resources[st] = build_resource_hash_start(RESOURCE_TYPE_OBJECT_LINK, schema, table, table.gsub('_', '-'))
|
1618
|
+
ph_clean = remove_placeholder_trailing_braces(ph)
|
1619
|
+
# Whilst we're looping, might as well create the @schema_fks_links HASH.
|
1620
|
+
@schema_fks_links[ph_clean] = [] if @schema_fks_links[ph_clean].nil?
|
1621
|
+
@schema_fks_links[ph_clean] << key
|
1622
|
+
end
|
1623
|
+
end
|
1624
|
+
end
|
1625
|
+
end
|
1626
|
+
end
|
1627
|
+
@schema_resources[st] = build_resource_hash_start(RESOURCE_TYPE_PARENT, schema, table, table.gsub('_', '-')) if @schema_resources[st].nil?
|
1628
|
+
end
|
1629
|
+
end
|
1630
|
+
end
|
1631
|
+
|
1632
|
+
# Add :tree & :depth key to @schema_end_points.
|
1633
|
+
@schema_resources.each do |key, data|
|
1634
|
+
tree = []
|
1635
|
+
item_last = nil
|
1636
|
+
data_resource_split = data[:resource].split('/')
|
1637
|
+
data_resource_split.each_with_index do |fragment, idx|
|
1638
|
+
item_frag = fragment.gsub('-', '_')
|
1639
|
+
if idx > 0
|
1640
|
+
@schema_resources[key][:parent] = item_last if idx == (data_resource_split.length - 1)
|
1641
|
+
item_last = "#{tree[idx - 1].gsub('-', '_')}_#{item_frag}"
|
1642
|
+
tree << item_last
|
1643
|
+
else
|
1644
|
+
@schema_resources[key][:parent] = nil
|
1645
|
+
item_last = item_frag
|
1646
|
+
tree << item_last
|
1647
|
+
end
|
1648
|
+
end
|
1649
|
+
@schema_resources[key][:tree] = tree
|
1650
|
+
@schema_resources[key][:depth] = data_resource_split.length
|
1651
|
+
end
|
1652
|
+
|
1653
|
+
# Add :dependents key to @schema_end_points.
|
1654
|
+
@schema_resources.each do |key, data|
|
1655
|
+
deps = data[:tree].dup
|
1656
|
+
deps.pop
|
1657
|
+
if deps.any?
|
1658
|
+
deps.each do |dep|
|
1659
|
+
key = "#{data[:schema]}.#{dep}"
|
1660
|
+
@schema_resources[key][:dependents] = [] if @schema_resources[key][:dependents].nil?
|
1661
|
+
@schema_resources[key][:dependents] << data[:table]
|
1662
|
+
end
|
1663
|
+
end
|
1664
|
+
end
|
1665
|
+
|
1666
|
+
# Build @resource hash (for YmlSchemaOutputter)
|
1667
|
+
@schema_resources.each { |key, data| @yml_schema_outputter.add_resource(key, data) }
|
1668
|
+
|
1669
|
+
# Check that placeholders are correct (IE: Something this is a CHILD is not also LINKED somewhere else).
|
1670
|
+
if !@schema_fks_placeholders.nil? && @schema_fks_placeholders.any?
|
1671
|
+
placeholder_type_error = false
|
1672
|
+
placeholder_type_error_view = []
|
1673
|
+
@schema_fks_placeholders.each do |key, data|
|
1674
|
+
if !data.nil? && data.any?
|
1675
|
+
data.each do |ph|
|
1676
|
+
# Remove '[]' or '[link]' for multi-nested placeholders.
|
1677
|
+
schema_table = remove_placeholder_trailing_braces(ph)
|
1678
|
+
unless @schema_resources[schema_table].nil?
|
1679
|
+
if ph =~ /\A#{schema_table}\z/
|
1680
|
+
placeholder_type = RESOURCE_TYPE_OBJECT
|
1681
|
+
elsif ph =~ /\A#{schema_table}\[\]\z/
|
1682
|
+
placeholder_type = RESOURCE_TYPE_OBJECT_LIST
|
1683
|
+
elsif ph =~ /\A#{schema_table}\[#{LINK}\]\z/
|
1684
|
+
placeholder_type = RESOURCE_TYPE_OBJECT_LINK
|
1685
|
+
else
|
1686
|
+
raise RuntimeError, "#{schema_table} \xe2\x86\x92 All tables should match at least one placeholder 'type'."
|
1687
|
+
end
|
1688
|
+
if placeholder_type != @schema_resources[schema_table][:type]
|
1689
|
+
placeholder_type_error = true
|
1690
|
+
key_split = key.split('.')
|
1691
|
+
add_error(nil, key_split[0], key_split[1], nil, "Attempted to define as '#{placeholder_type}' but was already defined as '#{@schema_resources[schema_table][:type]}'.", ph)
|
1692
|
+
placeholder_type_error_view << "\x1B[38;5;196m#{key} \xe2\x86\x92 #{ph} \xe2\x86\x92 #{@schema_resources[schema_table][:type].upcase} \xe2\x80\x94 ERROR!\x1B[0m"
|
1693
|
+
else
|
1694
|
+
placeholder_type_error_view << "\x1B[38;5;84m#{key}\x1B[0m \xe2\x86\x92 #{ph} \xe2\x80\x94 #{@schema_resources[schema_table][:type].upcase} \xe2\x80\x94 OK!"
|
1695
|
+
end
|
1696
|
+
end
|
1697
|
+
end
|
1698
|
+
end
|
1699
|
+
end
|
1700
|
+
# Add errors (if any).
|
1701
|
+
@errors_array.push({"Cannot mix placeholder 'types'." => placeholder_type_error_view}) if placeholder_type_error
|
1702
|
+
end
|
1703
|
+
|
1704
|
+
@schema_config.each do |schema, schema_data|
|
1705
|
+
schema_data.each do |table, table_data|
|
1706
|
+
begin
|
1707
|
+
@schema_resources["#{schema}.#{table}"][:methods_internal] = {}
|
1708
|
+
@schema_resources["#{schema}.#{table}"][:methods_oauth] = {}
|
1709
|
+
unless @schema_resources["#{schema}.#{table}"].nil?
|
1710
|
+
table_data[CONFIG_INTERNAL].each { |key, value| @schema_resources["#{schema}.#{table}"][:methods_internal][key] = value } if table_data.has_key?(CONFIG_INTERNAL)
|
1711
|
+
table_data[CONFIG_OAUTH].each { |key, value| @schema_resources["#{schema}.#{table}"][:methods_oauth][key] = value } if table_data.has_key?(CONFIG_OAUTH)
|
1712
|
+
end
|
1713
|
+
rescue
|
1714
|
+
end
|
1715
|
+
end
|
1716
|
+
end
|
1717
|
+
|
1718
|
+
end
|
1719
|
+
|
1720
|
+
# Build the initial end-point hash but more work is done to it after.
|
1721
|
+
# @return Hash
|
1722
|
+
def build_resource_hash_start(type, schema, table, end_point)
|
1723
|
+
{
|
1724
|
+
:type => type,
|
1725
|
+
:schema => schema,
|
1726
|
+
:table => table,
|
1727
|
+
:resource => end_point
|
1728
|
+
}
|
1729
|
+
end
|
1730
|
+
|
1731
|
+
# Adds 'dependencies' to end-point array (and validates there are no duplicates in a table).
|
1732
|
+
# @return void
|
1733
|
+
def build_fks_dependencies
|
1734
|
+
@schema_resources.each do |key, data|
|
1735
|
+
dependencies = []
|
1736
|
+
@schema_fks.each do |sfk_key, sfk_data|
|
1737
|
+
sfk_data.each do |sfk_data_inner|
|
1738
|
+
sfk_split = sfk_data_inner.split('.')
|
1739
|
+
sfk = "#{sfk_split[0]}.#{sfk_split[1]}"
|
1740
|
+
if key == sfk
|
1741
|
+
sfk_key_split = sfk_key.split('.')
|
1742
|
+
dependency = "#{sfk_key_split[0]}.#{sfk_key_split[1]}"
|
1743
|
+
dependencies << dependency unless dependencies.include?(dependency)
|
1744
|
+
end
|
1745
|
+
end
|
1746
|
+
end
|
1747
|
+
@schema_fks_dependencies[key] = dependencies
|
1748
|
+
end
|
1749
|
+
end
|
1750
|
+
|
1751
|
+
# Validates @schema_resources.
|
1752
|
+
# Checks that all tables which are "Embedded" have all HTTP methods enabled -> GET, POST, PATCH, DELETE
|
1753
|
+
# @return void
|
1754
|
+
def validate_resources
|
1755
|
+
embedded_data = Blufin::SiteEmbedded::get_data
|
1756
|
+
auth_level = Blufin::SiteAuth::get_auth_level
|
1757
|
+
auth_level_objects = Blufin::SiteAuth::AUTHENTICATION_LEVELS[auth_level]
|
1758
|
+
raise 'This error occurred because you forgot to run Blufin::SiteAuth::init() somewhere...' if auth_level_objects.nil?
|
1759
|
+
auth_level_objects.each do |auth_level_object|
|
1760
|
+
schema = embedded_data[auth_level_object][:schema]
|
1761
|
+
table = embedded_data[auth_level_object][:table]
|
1762
|
+
return if @schema_resources["#{schema}.#{table}"].nil?
|
1763
|
+
http_methods = @schema_resources["#{schema}.#{table}"][:methods_internal].keys
|
1764
|
+
# Validate HTTP methods depending on schema.
|
1765
|
+
if schema == CONFIG
|
1766
|
+
@error_handler.add_error(Blufin::YmlErrorHandler::API_ENDPOINTS_NOT_ALLOWED, "#{schema}/#{table}.yml", 'config', 'internal', http_methods.join(', ')) if http_methods.include?(Blufin::YmlConfigValidator::POST) && http_methods.include?(Blufin::YmlConfigValidator::PATCH) && http_methods.include?(Blufin::YmlConfigValidator::DELETE)
|
1767
|
+
else
|
1768
|
+
@error_handler.add_error(Blufin::YmlErrorHandler::API_ENDPOINTS_MISSING, "#{schema}/#{table}.yml", 'config', 'internal', nil) unless http_methods.include?(Blufin::YmlConfigValidator::GET) && http_methods.include?(Blufin::YmlConfigValidator::POST) && http_methods.include?(Blufin::YmlConfigValidator::PATCH) && http_methods.include?(Blufin::YmlConfigValidator::DELETE)
|
1769
|
+
end
|
1770
|
+
end
|
1771
|
+
end
|
1772
|
+
|
1773
|
+
# Add a 'section:schema' error.
|
1774
|
+
# @return void
|
1775
|
+
def add_error(error_file, schema, table, column, error_message, error_detail)
|
1776
|
+
add_error_abstract(error_file, schema, table, column, error_message, error_detail)
|
1777
|
+
end
|
1778
|
+
|
1779
|
+
# Add a 'section:schema' error with multi-line content).
|
1780
|
+
# @return void
|
1781
|
+
def add_error_with_multi_content(error_file, schema, table, column, error_message, error_detail, multi_line_content)
|
1782
|
+
add_error_abstract(error_file, schema, table, column, error_message, error_detail, multi_line_content)
|
1783
|
+
end
|
1784
|
+
|
1785
|
+
# The abstract method for adding errors.
|
1786
|
+
# @return
|
1787
|
+
def add_error_abstract(error_file, schema, table, column, error_message, error_detail, multi_line_content = nil, section = 'schema')
|
1788
|
+
|
1789
|
+
error_object = Blufin::SchemaError.new
|
1790
|
+
error_object.multi_line_content = multi_line_content
|
1791
|
+
error_object.section = section
|
1792
|
+
|
1793
|
+
if schema.nil? && table.nil? && column.nil?
|
1794
|
+
error_object.schema_address = "\xe2\x80\x94"
|
1795
|
+
else
|
1796
|
+
schema_address = []
|
1797
|
+
schema_address << (schema.nil? ? nil : schema)
|
1798
|
+
schema_address << (table.nil? ? nil : table)
|
1799
|
+
schema_address << (column.nil? ? nil : column)
|
1800
|
+
schema_address.compact!
|
1801
|
+
error_object.schema_address = schema_address
|
1802
|
+
end
|
1803
|
+
|
1804
|
+
if error_file.nil?
|
1805
|
+
if !table.nil?
|
1806
|
+
error_object.file = "#{table}.yml"
|
1807
|
+
else
|
1808
|
+
error_object.file = "\xe2\x80\x94"
|
1809
|
+
end
|
1810
|
+
else
|
1811
|
+
if error_file == ERROR_MULTIPLE_FILES
|
1812
|
+
error_object.file = ERROR_MULTIPLE_FILES
|
1813
|
+
else
|
1814
|
+
file_parts = error_file.split('/')
|
1815
|
+
error_object.file = file_parts[file_parts.length - 1]
|
1816
|
+
end
|
1817
|
+
end
|
1818
|
+
|
1819
|
+
error_object.error_message = (error_message.nil? ? "\xe2\x80\x94" : error_message)
|
1820
|
+
error_object.error_detail = (error_detail.nil? ? "\xe2\x80\x94" : error_detail)
|
1821
|
+
|
1822
|
+
@error_handler.add_error_schema(error_object)
|
1823
|
+
|
1824
|
+
end
|
1825
|
+
|
1826
|
+
# Removed trailing [] or [link] from placeholders strings.
|
1827
|
+
# @return String
|
1828
|
+
def remove_placeholder_trailing_braces(placeholder)
|
1829
|
+
(placeholder =~ /\A[a-z_.]+\[#{LINK}\]/) ? placeholder.gsub("[#{LINK}]", '') : placeholder.gsub('[]', '')
|
1830
|
+
end
|
1831
|
+
|
1832
|
+
# Add to the global @schema_data Array.
|
1833
|
+
# @return void
|
1834
|
+
def add_schema_data(schema, table, column, data)
|
1835
|
+
|
1836
|
+
@schema_data[schema] = {} if @schema_data[schema].nil?
|
1837
|
+
@schema_data[schema][table] = {} if @schema_data[schema][table].nil?
|
1838
|
+
@schema_data[schema][table][column] = data
|
1839
|
+
|
1840
|
+
end
|
1841
|
+
|
1842
|
+
# Returns true if the column name matches regex for OBJECT, OBJECT_LIST or OBJECT_LINK
|
1843
|
+
# @return bool
|
1844
|
+
def self.column_is_object(column_name)
|
1845
|
+
column_name =~ /^(#{VALID_SCHEMAS_REGEX})\.[\w]+/ || column_name =~ /^(#{VALID_SCHEMAS_REGEX})\.[\w]+\[\]$/ || column_name =~ /^(#{VALID_SCHEMAS_REGEX})\.[\w]+\[#{LINK}\]$/
|
1846
|
+
end
|
1847
|
+
|
1848
|
+
end
|
1849
|
+
|
1850
|
+
end
|