feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
124
frontend/infra/idl/idl-parser/src/common/extension_type.ts
Normal file
124
frontend/infra/idl/idl-parser/src/common/extension_type.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const POSITIONS = [
|
||||
'query',
|
||||
'body',
|
||||
'path',
|
||||
'header',
|
||||
'entire_body',
|
||||
'raw_body',
|
||||
'status_code',
|
||||
];
|
||||
|
||||
export const SERIALIZERS = ['json', 'form', 'urlencoded'];
|
||||
|
||||
export const UPPERCASE_METHODS = [
|
||||
'GET',
|
||||
'POST',
|
||||
'PUT',
|
||||
'DELETE',
|
||||
'PATCH',
|
||||
'HEAD',
|
||||
];
|
||||
|
||||
export const LOWERCASE_METHODS = [
|
||||
'get',
|
||||
'post',
|
||||
'put',
|
||||
'delete',
|
||||
'patch',
|
||||
'head',
|
||||
];
|
||||
|
||||
export const SERVICE_EXTENSTION_CONFIG_KEYS = ['uri_prefix'];
|
||||
|
||||
export const FUNCTION_EXTENSTION_CONFIG_KEYS = [
|
||||
'serializer',
|
||||
'uri',
|
||||
'method',
|
||||
'group',
|
||||
'custom',
|
||||
'version',
|
||||
...LOWERCASE_METHODS,
|
||||
];
|
||||
|
||||
export const FIELD_EXTENSTION_CONFIG_KEYS = [
|
||||
'position',
|
||||
'key',
|
||||
'web_type',
|
||||
'value_type',
|
||||
'tag',
|
||||
...POSITIONS,
|
||||
];
|
||||
|
||||
export type Position =
|
||||
| 'query'
|
||||
| 'body'
|
||||
| 'path'
|
||||
| 'header'
|
||||
| 'entire_body'
|
||||
| 'raw_body'
|
||||
| 'status_code';
|
||||
|
||||
export type Serializer = 'json' | 'form' | 'urlencoded';
|
||||
|
||||
export type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD';
|
||||
|
||||
export interface ServiceExtensionConfig {
|
||||
uri_prefix?: string;
|
||||
}
|
||||
|
||||
export interface FunctionExtensionConfig {
|
||||
serializer?: Serializer;
|
||||
uri?: string;
|
||||
method?: Method;
|
||||
group?: string;
|
||||
custom?: string;
|
||||
// NOTE: used in bytedance
|
||||
version?: string;
|
||||
|
||||
get?: string;
|
||||
post?: string;
|
||||
put?: string;
|
||||
delete?: string;
|
||||
}
|
||||
|
||||
export interface FieldExtensionConfig {
|
||||
position?: Position;
|
||||
key?: string;
|
||||
web_type?: string;
|
||||
value_type?: string;
|
||||
|
||||
query?: string;
|
||||
body?: string;
|
||||
path?: string;
|
||||
header?: string;
|
||||
entire_body?: string;
|
||||
raw_body?: string;
|
||||
status_code?: string;
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
export interface ExtensionConfig
|
||||
extends ServiceExtensionConfig,
|
||||
FunctionExtensionConfig,
|
||||
FieldExtensionConfig {}
|
||||
|
||||
export type ExtensionConfigStringKey = Exclude<
|
||||
keyof ExtensionConfig,
|
||||
'serializer' | 'method' | 'position'
|
||||
>;
|
||||
203
frontend/infra/idl/idl-parser/src/common/extension_util.ts
Normal file
203
frontend/infra/idl/idl-parser/src/common/extension_util.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
POSITIONS,
|
||||
SERIALIZERS,
|
||||
UPPERCASE_METHODS,
|
||||
LOWERCASE_METHODS,
|
||||
SERVICE_EXTENSTION_CONFIG_KEYS,
|
||||
FUNCTION_EXTENSTION_CONFIG_KEYS,
|
||||
FIELD_EXTENSTION_CONFIG_KEYS,
|
||||
type ExtensionConfig,
|
||||
type ExtensionConfigStringKey,
|
||||
type FieldExtensionConfig,
|
||||
type FunctionExtensionConfig,
|
||||
type ServiceExtensionConfig,
|
||||
type Serializer,
|
||||
type Method,
|
||||
type Position,
|
||||
} from './extension_type';
|
||||
|
||||
const goJsonTagRegExp = /^\s*json:(\\?[',"])(.*?)\1/;
|
||||
|
||||
function getTag(newtag = '', inputStr = '') {
|
||||
const tags: string[] = [];
|
||||
if (inputStr.includes('omitempty')) {
|
||||
tags.push('omitempty');
|
||||
}
|
||||
|
||||
if (inputStr.includes('required')) {
|
||||
tags.push('required');
|
||||
}
|
||||
|
||||
if (inputStr === 'int2str') {
|
||||
tags.push('int2str');
|
||||
}
|
||||
|
||||
if (tags.length === 0) {
|
||||
return newtag;
|
||||
}
|
||||
|
||||
return newtag + (newtag.length > 0 ? ',' : '') + tags.join(',');
|
||||
}
|
||||
|
||||
// NOTE: agw is a similar specification in Bytedance,
|
||||
// so we should cover the agw specification here.
|
||||
// some old rules should also be covered here.
|
||||
// eslint-disable-next-line complexity
|
||||
export function extractExtensionConfig(
|
||||
key: string,
|
||||
value: string,
|
||||
ignoreTag = false,
|
||||
) {
|
||||
const config: ExtensionConfig = {};
|
||||
|
||||
// an old rule: go.tag = "json:\"id,omitempty\""
|
||||
if (!ignoreTag && /^go\.tag/.test(key)) {
|
||||
const matches = value.match(goJsonTagRegExp);
|
||||
if (matches) {
|
||||
/* istanbul ignore next */
|
||||
const tagValues = (matches[2] || '').split(',');
|
||||
const tagKey = tagValues[0];
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (tagKey) {
|
||||
if (tagKey === '-') {
|
||||
config.tag = 'ignore';
|
||||
} else if (/^[a-zA-Z0-9_-]+$/.test(tagKey)) {
|
||||
config.key = tagKey;
|
||||
}
|
||||
}
|
||||
|
||||
const extraInfos = tagValues.slice(1).map(item => item.trim());
|
||||
if (extraInfos.includes('string')) {
|
||||
config.value_type = 'string';
|
||||
}
|
||||
|
||||
const newTag = getTag(config.tag, matches[2]);
|
||||
if (newTag) {
|
||||
config.tag = newTag;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// the agw rules: agw.source = 'header' or agw.target = 'http_code'
|
||||
if (key === 'source' || key === 'target') {
|
||||
/* istanbul ignore else */
|
||||
if (value === 'http_code') {
|
||||
config.position = 'status_code';
|
||||
} else if (POSITIONS.includes(value)) {
|
||||
config.position = value as Position;
|
||||
}
|
||||
} else if (key === 'method') {
|
||||
// the agw rule: agw.method = 'POST|GET'
|
||||
const method = value.split('|')[0];
|
||||
if (UPPERCASE_METHODS.includes(method)) {
|
||||
config.method = method as Method;
|
||||
}
|
||||
} else if (key === 'position') {
|
||||
if (POSITIONS.includes(value)) {
|
||||
config.position = value as Position;
|
||||
}
|
||||
} else if (key === 'serializer') {
|
||||
if (SERIALIZERS.includes(value)) {
|
||||
config.serializer = value as Serializer;
|
||||
}
|
||||
} else if (LOWERCASE_METHODS.includes(key)) {
|
||||
config.method = key.toUpperCase() as Method;
|
||||
config.uri = value;
|
||||
} else if (POSITIONS.includes(key)) {
|
||||
config.position = key as Position;
|
||||
|
||||
// cover an old rule: (api.body = "tags, omitempty")
|
||||
const parts = value.split(',');
|
||||
config.key = parts[0].trim().replace(/\[\]$/, '');
|
||||
const newTag = getTag(config.tag, value);
|
||||
if (newTag) {
|
||||
config.tag = newTag;
|
||||
}
|
||||
} else if (
|
||||
[
|
||||
'uri_prefix',
|
||||
'uri',
|
||||
'group',
|
||||
'custom',
|
||||
'version',
|
||||
'key',
|
||||
'web_type',
|
||||
'value_type',
|
||||
'tag',
|
||||
].includes(key)
|
||||
) {
|
||||
config[key as 'uri_prefix'] = value;
|
||||
} else if (key === 'req.headers') {
|
||||
// NOTE: Compliance with old specifications
|
||||
if (value.includes('x-www-form-urlencoded')) {
|
||||
config.serializer = 'urlencoded';
|
||||
} else if (value.includes('form-data')) {
|
||||
config.serializer = 'form';
|
||||
} else if (value.includes('json')) {
|
||||
config.serializer = 'json';
|
||||
}
|
||||
} else if (key === 'js_conv' && ['string', 'str', 'true'].includes(value)) {
|
||||
// NOTE: used in bytedance
|
||||
const newTag = getTag(config.tag, 'int2str');
|
||||
if (newTag) {
|
||||
config.tag = newTag;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export function filterConfig(
|
||||
config: ExtensionConfig,
|
||||
keys: string[],
|
||||
): ExtensionConfig {
|
||||
const filteredConfig: ExtensionConfig = {};
|
||||
Object.keys(config).forEach(key => {
|
||||
if (keys.includes(key)) {
|
||||
filteredConfig[key as ExtensionConfigStringKey] =
|
||||
config[key as ExtensionConfigStringKey];
|
||||
}
|
||||
});
|
||||
|
||||
return filteredConfig;
|
||||
}
|
||||
|
||||
export function filterFieldExtensionConfig(config: ExtensionConfig) {
|
||||
return filterConfig(
|
||||
config,
|
||||
FIELD_EXTENSTION_CONFIG_KEYS,
|
||||
) as FieldExtensionConfig;
|
||||
}
|
||||
|
||||
export function filterFunctionExtensionConfig(config: ExtensionConfig) {
|
||||
return filterConfig(
|
||||
config,
|
||||
FUNCTION_EXTENSTION_CONFIG_KEYS,
|
||||
) as FunctionExtensionConfig;
|
||||
}
|
||||
|
||||
export function filterServiceExtensionConfig(config: ExtensionConfig) {
|
||||
return filterConfig(
|
||||
config,
|
||||
SERVICE_EXTENSTION_CONFIG_KEYS,
|
||||
) as ServiceExtensionConfig;
|
||||
}
|
||||
Reference in New Issue
Block a user