UNPKG

node-vk-sdk

Version:

VK API SDK for Node.js

367 lines (293 loc) 11.1 kB
import TypescriptCodeGenerator from "../codegen/TypescriptCodeGenerator"; import ClassScheme from "../codegen/schema/ClassScheme"; import ClassField from "../codegen/schema/ClassField"; import StringType from "../codegen/types/StringType"; import NumberType from "../codegen/types/NumberType"; import VectorType from "../codegen/types/VectorType"; import {toCamelCase} from "../codegen/Utils"; import CustomType from "../codegen/types/CustomType"; import ApiMethodScheme from "../codegen/schema/ApiMethodScheme"; import ApiMethodParam from "../codegen/schema/ApiMethodParam"; import {Type} from "../codegen/types/Type"; import AnyType from "../codegen/types/AnyType"; import BooleanType from "../codegen/types/BooleanType"; import {writeFile} from "fs"; import SourceCode from "../codegen/SourceCode"; import AliasClassScheme from "../codegen/schema/AliasClassScheme"; import CustomPrimitiveScheme from "../codegen/schema/CustomPrimitiveScheme"; import CustomPrimitiveType from "../codegen/types/CustomPrimitiveType"; import {ConsoleLogger} from "../log/ConsoleLogger"; const objects = require('../vk-api-schema/objects.json') const responses = require('../vk-api-schema/responses.json') const methods = require('../vk-api-schema/methods.json') const codeGenerator = new TypescriptCodeGenerator() const logger = new ConsoleLogger() const MODELS_FILE = 'Models.ts' const RESPONSES_FILE = 'Responses.ts' const VKAPI_FILE = 'VKApi.ts' const METHODS_PROPS_FILE = 'MethodsProps.ts' const GENERATED_PATH = __dirname + '/../generated/' main() async function main() { try { await generate() } catch (e) { console.error(e) } } async function generate() { let modelsCode: SourceCode[] = [] // Hack types missing in schema let anyType = {type: 'any', properties: {}} let anyResponse = { type: 'object', properties: {response: {type: 'any'}} } objects.definitions['account_info'].properties['link_redirects'] = anyType objects.definitions['wall_wallpost'].properties['poster'] = anyType objects.definitions['callback_message_base'] = anyType objects.definitions['notifications_notification_item'] = anyType objects.definitions['photos_photo_falseable'] = anyType objects.definitions['poster'] = anyType // Not supported yet responses.definitions['groups_getSettings_response'] = anyResponse responses.definitions['messages_send_user_ids_response'] = anyResponse responses.definitions['secure_giveEventSticker_response'] = anyResponse // // Models // for (let className in objects.definitions) { let classScheme = jsonToClassScheme(className, objects.definitions[className]) if (classScheme instanceof AliasClassScheme) { classScheme = new AliasClassScheme( classScheme.name, classScheme.fields, new ClassScheme( classScheme.aliasClass.name.replace('Models.', ''), classScheme.aliasClass.fields ) ) } if (classScheme instanceof ClassScheme) { modelsCode.push(codeGenerator.generateInterface(classScheme)) } if (classScheme instanceof CustomPrimitiveScheme) { modelsCode.push(codeGenerator.generateCustomPrimitive(classScheme, true)) } } await saveToFile(GENERATED_PATH + MODELS_FILE, modelsCode.map(c => c.render()).join('\n\n')) logger.log('models generated') // // Responses // let responsesCode: SourceCode[] = [] for (let className in responses.definitions) { let classScheme = jsonToClassScheme(className, responses.definitions[className].properties.response, true) if (classScheme instanceof ClassScheme) { responsesCode.push(codeGenerator.generateInterface(classScheme)) } if (classScheme instanceof CustomPrimitiveScheme) { responsesCode.push(codeGenerator.generateCustomPrimitive(classScheme, true)) } } let responsesFileCode = new SourceCode() responsesFileCode.add('import * as Models from "./Models"') responsesFileCode.add('') responsesCode.forEach(c => responsesFileCode.append(c)) await saveToFile(GENERATED_PATH + RESPONSES_FILE, responsesFileCode.render()) logger.log('responses generated') // // Methods props // let methodsPropsCode: SourceCode[] = [] methods.methods.forEach((method: any) => { let scheme = jsonToMethodScheme(method) methodsPropsCode.push(codeGenerator.generateApiMethodParamsInterface(scheme)) }) await saveToFile(GENERATED_PATH + METHODS_PROPS_FILE, 'import * as Models from "./Models"\n\n' + methodsPropsCode.map(c => c.render()).join('\n\n')) logger.log('methods props generated') // // VKApi // let methodsCode: SourceCode[] = [] methods.methods.forEach((method: any) => { let scheme = jsonToMethodScheme(method) methodsCode.push(codeGenerator.generateApiMethod(scheme)) }) let apiClassCode = new SourceCode() apiClassCode.add('import {BaseVKApi} from "../api/BaseVKApi"') apiClassCode.add('import * as MethodsProps from "./MethodsProps"') apiClassCode.add('import * as Responses from "./Responses"') apiClassCode.add('') apiClassCode.add('export class VKApi extends BaseVKApi {') methodsCode.forEach(c => apiClassCode.append(c, 1)) apiClassCode.add('}') await saveToFile(GENERATED_PATH + VKAPI_FILE, apiClassCode.render()) logger.log('VKApi generated') } function jsonToMethodScheme(scheme: any): ApiMethodScheme { let params: ApiMethodParam[] = [] let paramsSet = new Set<string>() if (scheme.parameters) { for (let param of scheme.parameters) { if (paramsSet.has(param.name)) { logger.warn(`Duplicate parameter ${param.name} in ${scheme.name}`) continue } params.push(new ApiMethodParam( param.name, parseType(param, true), !!param.required, param.description, )) paramsSet.add(param.name) } } params.push( new ApiMethodParam( 'access_token', new StringType(), false, 'access token' ) ) return new ApiMethodScheme( scheme.name, params, new CustomType('Responses.' + toCamelCase(normalizePath(scheme.responses.response.$ref), true)), scheme.description, true ) } function jsonToClassScheme(name: string, scheme: any, forResponses = false): ClassScheme | CustomPrimitiveScheme { // just create alias here if (scheme.$ref) { return new AliasClassScheme( toCamelCase(name, true), [], jsonToClassScheme( 'Models.' + toCamelCase(normalizePath(scheme.$ref), true), getScheme(scheme.$ref) ) as ClassScheme ) } if (scheme.type !== 'object') { return new CustomPrimitiveScheme( toCamelCase(name, true), parseType(scheme, forResponses) ) } let fields: ClassField[] = [] let props = getSchemeClassProps(scheme) for (let propName in props) { let name = propName if (name == '2fa_required') name = 'twoFaRequired' if (name == 'class') name = 'schoolClass' fields.push( new ClassField( name, parseType(props[propName], forResponses), props[propName].description || '' ) ) } return new ClassScheme( toCamelCase(name, true), fields ) } function getSchemeClassProps(scheme: any): any { let props = {} if (scheme.properties) props = {...props, ...scheme.properties} if (scheme.allOf) { scheme.allOf.forEach((prop: any) => { props = {...props, ...getSchemeClassProps(prop)} }) } if (scheme.$ref) { let typeScheme = getScheme(scheme.$ref) props = {...props, ...getSchemeClassProps(typeScheme)} } return props } function parseType(scheme: any, forResponses = false): Type { let type = scheme.type if (type == 'integer' || type == 'number') return new NumberType() if (type == 'string') return new StringType() if (type == 'boolean') return new BooleanType() if (type == 'array') { if (scheme.items.oneOf) { return new VectorType(new AnyType()) } if (scheme.items.$ref) { let primitive = isPrimitive(scheme.items.$ref) let name = toCamelCase(normalizePath(scheme.items.$ref), true) if (forResponses) { if (primitive) return new VectorType(new CustomPrimitiveType('Models.' + name)) return new VectorType(new CustomType('Models.' + name)) } if (primitive) return new VectorType(new CustomPrimitiveType(name)) return new VectorType(new CustomType(name)) } return new VectorType(parseType(scheme.items, forResponses)) } if (type == 'any') { return new AnyType() } if (scheme.$ref) { let primitive = isPrimitive(scheme.$ref) let name = toCamelCase(normalizePath(scheme.$ref), true) if (forResponses) { if (primitive) return new CustomPrimitiveType('Models.' + name) return new CustomType('Models.' + name) } if (primitive) return new CustomPrimitiveType(name) return new CustomType(name) } if (Array.isArray(type)) { // hack ads_ad.ad_platform return new AnyType() } if (scheme.properties && scheme.properties.count && scheme.properties.items) { // convert { cont, items } to array return parseType(scheme.properties.items, forResponses) } if (scheme.allOf) { return new AnyType() } throw {'UNSUPPORTED TYPE': scheme} } function getScheme(ref: string): any { return objects.definitions[normalizePath(ref)] } function normalizePath(path: string): string { if (path.indexOf('objects.json#/definitions/') > -1) return path.replace('objects.json#/definitions/', '') if (path.indexOf('responses.json#/definitions/') > -1) return path.replace('responses.json#/definitions/', '') return path.replace('#/definitions/', '') } function isPrimitive(ref: string): boolean { let scheme = getScheme(ref) if (scheme.type !== 'object') return true return false } async function saveToFile(name: string, data: string): Promise<any> { return new Promise((resolve, reject) => { writeFile(name, data, err => { if (err) { reject(err) return } resolve(true) }) }) }