openapi_generate_typescript_fetch 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,226 @@
1
+ <%=
2
+ Gen.output.config = Gen.x.cfg['ts_indentation']
3
+ Gen.x.generator_info
4
+ %>
5
+
6
+ import type {CallExtras} from './schemas.d.ts';
7
+
8
+ <%=
9
+ # From https://stackoverflow.com/questions/1661197/what-characters-are-valid-for-javascript-variable-names#9337047
10
+ # and later comment.
11
+ # re = Regexp.new('^(?!(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$)[$_\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}][$_\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}\u200C\u200D\p{Mn}\p{Mc}\p{Nd}\p{Pc}]*$')
12
+ def allowed_name(str, default_name)
13
+ re = Regexp.new('^[$_\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}][$_\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}\u200C\u200D\p{Mn}\p{Mc}\p{Nd}\p{Pc}]*$')
14
+ ok = []
15
+ str.each_char do |char|
16
+ if ok.empty?
17
+ ok.push(char) if re.match(char)
18
+ else
19
+ if re.match('A' + char) # Not first so set something to precede.
20
+ ok.push(char)
21
+ else
22
+ ok.push('_') unless ok.last == '_'
23
+ end
24
+ end
25
+ end
26
+ ok.pop unless ok.empty? || ok.last != '_'
27
+ ok.join || default_name
28
+ end
29
+
30
+ out = []
31
+ # Has any server at top level?
32
+ # Default to server with '/' path.
33
+ servers = Gen.doc['servers'] || []
34
+ top = OpenAPISourceTools::ApiObjects::ServerAlternatives.new(servers)
35
+ Gen.doc[:servers] = top
36
+ Gen.doc[:servers].set_id = 0
37
+ overrides = []
38
+ opIds = []
39
+ opId2group = {}
40
+ group2opIds = {}
41
+ security_schemes = {}
42
+ (Gen.doc.dig('components', 'securitySchemes') || {}).each do |name, scheme|
43
+ security_schemes[name] = OpenAPISourceTools::ApiObjects::SecurityScheme.new(name, scheme)
44
+ end
45
+ security = OpenAPISourceTools::ApiObjects::SecurityAlternatives.new(
46
+ (Gen.doc['security'] || []).map { |s| OpenAPISourceTools::ApiObjects::SecurityRequirementObject.new(s, security_schemes) },
47
+ security_schemes)
48
+ security.set_id = 0
49
+ security_overrides = []
50
+ opId2sec_group = {}
51
+ sec_group2opIds = {}
52
+ # Find server alternatives for each path item, or use top-level.
53
+ # Set security for each operation object.
54
+ Gen.doc['paths'].each do |path, path_item|
55
+ servers = path_item['servers']
56
+ if servers.nil?
57
+ srvs = top
58
+ else
59
+ # If servers is exactly as at top level, this keeps it in separate group.
60
+ # Either servers array is an error or duplication is intentional.
61
+ srvs = OpenAPISourceTools::ApiObjects::ServerAlternatives.new(servers)
62
+ idx = overrides.index(srvs)
63
+ if idx.nil?
64
+ overrides.push(srvs)
65
+ srvs.set_id = overrides.size
66
+ else
67
+ srvs = overrides[index]
68
+ end
69
+ warn("Servers at path #{path} are identical to top level.") if top == srvs
70
+ end
71
+ path_item[:servers] = srvs
72
+ oos = OpenAPISourceTools::ApiObjects.operation_objects(path_item).values
73
+ oos.each do |oo|
74
+ oid = oo['operationId']
75
+ next if oid.nil?
76
+ opIds.push(oid)
77
+ opId2group[oid] = srvs
78
+ group2opIds[srvs.set_id] = group2opIds.fetch(srvs.set_id, []).push(oid)
79
+ sec = oo['security']
80
+ if sec.nil?
81
+ sec = security
82
+ else
83
+ sec = OpenAPISourceTools::ApiObjects::SecurityAlternatives.new(
84
+ sec.map { |s| OpenAPISourceTools::ApiObjects::SecurityRequirementObject.new(s, security_schemes) },
85
+ security_schemes)
86
+ idx = security_overrides.index(sec)
87
+ if idx.nil?
88
+ security_overrides.push(sec)
89
+ sec.set_id = security_overrides.size
90
+ else
91
+ sec = security_overrides[idx]
92
+ end
93
+ end
94
+ oo[:security] = sec
95
+ opId2sec_group[oid] = sec
96
+ sec_group2opIds[sec.set_id] = sec_group2opIds.fetch(sec.set_id, []).push(oid)
97
+ end
98
+ end
99
+
100
+ # Make server variable types, var name to string but names vary.
101
+ # ServerObject matches arguments type, function to use SVO checkers and make URL.
102
+ svos = []
103
+ svo2data = {}.compare_by_identity
104
+ svo2checker = {}.compare_by_identity
105
+ sos = []
106
+ so2data = {}.compare_by_identity
107
+ so2checker = {}.compare_by_identity
108
+ consts = Set.new
109
+ [ top ].concat(overrides).each do |sa|
110
+ sa.servers.each do |so|
111
+ index = sos.index(so)
112
+ if index.nil?
113
+ sos.push(so)
114
+ so2data[so] = so
115
+ index = sos.size
116
+ else
117
+ so2data[so] = sos[index]
118
+ end
119
+ so2checker[so] = LuckyCase.camel_case(allowed_name(so.url, "serverUrl#{index}"))
120
+ so.variables.each do |svo|
121
+ consts.add(svo.default)
122
+ svo.enum.each { |e| consts.add(e) }
123
+ index = svos.index { |x| x.default == svo.default && x.enum == svo.enum }
124
+ if index.nil?
125
+ index = svos.size
126
+ svos.push(svo)
127
+ svo2data[svo] = svo
128
+ svo2data[svo] = svos[index]
129
+ end
130
+ svo2checker[svo] = "serverVariable#{index}"
131
+ end
132
+ end
133
+ end
134
+
135
+ value2const = {}
136
+ consts.to_a.sort!.each do |s|
137
+ value2const[s] = "srvVarConst#{value2const.size}"
138
+ out.push("const #{value2const[s]} = '#{s}';")
139
+ end
140
+
141
+ # Behind the scenes a mapping from operationId to server URL string?
142
+ Gen.output.join(out)
143
+ %>
144
+
145
+ <%=
146
+ out = [ '// Server variable enum constants.' ]
147
+ enums2const = {}
148
+ svos.each do |svo|
149
+ next if svo.enum.empty?
150
+ key = svo.enum.join(', ')
151
+ unless enums2const.key?(key)
152
+ name = "srvVarEnumsConst#{enums2const.size}"
153
+ c = "const #{name} = [ #{svo.enum.map { |e| value2const[e] }.join(', ')} ];"
154
+ enums2const[key] = name
155
+ out.push(c)
156
+ end
157
+ end
158
+ out = [ '// No server variable enum constants.' ] if out.size == 1
159
+ Gen.output.join(out)
160
+ %>
161
+ <%=
162
+ out = [ '// Server variable value check functions.' ]
163
+ svos.each do |svo|
164
+ if svo.enum.empty?
165
+ s = <<EOB
166
+ function #{svo2checker[svo]}(value: string|undefined): string {
167
+ return (value === undefined) ? #{value2const[svo.default]} : value;
168
+ }
169
+ EOB
170
+ else
171
+ key = svo.enum.join(', ')
172
+ s = <<EOB
173
+ function #{svo2checker[svo]}(value: string|undefined): string {
174
+ if (value === undefined) {
175
+ return #{value2const[svo.default]};
176
+ }
177
+ if (#{enums2const[key]}.includes(value)) {
178
+ return value;
179
+ }
180
+ throw new Error(`Invalid server variable #{svo.name} value: ${value}`);
181
+ }
182
+ EOB
183
+ end
184
+ out.push(s)
185
+ end
186
+ out = [ '// No server variable value check functions.' ] if out.size == 1
187
+ Gen.output.join(out)
188
+ %>
189
+ <%=
190
+ out = [ '// Server URL assign argument types and functions.']
191
+ so2args = {}.compare_by_identity
192
+ sos.each do |so|
193
+ Gen.x.servers.functions.push(so2checker[so])
194
+ if so.variables.empty?
195
+ s = <<EOB
196
+ // No variables.
197
+ export function #{so2checker[so]}(): string {
198
+ return '#{so.url}';
199
+ }
200
+ EOB
201
+ out.push(s)
202
+ next
203
+ end
204
+ iname = "#{LuckyCase.pascal_case(so2checker[so])}Args"
205
+ so2args[so] = iname
206
+ Gen.x.servers.types.push(iname)
207
+ s = <<EOB
208
+ export type #{iname} = {
209
+ #{so.variables.map { |v| "#{v.name}#{v.default ? '?' : ''}: string;" }.join("\n ")}
210
+ }
211
+
212
+ export function #{so2checker[so]}(args: #{iname}): string {
213
+ #{so.variables.map { |v| "const #{v.name} = encodeURIComponent(#{svo2checker[v]}(args.#{v.name}));" }.join("\n ")}
214
+ return `#{so.url.gsub('{', '${')}`;
215
+ }
216
+ EOB
217
+ out.push(s)
218
+ end
219
+ Gen.x.server_data = { # Store for test generation.
220
+ server_objects: sos,
221
+ checkers: so2checker,
222
+ args: so2args
223
+ }
224
+ out = [ '// No server URL compose argument types or functions.'] if out.size == 1
225
+ Gen.output.join(out)
226
+ %>
@@ -0,0 +1,114 @@
1
+ <%=
2
+ Gen.output.config = Gen.x.cfg['ts_indentation']
3
+ Gen.x.generator_info
4
+ %>
5
+
6
+ import type {<%= %w[CallExtras].concat(Gen.x.schemas.import_types).join(', ') %>} from './schemas.d.ts';
7
+ import type { RequestInitModifier } from './callclasses.d.ts';
8
+ import type { EncoderFunction } from './schemas.d.ts';
9
+ // Type that holds all parameters suffixed by their type.
10
+
11
+ export type AllParameters = {
12
+ <%=
13
+ name2types = {}
14
+ Gen.doc['paths'].each do |path, path_item_object|
15
+ oos = OpenAPISourceTools::ApiObjects.operation_objects(path_item_object)
16
+ oos.each do |method, operation_object|
17
+ next if operation_object[:parameterless]
18
+ operation_object[:paraschemas].each do |ps|
19
+ types = name2types.fetch(ps[:param][:name], [])
20
+ types.push({
21
+ name: ps[:param][:name],
22
+ type: ps[:schema][:name],
23
+ ps: ps,
24
+ count: 0,
25
+ shared: '',
26
+ majority: false,
27
+ ooid: operation_object.object_id
28
+ })
29
+ name2types[ps[:param][:name]] = types
30
+ ps[:shared] = types.last
31
+ end
32
+ end
33
+ end
34
+ out = []
35
+ ap = {}
36
+ names = name2types.keys.sort!
37
+ names.each do |name|
38
+ types = name2types[name]
39
+ uniqs = types.map { |ti| ti[:type] }.uniq
40
+ type2count = {}
41
+ uniqs.each do |type|
42
+ type2count[type] = types.count { |ti| ti[:type] == type }
43
+ end
44
+ uniqs.sort! { |a, b| type2count[b] <=> type2count[a] }
45
+ if uniqs.size == 1 || uniqs.size > 1 && type2count[uniqs.first] > type2count[uniqs[1]]
46
+ out.push(" #{name}?: #{uniqs.first};")
47
+ types.select { |ti| ti[:type] == uniqs.first }.each do |ti|
48
+ ti[:majority] = true
49
+ ap[name] = ap.fetch(name, []).push(ti)
50
+ end
51
+ end
52
+ uniqs.each do |type|
53
+ combo = name + type
54
+ out.push(" #{combo}?: #{type};")
55
+ types.select { |ti| ti[:type] == type }.each do |ti|
56
+ ti[:shared] = combo
57
+ ti[:count] = type2count[type]
58
+ ap[combo] = ap.fetch(combo, []).push(ti)
59
+ end
60
+ end
61
+ end
62
+ Gen.doc[:all_params] = ap
63
+ Gen.output.join(out)
64
+ %>
65
+ }
66
+
67
+ export type Shared = {
68
+ serverUrl?: string;
69
+ params: AllParameters;
70
+ extras: CallExtras;
71
+ modifier: RequestInitModifier|null;
72
+ abortSignalable: boolean;
73
+ encoder: EncoderFunction|null;
74
+ }
75
+
76
+ export const shared: Shared = {
77
+ params: {},
78
+ extras: {},
79
+ modifier: null,
80
+ abortSignalable: false,
81
+ encoder: null
82
+ }
83
+
84
+ // Functions to get a partial of shared that contains exactly what callclass args type has.
85
+
86
+ <%=
87
+ out = []
88
+ Gen.doc['paths'].each do |path, path_item_object|
89
+ oos = OpenAPISourceTools::ApiObjects.operation_objects(path_item_object)
90
+ oos.each do |method, operation_object|
91
+ next if operation_object[:parameterless]
92
+ assigns = []
93
+ operation_object[:paraschemas].each do |ps|
94
+ ti = ps[:shared]
95
+ assigns.push(" if (shared.params.#{ti[:shared]} !== undefined) out.#{ti[:name]} = shared.params.#{ti[:shared]};")
96
+ if ti[:majority]
97
+ assigns.push(" else if (shared.params.#{ti[:name]} !== undefined) out.#{ti[:name]} = shared.params.#{ti[:name]};")
98
+ end
99
+ end
100
+ func_name = "shared#{operation_object[:args_name]}"
101
+ Gen.x.shared.functions.push(func_name)
102
+ operation_object[:shared] = func_name
103
+ s = <<EOB
104
+ export function #{func_name}(): Partial<#{operation_object[:args_name]}> {
105
+ const out: Partial<#{operation_object[:args_name]}> = {};
106
+ #{assigns.join("\n")}
107
+ return out;
108
+ }
109
+ EOB
110
+ out.push(s)
111
+ end
112
+ end
113
+ Gen.output.join(out)
114
+ %>