Skip to content

Commit 9380dfd

Browse files
committed
Add typescript code generation
2 parents 8806a2b + ffc7f66 commit 9380dfd

File tree

6 files changed

+425
-165
lines changed

6 files changed

+425
-165
lines changed

src/Controller/coreORM.utils.part1.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ function util_get_entity_props_for_db_actions(entity::IEntity,
7272
columns_selection_and_mapping,
7373
entity_type)
7474
props = util_replace_enums_by_id_if_needed(props, orm_module, dbconn)
75+
props = util_replace_empty_vector_of_enums_by_missing(props)
7576
props = util_replace_dict_types(props, orm_module, dbconn)
7677

7778
end
@@ -114,6 +115,7 @@ function util_get_entity_props_for_comparison(entity::IEntity,
114115
columns_selection_and_mapping,
115116
entity_type)
116117
props = util_replace_enums_by_id_if_needed(props,orm_module,dbconn)
118+
props = util_replace_empty_vector_of_enums_by_missing(props)
117119
props = util_replace_dict_types(props,orm_module,dbconn)
118120

119121
return(props)
@@ -421,6 +423,8 @@ function util_dict2entity(props_dict::Dict{Symbol,T},
421423
# @info props_dict[fsymbol]
422424
props_dict[fsymbol] =
423425
PostgresORMUtil.postgresql_string_array_2_string_vector(props_dict[fsymbol])
426+
elseif ftype <: Vector{T} where T <: Base.Enums.Enum
427+
props_dict[fsymbol] = string2vector_of_enums(ftype,props_dict[fsymbol])
424428
elseif ftype <: Enum
425429

426430
if ismissing(props_dict[fsymbol])
@@ -899,6 +903,23 @@ function util_replace_enums_by_id_if_needed(props::Dict,
899903
return props
900904
end
901905

906+
"""
907+
util_replace_empty_vector_of_enums_by_missing(props::Dict)
908+
909+
Replace empty vector of an enum by missing. This allows developers to avoid testing both for
910+
nullity and emptiness of the property
911+
"""
912+
function util_replace_empty_vector_of_enums_by_missing(props::Dict)
913+
for (prop_symbol, prop_val) in props
914+
if typeof(prop_val) <: Vector{T} where T <: Base.Enums.Enum
915+
if isempty(prop_val)
916+
props[prop_symbol] = missing
917+
end
918+
end
919+
end
920+
return props
921+
end
922+
902923
function util_replace_dict_types(props::Dict,
903924
orm_module::Module,
904925
dbconn::LibPQ.Connection)

src/PostgresORM.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ module PostgresORM
3939
# include("./web-api-definition.jl")
4040
# end
4141

42-
greet() = return ("Hello World!")
42+
greet() = return ("Hello World from PostgresORM!")
4343

4444
module PostgresORMUtil
4545
using ..PostgresORM
@@ -48,7 +48,8 @@ module PostgresORM
4848
export opendbconn, closedbconn, dict2namedtuple, namedtuple2dict, tovector,
4949
get_nonmissing_typeof_uniontype, dataframe2vector_of_namedtuples,
5050
dataframerow2namedtuple, getdictvalues, getpropertiesvalues,
51-
setpropertiesvalues!, remove_spaces_and_split, diff_dict, string2enum,
51+
setpropertiesvalues!, remove_spaces_and_split, diff_dict,
52+
string2enum, string2vector_of_enums,
5253
int2enum, enum2int, dictstringkeys2symbol, dictnothingvalues2missing,
5354
getproperties_asdict, string2zoneddatetime
5455

@@ -108,8 +109,9 @@ module PostgresORM
108109
using ..CRUDType
109110
using ..PostgresORMUtil, ..SchemaInfo
110111
# using .ModificationORM # no need (because ModificationORM is a children module ?)
111-
using Tables, DataFrames, Query, LibPQ, Dates, UUIDs, TickTock
112+
using Tables, DataFrames, Query, LibPQ, Dates, TimeZones, UUIDs, TickTock
112113
include("./Tool/Tool.jl")
114+
include("./Tool/Tool-typescript.jl")
113115

114116
end # module Tool
115117

src/PostgresORMUtil/utils.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,27 @@ function string2enum(enumType::DataType,str::String)
137137

138138
end
139139

140+
"""
141+
"""
142+
function string2vector_of_enums(vectorOfEnumsTypes::Type{Vector{T}},
143+
str::Union{String,Missing}) where T <: Base.Enums.Enum
144+
enumType = eltype(vectorOfEnumsTypes)
145+
146+
if ismissing(str)
147+
return missing
148+
end
149+
150+
# If no element in the array return an empty array
151+
if str == "{}"
152+
return T[]
153+
end
154+
155+
chop(str,head = 1,tail = 1) |> n -> split(n,",") |>
156+
n -> string.(n) |>
157+
n -> string2enum.(enumType,n)
158+
159+
end
160+
140161
function string2zoneddatetime(str)
141162
# eg. "2019-09-03T11:00:00.000Z"
142163
date_match_GMT =

src/Tool/Tool-typescript.jl

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
function generate_typescript_code(dbconn::LibPQ.Connection,
2+
outdir::String,
3+
relative_path_to_enum_dir::String
4+
;lang_code = "eng",
5+
module_name_for_all_schemas::Union{String,Missing} = "Model")
6+
7+
object_model = generate_object_model(
8+
dbconn,
9+
lang_code,
10+
module_name_for_all_schemas = module_name_for_all_schemas
11+
)
12+
13+
generate_typescript_enums_from_object_model(object_model, outdir)
14+
15+
generate_typescript_classes_from_object_model(object_model,
16+
outdir,
17+
relative_path_to_enum_dir)
18+
19+
end
20+
21+
22+
function generate_typescript_enums_from_object_model(object_model::Dict, outdir::String)
23+
24+
outdir = joinpath(outdir,"enum")
25+
26+
if !isdir(outdir)
27+
mkpath(outdir)
28+
end
29+
30+
enums = object_model[:enums]
31+
32+
for e in enums
33+
34+
type_name = e[:type_name]
35+
file_path = joinpath(outdir,"$type_name.ts")
36+
37+
content = "export enum $type_name {\n\n"
38+
39+
for (idx,v) in enumerate(e[:values])
40+
content *= " $v = $idx, \n"
41+
end
42+
43+
content *= "\n"
44+
content *= "}" # close enum
45+
46+
write(file_path,content)
47+
end
48+
49+
50+
51+
end
52+
53+
"""
54+
get_typescript_type_of_elt_type(julia_elt_type::String)
55+
56+
Eg. Model.Patient returns Patient
57+
"""
58+
function get_typescript_type_of_elt_type(julia_elt_type::String)
59+
60+
julia_elt_type = (last split)(julia_elt_type,".")
61+
62+
typescript_elt_type = try
63+
basic_elt_type = eval(Symbol(julia_elt_type))
64+
if basic_elt_type == Bool
65+
typescript_elt_type = "boolean"
66+
elseif basic_elt_type == String
67+
typescript_elt_type = "string"
68+
elseif (basic_elt_type <: Date
69+
|| basic_elt_type <: DateTime
70+
|| basic_elt_type <: ZonedDateTime)
71+
typescript_elt_type = "Date"
72+
elseif basic_elt_type <: Number
73+
typescript_elt_type = "number"
74+
end
75+
catch e
76+
typescript_elt_type = julia_elt_type
77+
end
78+
return typescript_elt_type
79+
end
80+
81+
"""
82+
get_typescript_elt_type(_field::Dict)
83+
84+
Eg. Vector{Model.Patient} returns Patient
85+
"""
86+
function get_typescript_elt_type(_field::Dict)
87+
chop(get_typescript_type(_field), tail = 2) # Remove the trailing '[]'
88+
end
89+
90+
"""
91+
get_typescript_type(_field::Dict)
92+
93+
Eg. Vector{Model.Patient} returns Patient[]
94+
"""
95+
function get_typescript_type(_field::Dict)
96+
97+
_regexVector = r"Vector{([a-zA-Z0-9._]+)}"
98+
99+
if (_m = match(_regexVector, _field[:field_type])) |> !isnothing
100+
elt_type_name = _m |>
101+
n -> string(n.captures[1])
102+
typescript_elt_type = get_typescript_type_of_elt_type(elt_type_name)
103+
return "$typescript_elt_type[]"
104+
else
105+
return get_typescript_type_of_elt_type(_field[:field_type])
106+
end
107+
108+
end
109+
110+
111+
function generate_typescript_classes_from_object_model(object_model::Dict,
112+
outdir::String,
113+
relative_path_to_enum_dir::String)
114+
115+
outdir = joinpath(outdir,"classes")
116+
117+
modules = object_model[:modules]
118+
structs = object_model[:structs]
119+
fields = object_model[:fields]
120+
121+
# Reset content
122+
for _struct in structs
123+
_struct[:struct_content] = ""
124+
end
125+
126+
# ################ #
127+
# Open the struct #
128+
# ################ #
129+
for _struct in structs
130+
str = "export class $(_struct[:name]) {\n\n"
131+
_struct[:struct_content] *= str
132+
end
133+
134+
# ################## #
135+
# Declare the fields #
136+
# ################## #
137+
for f in fields
138+
139+
if !haskey(f,:struct)
140+
@error f
141+
return
142+
end
143+
144+
_struct = f[:struct]
145+
146+
field_name = f[:name]
147+
typescript_type = get_typescript_type(f)
148+
str = " $field_name:$typescript_type;\n"
149+
_struct[:struct_content] *= str
150+
end
151+
152+
# #################### #
153+
# Open the constructor #
154+
# #################### #
155+
for _struct in structs
156+
str = "\n"
157+
str *= " constructor(_json:Object) {\n"
158+
_struct[:struct_content] *= str
159+
end
160+
161+
# ####################################################### #
162+
# Add the arguments declaration of the second constructor #
163+
# ####################################################### #
164+
for f in fields
165+
166+
_struct = f[:struct]
167+
168+
field_name = f[:name]
169+
indent = repeat(" ", 8)
170+
str = ""
171+
if (f[:is_onetoone] || f[:is_manytoone])
172+
str *= indent * "if (_json['$field_name'] != null) {\n"
173+
str *= indent * " " * "this.$field_name = new $(get_typescript_type_of_elt_type(f[:field_type]))(_json['$field_name']);\n"
174+
str *= indent * "}\n"
175+
elseif f[:is_onetomany]
176+
elt_type = get_typescript_elt_type(f)
177+
str *= indent * "if (_json['$field_name'] != null) {\n"
178+
str *= indent * " " * "for (let e of _json['$field_name']) {\n"
179+
str *= indent * " " * "this.$field_name.push(new $elt_type(e));\n"
180+
str *= indent * " " * "}\n"
181+
str *= indent * "}\n"
182+
elseif f[:is_enum]
183+
str *= indent * "if (_json['$field_name'] != null) {\n"
184+
str *= indent * " " * "this.$field_name = Number($(get_typescript_type_of_elt_type(f[:field_type]))[_json['$field_name']]);\n"
185+
str *= indent * "}\n"
186+
elseif f[:is_vectorofenum]
187+
elt_type = get_typescript_elt_type(f)
188+
str *= indent * "if (_json['$field_name'] != null) {\n"
189+
str *= indent * " " * "for (let e of _json['$field_name']) {\n"
190+
str *= indent * " " * "this.$field_name.push(Number($elt_type[e]));\n"
191+
str *= indent * " " * "}\n"
192+
str *= indent * "}\n"
193+
else
194+
str *= indent * "this.$field_name = _json['$field_name'];\n"
195+
end
196+
_struct[:struct_content] *= str
197+
end
198+
199+
# ############################ #
200+
# Close the constructor #
201+
# ############################ #
202+
for _struct in structs
203+
str = " }\n"
204+
_struct[:struct_content] *= str
205+
end
206+
207+
# ################################################# #
208+
# Initialize the vector of imports for every struct #
209+
# ################################################# #
210+
for _struct in structs
211+
_struct[:imports] = []
212+
end
213+
214+
# ######################################################## #
215+
# Add the imports for enums, vetors of enum, complex types #
216+
# ######################################################## #
217+
for f in fields
218+
219+
_struct = f[:struct]
220+
if (f[:is_onetoone] || f[:is_manytoone])
221+
elt_type = get_typescript_type_of_elt_type(f[:field_type])
222+
pathToImport = joinpath("./",elt_type)
223+
push!(
224+
_struct[:imports],
225+
"import { $elt_type } from \"$pathToImport\""
226+
)
227+
elseif f[:is_onetomany]
228+
elt_type = get_typescript_elt_type(f)
229+
pathToImport = joinpath("./",elt_type)
230+
push!(
231+
_struct[:imports],
232+
"import { $elt_type } from \"$pathToImport\""
233+
)
234+
elseif f[:is_enum]
235+
elt_type = get_typescript_type_of_elt_type(f[:field_type])
236+
pathToImport = joinpath(relative_path_to_enum_dir,elt_type)
237+
push!(
238+
_struct[:imports],
239+
"import { $elt_type } from \"$pathToImport\""
240+
)
241+
elseif f[:is_vectorofenum]
242+
elt_type = get_typescript_elt_type(f)
243+
pathToImport = joinpath(relative_path_to_enum_dir,elt_type)
244+
push!(
245+
_struct[:imports],
246+
"import { $elt_type } from \"$pathToImport\""
247+
)
248+
end
249+
250+
end
251+
# Add the string of imports
252+
for _struct in structs
253+
if length(_struct[:imports]) > 0
254+
stringOfImports = join(unique(_struct[:imports]),";\n") * ";\n\n"
255+
_struct[:struct_content] = stringOfImports * _struct[:struct_content]
256+
end
257+
end
258+
259+
# ################ #
260+
# Close the struct #
261+
# ################ #
262+
for _struct in structs
263+
str = "\n} "
264+
_struct[:struct_content] *= str
265+
end
266+
267+
# ############## #
268+
# Write to files #
269+
# ############## #
270+
271+
# Empty the modules dirs
272+
for _module in modules
273+
module_dir = joinpath(outdir,_module[:name])
274+
rm(module_dir, recursive=true, force = true)
275+
mkpath(module_dir)
276+
end
277+
278+
279+
# Write the content of structs to files
280+
for _struct in structs
281+
module_dir = joinpath(outdir,_struct[:module][:name])
282+
if !isdir(module_dir)
283+
mkpath(module_dir)
284+
end
285+
file_path = joinpath(module_dir,"$(_struct[:name]).ts")
286+
write(file_path,_struct[:struct_content])
287+
end
288+
289+
end

0 commit comments

Comments
 (0)