├── packages ├── thrift-server-core │ ├── .prettierignore │ ├── src │ │ ├── main │ │ │ ├── errors │ │ │ │ ├── index.ts │ │ │ │ ├── InputBufferUnderrunError.ts │ │ │ │ ├── TProtocolException.ts │ │ │ │ └── TApplicationException.ts │ │ │ ├── utils │ │ │ │ ├── normalizePath.ts │ │ │ │ ├── index.ts │ │ │ │ ├── appendThriftObject.ts │ │ │ │ ├── readThriftObject.ts │ │ │ │ └── readThriftMetadata.ts │ │ │ ├── transports │ │ │ │ ├── index.ts │ │ │ │ ├── TTransport.ts │ │ │ │ └── ThriftFrameCodec.ts │ │ │ ├── protocols │ │ │ │ ├── index.ts │ │ │ │ └── TProtocol.ts │ │ │ ├── index.ts │ │ │ ├── logger.ts │ │ │ └── binary.ts │ │ └── tests │ │ │ ├── thrift │ │ │ ├── add-service.thrift │ │ │ ├── exceptions.thrift │ │ │ ├── operation.thrift │ │ │ ├── shared.thrift │ │ │ ├── common.thrift │ │ │ └── calculator-service.thrift │ │ │ └── unit │ │ │ ├── types.ts │ │ │ └── parseJson.spec.ts │ ├── .prettierrc │ ├── NOTICE │ ├── tsconfig.json │ ├── tslint.json │ ├── package.json │ └── README.md ├── zipkin-tracing-hapi │ ├── src │ │ └── main │ │ │ └── index.ts │ ├── .prettierrc │ ├── tslint.json │ ├── tsconfig.json │ └── package.json ├── zipkin-tracing-express │ ├── src │ │ └── main │ │ │ ├── index.ts │ │ │ └── ZipkinTracingExpress.ts │ ├── .prettierrc │ ├── tsconfig.json │ ├── tslint.json │ └── package.json ├── thrift-client-context-filter │ ├── src │ │ └── main │ │ │ ├── index.ts │ │ │ ├── logger.ts │ │ │ └── ThriftClientContextFilter.ts │ ├── .prettierrc │ ├── tsconfig.json │ ├── tslint.json │ ├── package.json │ └── README.md ├── thrift-client-timing-filter │ ├── src │ │ └── main │ │ │ ├── index.ts │ │ │ ├── logger.ts │ │ │ └── ThriftClientTimingFilter.ts │ ├── .prettierrc │ ├── tsconfig.json │ ├── tslint.json │ ├── package.json │ └── README.md ├── thrift-client-zipkin-filter │ ├── src │ │ └── main │ │ │ ├── index.ts │ │ │ └── ThriftClientZipkinFilter.ts │ ├── .prettierrc │ ├── tsconfig.json │ ├── tslint.json │ ├── package.json │ └── README.md ├── thrift-client │ ├── src │ │ └── main │ │ │ ├── index.ts │ │ │ ├── logger.ts │ │ │ ├── connections │ │ │ ├── NullConnection.ts │ │ │ ├── utils.ts │ │ │ ├── pool.ts │ │ │ └── index.ts │ │ │ └── types.ts │ ├── .prettierrc │ ├── tsconfig.json │ ├── tslint.json │ └── package.json ├── thrift-client-ttwitter-filter │ ├── src │ │ ├── main │ │ │ ├── index.ts │ │ │ └── logger.ts │ │ └── ttwitter │ │ │ └── com │ │ │ └── creditkarma │ │ │ └── finagle │ │ │ └── thrift │ │ │ ├── AnnotationType.ts │ │ │ ├── index.ts │ │ │ ├── constants.ts │ │ │ ├── UpgradeReply.ts │ │ │ ├── ConnectionOptions.ts │ │ │ ├── ClientId.ts │ │ │ ├── Delegation.ts │ │ │ └── RequestContext.ts │ ├── .prettierrc │ ├── tsconfig.json │ ├── tslint.json │ ├── package.json │ └── README.md ├── thrift-integration │ ├── .prettierignore │ ├── src │ │ ├── client │ │ │ └── tracing │ │ │ │ ├── config.ts │ │ │ │ └── mock-collector.ts │ │ ├── thrift │ │ │ ├── add-service.thrift │ │ │ ├── exceptions.thrift │ │ │ ├── operation.thrift │ │ │ ├── common.thrift │ │ │ ├── shared.thrift │ │ │ └── calculator-service.thrift │ │ ├── config.ts │ │ ├── hapi-add-service.ts │ │ ├── hapi │ │ │ └── index.spec.ts │ │ ├── express │ │ │ └── index.spec.ts │ │ └── apache-calculator-service.ts │ ├── .prettierrc │ ├── README.md │ ├── tslint.json │ ├── tsconfig.json │ └── package.json ├── zipkin-core │ ├── src │ │ ├── main │ │ │ ├── index.ts │ │ │ ├── constants.ts │ │ │ ├── types.ts │ │ │ └── zipkin.ts │ │ └── tests │ │ │ └── unit │ │ │ └── zipkin.spec.ts │ ├── .prettierrc │ ├── README.md │ ├── tsconfig.json │ ├── tslint.json │ └── package.json ├── thrift-server-express │ ├── .prettierrc │ ├── src │ │ └── main │ │ │ ├── logger.ts │ │ │ ├── types.ts │ │ │ ├── index.ts │ │ │ └── ThriftServerExpress.ts │ ├── tslint.json │ ├── tsconfig.json │ └── package.json ├── thrift-server-hapi │ ├── .prettierrc │ ├── src │ │ └── main │ │ │ ├── logger.ts │ │ │ ├── types.ts │ │ │ └── index.ts │ ├── tslint.json │ ├── tsconfig.json │ └── package.json └── thrift-server-ecosystem │ ├── .prettierrc │ ├── thrift │ ├── add-service.thrift │ ├── calculator-service.thrift │ └── shared.thrift │ ├── config │ └── default.json │ ├── tslint.json │ ├── src │ ├── index.ts │ ├── add-service.ts │ ├── calculator-service.ts │ └── client.ts │ ├── tsconfig.json │ ├── README.md │ └── package.json ├── lerna.json ├── .editorconfig ├── .vscode └── settings.json ├── .gitignore ├── PULL_REQUEST_TEMPLATE.md ├── CONTRIBUTING.md ├── package.json ├── .circleci └── config.yml └── CODE_OF_CONDUCT.md /packages/thrift-server-core/.prettierignore: -------------------------------------------------------------------------------- 1 | src/tests/generated/**/*.ts 2 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-hapi/src/main/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ZipkinTracingHapi' 2 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-express/src/main/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ZipkinTracingExpress' 2 | -------------------------------------------------------------------------------- /packages/thrift-client-context-filter/src/main/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ThriftClientContextFilter' 2 | -------------------------------------------------------------------------------- /packages/thrift-client-timing-filter/src/main/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ThriftClientTimingFilter' 2 | -------------------------------------------------------------------------------- /packages/thrift-client-zipkin-filter/src/main/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ThriftClientZipkinFilter' 2 | -------------------------------------------------------------------------------- /packages/thrift-client/src/main/index.ts: -------------------------------------------------------------------------------- 1 | export * from './connections' 2 | export * from './types' 3 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/src/main/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ThriftClientTTwitterFilter' 2 | -------------------------------------------------------------------------------- /packages/thrift-integration/.prettierignore: -------------------------------------------------------------------------------- 1 | src/generated/**/*.ts 2 | src/generated-apache/**/*.ts 3 | src/generated-strict/**/*.ts 4 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/client/tracing/config.ts: -------------------------------------------------------------------------------- 1 | export const COLLECTOR_CONFIG = { 2 | host: 'localhost', 3 | port: 9411, 4 | } 5 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "3.14.1", 3 | "version": "1.0.4", 4 | "loglevel": "verbose", 5 | "packages": [ 6 | "packages/*" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/zipkin-core/src/main/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constants' 2 | export * from './types' 3 | export * from './utils' 4 | export * from './zipkin' 5 | -------------------------------------------------------------------------------- /packages/zipkin-core/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-client/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/errors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './InputBufferUnderrunError' 2 | export * from './TApplicationException' 3 | export * from './TProtocolException' 4 | -------------------------------------------------------------------------------- /packages/thrift-integration/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-server-core/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-server-express/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-server-hapi/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-hapi/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-client/src/main/logger.ts: -------------------------------------------------------------------------------- 1 | import { LogFunction, makeLogger } from '@creditkarma/thrift-server-core' 2 | 3 | export const defaultLogger: LogFunction = makeLogger('thrift-client') 4 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-express/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-client-context-filter/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-client-timing-filter/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-client-zipkin-filter/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-server-hapi/src/main/logger.ts: -------------------------------------------------------------------------------- 1 | import { LogFunction, makeLogger } from '@creditkarma/thrift-server-core' 2 | 3 | export const defaultLogger: LogFunction = makeLogger('thrift-server-hapi') 4 | -------------------------------------------------------------------------------- /packages/thrift-server-express/src/main/logger.ts: -------------------------------------------------------------------------------- 1 | import { LogFunction, makeLogger } from '@creditkarma/thrift-server-core' 2 | 3 | export const defaultLogger: LogFunction = makeLogger('thrift-server-express') 4 | -------------------------------------------------------------------------------- /packages/thrift-server-core/NOTICE: -------------------------------------------------------------------------------- 1 | Apache Thrift 2 | Copyright 2006-2017 The Apache Software Foundation. 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | -------------------------------------------------------------------------------- /packages/thrift-client-context-filter/src/main/logger.ts: -------------------------------------------------------------------------------- 1 | import { LogFunction, makeLogger } from '@creditkarma/thrift-server-core' 2 | 3 | export const defaultLogger: LogFunction = makeLogger( 4 | 'thrift-client-context-filter', 5 | ) 6 | -------------------------------------------------------------------------------- /packages/thrift-client-timing-filter/src/main/logger.ts: -------------------------------------------------------------------------------- 1 | import { LogFunction, makeLogger } from '@creditkarma/thrift-server-core' 2 | 3 | export const defaultLogger: LogFunction = makeLogger( 4 | 'thrift-client-timing-filter', 5 | ) 6 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/src/main/logger.ts: -------------------------------------------------------------------------------- 1 | import { LogFunction, makeLogger } from '@creditkarma/thrift-server-core' 2 | 3 | export const defaultLogger: LogFunction = makeLogger( 4 | 'thrift-client-ttwitter-filter', 5 | ) 6 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/thrift/add-service.thrift: -------------------------------------------------------------------------------- 1 | namespace java add-service 2 | 3 | service AddService { 4 | 5 | void ping(), 6 | 7 | i32 add(1: i32 num1, 2: i32 num2), 8 | 9 | i64 addInt64(1: i64 num1, 2: i64 num2), 10 | 11 | } 12 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/thrift/add-service.thrift: -------------------------------------------------------------------------------- 1 | namespace java add-service 2 | 3 | service AddService { 4 | 5 | void ping(), 6 | 7 | i32 add(1: i32 num1, 2: i32 num2), 8 | 9 | i64 addInt64(1: i64 num1, 2: i64 num2), 10 | 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/tests/thrift/add-service.thrift: -------------------------------------------------------------------------------- 1 | namespace java add-service 2 | 3 | service AddService { 4 | 5 | void ping(), 6 | 7 | i32 add(1: i32 num1, 2: i32 num2), 8 | 9 | i64 addInt64(1: i64 num1, 2: i64 num2), 10 | 11 | } 12 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/errors/InputBufferUnderrunError.ts: -------------------------------------------------------------------------------- 1 | export class InputBufferUnderrunError extends Error { 2 | public readonly name: string = 'InputBufferUnderrunError' 3 | 4 | constructor(message?: string) { 5 | super(message) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-client/src/main/connections/NullConnection.ts: -------------------------------------------------------------------------------- 1 | import * as Core from '@creditkarma/thrift-server-core' 2 | 3 | export class NullConnection extends Core.ThriftConnection { 4 | public send(dataToSend: Buffer, context: any = {}): Promise { 5 | return Promise.reject(new Error(`Not implemented`)) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "client": { 3 | "host": "localhost", 4 | "port": 8080 5 | }, 6 | 7 | "add-service": { 8 | "host": "localhost", 9 | "port": 8050 10 | }, 11 | 12 | "calculator-service": { 13 | "host": "localhost", 14 | "port": 8045 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/zipkin-core/README.md: -------------------------------------------------------------------------------- 1 | # Thrift Zipkin Core 2 | 3 | ## Contributing 4 | 5 | For more information about contributing new features and bug fixes, see our [Contribution Guidelines](../../CONTRIBUTING.md). 6 | External contributors must sign Contributor License Agreement (CLA) 7 | 8 | ## License 9 | 10 | This project is licensed under [Apache License Version 2.0](./LICENSE) 11 | -------------------------------------------------------------------------------- /packages/zipkin-core/src/main/constants.ts: -------------------------------------------------------------------------------- 1 | export const L5D_TRACE_HDR: string = 'l5d-ctx-trace' 2 | 3 | const B3Prefix: string = 'x-b3' 4 | 5 | export const ZipkinHeaders = { 6 | TraceId: `${B3Prefix}-traceid`, 7 | SpanId: `${B3Prefix}-spanid`, 8 | ParentId: `${B3Prefix}-parentspanid`, 9 | Sampled: `${B3Prefix}-sampled`, 10 | Flags: `${B3Prefix}-flags`, 11 | } 12 | -------------------------------------------------------------------------------- /packages/thrift-client/src/main/connections/utils.ts: -------------------------------------------------------------------------------- 1 | import { IThriftClientFilter } from '../types' 2 | 3 | export function filterByMethod( 4 | method: string, 5 | ): (filter: IThriftClientFilter) => boolean { 6 | return (filter: any): boolean => { 7 | return ( 8 | filter.methods.length === 0 || filter.methods.indexOf(method) > -1 9 | ) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/utils/normalizePath.ts: -------------------------------------------------------------------------------- 1 | export const normalizePath = (path: string = '/'): string => { 2 | path = path.trim() 3 | 4 | if (path === '/' || path === '') { 5 | return '' 6 | } 7 | 8 | if (!path.startsWith('/')) { 9 | path = `/${path}` 10 | } 11 | 12 | if (path.endsWith('/')) { 13 | path = path.substring(0, path.length - 1) 14 | } 15 | 16 | return path 17 | } 18 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/src/ttwitter/com/creditkarma/finagle/thrift/AnnotationType.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /* 4 | * Autogenerated by @creditkarma/thrift-typescript v3.7.6 5 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 6 | */ 7 | export enum AnnotationType { 8 | BOOL = 0, 9 | BYTES = 1, 10 | I16 = 2, 11 | I32 = 3, 12 | I64 = 4, 13 | DOUBLE = 5, 14 | STRING = 6 15 | } 16 | -------------------------------------------------------------------------------- /packages/thrift-integration/README.md: -------------------------------------------------------------------------------- 1 | # Thrift Integration Tests 2 | 3 | Integration test package for the thrift-server ecosystem. 4 | 5 | ## Contributing 6 | 7 | For more information about contributing new features and bug fixes, see our [Contribution Guidelines](../../CONTRIBUTING.md). 8 | External contributors must sign Contributor License Agreement (CLA) 9 | 10 | ## License 11 | 12 | This project is licensed under [Apache License Version 2.0](./LICENSE) 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "editor.tabSize": 4, 4 | "editor.rulers": [120], 5 | "files.trimTrailingWhitespace": true, 6 | "files.insertFinalNewline": true, 7 | "editor.wordWrapColumn": 120, 8 | "files.exclude": { 9 | "**/.DS_Store": true, 10 | "coverage": true, 11 | "coverage.lcov": true 12 | }, 13 | "typescript.tsdk": "node_modules/typescript/lib" 14 | } 15 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/thrift/exceptions.thrift: -------------------------------------------------------------------------------- 1 | namespace cpp exceptions 2 | namespace d exceptions 3 | namespace dart exceptions 4 | namespace java exceptions 5 | namespace perl exceptions 6 | namespace php exceptions 7 | namespace haxe exceptions 8 | namespace netcore exceptions 9 | 10 | include "shared.thrift" 11 | 12 | exception InvalidOperation { 13 | 1: i32 whatOp, 14 | 2: string why 15 | } 16 | 17 | exception InvalidResult { 18 | 1: string message 19 | 2: shared.Code code 20 | } 21 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/tests/thrift/exceptions.thrift: -------------------------------------------------------------------------------- 1 | namespace cpp exceptions 2 | namespace d exceptions 3 | namespace dart exceptions 4 | namespace java exceptions 5 | namespace perl exceptions 6 | namespace php exceptions 7 | namespace haxe exceptions 8 | namespace netcore exceptions 9 | 10 | include "shared.thrift" 11 | 12 | exception InvalidOperation { 13 | 1: i32 whatOp, 14 | 2: string why 15 | } 16 | 17 | exception InvalidResult { 18 | 1: string message 19 | 2: shared.Code code 20 | } 21 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/thrift/operation.thrift: -------------------------------------------------------------------------------- 1 | namespace java operation 2 | namespace js operation 3 | 4 | include "exceptions.thrift" 5 | 6 | typedef exceptions.InvalidOperation JankyOperation 7 | typedef exceptions.InvalidResult JankyResult 8 | 9 | /** 10 | * You can define enums, which are just 32 bit integers. Values are optional 11 | * and start at 1 if not supplied, C style again. 12 | */ 13 | enum Operation { 14 | ADD = 1, 15 | SUBTRACT = 2, 16 | MULTIPLY = 3, 17 | DIVIDE = 4 18 | } 19 | 20 | typedef Operation SomethingToDo 21 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/tests/thrift/operation.thrift: -------------------------------------------------------------------------------- 1 | namespace java operation 2 | namespace js operation 3 | 4 | include "exceptions.thrift" 5 | 6 | typedef exceptions.InvalidOperation JankyOperation 7 | typedef exceptions.InvalidResult JankyResult 8 | 9 | /** 10 | * You can define enums, which are just 32 bit integers. Values are optional 11 | * and start at 1 if not supplied, C style again. 12 | */ 13 | enum Operation { 14 | ADD = 1, 15 | SUBTRACT = 2, 16 | MULTIPLY = 3, 17 | DIVIDE = 4 18 | } 19 | 20 | typedef Operation SomethingToDo 21 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "max-line-length": false, 10 | "object-literal-sort-keys": false, 11 | "no-console": false, 12 | "no-var-requires": false, 13 | "array-type": [ 14 | true, 15 | "generic" 16 | ], 17 | "semicolon": false, 18 | "quotemark": false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/thrift-server-express/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "max-line-length": false, 10 | "object-literal-sort-keys": false, 11 | "no-console": false, 12 | "no-var-requires": false, 13 | "array-type": [ 14 | true, 15 | "generic" 16 | ], 17 | "semicolon": false, 18 | "quotemark": false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/thrift-server-express/src/main/types.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express' 2 | 3 | import { 4 | IThriftProcessor, 5 | IThriftServerOptions, 6 | } from '@creditkarma/thrift-server-core' 7 | 8 | export type IExpressServerOptions< 9 | TProcessor extends IThriftProcessor 10 | > = IThriftServerOptions 11 | 12 | export interface ICreateExpressServerOptions< 13 | TProcessor extends IThriftProcessor 14 | > { 15 | path?: string 16 | thriftOptions: IExpressServerOptions 17 | } 18 | -------------------------------------------------------------------------------- /packages/thrift-server-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "dist" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/errors/TProtocolException.ts: -------------------------------------------------------------------------------- 1 | export enum TProtocolExceptionType { 2 | UNKNOWN = 0, 3 | INVALID_DATA = 1, 4 | NEGATIVE_SIZE = 2, 5 | SIZE_LIMIT = 3, 6 | BAD_VERSION = 4, 7 | NOT_IMPLEMENTED = 5, 8 | DEPTH_LIMIT = 6, 9 | } 10 | 11 | export class TProtocolException extends Error { 12 | public readonly name: string = 'TProtocolException' 13 | public readonly type: TProtocolExceptionType 14 | 15 | constructor(type: TProtocolExceptionType, message?: string) { 16 | super(message) 17 | this.type = type 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/thrift-server-express/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "dist" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/thrift-server-hapi/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "max-line-length": false, 10 | "max-classes-per-file": false, 11 | "object-literal-sort-keys": false, 12 | "no-console": false, 13 | "no-var-requires": false, 14 | "array-type": [ 15 | true, 16 | "generic" 17 | ], 18 | "semicolon": false, 19 | "quotemark": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/zipkin-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "dist", 20 | "example" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-express/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "dist", 20 | "example" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/thrift-client-context-filter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "dist", 20 | "example" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/thrift-client-timing-filter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "dist", 20 | "example" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/thrift-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | "esModuleInterop": true, 17 | }, 18 | "exclude": [ 19 | "node_modules", 20 | "dist" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/thrift/calculator-service.thrift: -------------------------------------------------------------------------------- 1 | namespace java calculator-service 2 | 3 | enum Operation { 4 | ADD = 1, 5 | SUBTRACT = 2, 6 | MULTIPLY = 3, 7 | DIVIDE = 4 8 | } 9 | 10 | struct Work { 11 | 1: required i32 num1 = 0, 12 | 2: required i32 num2, 13 | 3: required Operation op, 14 | 4: optional string comment, 15 | } 16 | 17 | exception InvalidOperation { 18 | 1: i32 whatOp, 19 | 2: string why 20 | } 21 | 22 | service Calculator { 23 | 24 | void ping(), 25 | 26 | i32 add(1:i32 num1, 2:i32 num2), 27 | 28 | i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), 29 | 30 | } 31 | -------------------------------------------------------------------------------- /packages/thrift-client-zipkin-filter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | "esModuleInterop": true, 17 | }, 18 | "exclude": [ 19 | "node_modules", 20 | "dist", 21 | "example" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /packages/thrift-client/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "no-string-literal": false, 10 | "variable-name": false, 11 | "max-line-length": false, 12 | "jsdoc-format": false, 13 | "object-literal-sort-keys": false, 14 | "no-console": false, 15 | "no-var-requires": false, 16 | "array-type": [ 17 | true, 18 | "generic" 19 | ], 20 | "semicolon": false, 21 | "quotemark": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | "esModuleInterop": true, 17 | }, 18 | "exclude": [ 19 | "node_modules", 20 | "dist", 21 | "example" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /packages/thrift-integration/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "no-string-literal": false, 10 | "variable-name": false, 11 | "max-line-length": false, 12 | "jsdoc-format": false, 13 | "object-literal-sort-keys": false, 14 | "no-console": false, 15 | "no-var-requires": false, 16 | "array-type": [ 17 | true, 18 | "generic" 19 | ], 20 | "semicolon": false, 21 | "quotemark": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-hapi/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "no-string-literal": false, 10 | "variable-name": false, 11 | "max-line-length": false, 12 | "jsdoc-format": false, 13 | "object-literal-sort-keys": false, 14 | "no-console": false, 15 | "no-var-requires": false, 16 | "array-type": [ 17 | true, 18 | "generic" 19 | ], 20 | "semicolon": false, 21 | "quotemark": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-express/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "no-string-literal": false, 10 | "variable-name": false, 11 | "max-line-length": false, 12 | "jsdoc-format": false, 13 | "object-literal-sort-keys": false, 14 | "no-console": false, 15 | "no-var-requires": false, 16 | "array-type": [ 17 | true, 18 | "generic" 19 | ], 20 | "semicolon": false, 21 | "quotemark": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/thrift-client-context-filter/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "no-string-literal": false, 10 | "variable-name": false, 11 | "max-line-length": false, 12 | "jsdoc-format": false, 13 | "object-literal-sort-keys": false, 14 | "no-console": false, 15 | "no-var-requires": false, 16 | "array-type": [ 17 | true, 18 | "generic" 19 | ], 20 | "semicolon": false, 21 | "quotemark": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/thrift-client-timing-filter/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "no-string-literal": false, 10 | "variable-name": false, 11 | "max-line-length": false, 12 | "jsdoc-format": false, 13 | "object-literal-sort-keys": false, 14 | "no-console": false, 15 | "no-var-requires": false, 16 | "array-type": [ 17 | true, 18 | "generic" 19 | ], 20 | "semicolon": false, 21 | "quotemark": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "no-string-literal": false, 10 | "variable-name": false, 11 | "max-line-length": false, 12 | "jsdoc-format": false, 13 | "object-literal-sort-keys": false, 14 | "no-console": false, 15 | "no-var-requires": false, 16 | "array-type": [ 17 | true, 18 | "generic" 19 | ], 20 | "semicolon": false, 21 | "quotemark": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/thrift-client-zipkin-filter/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "no-string-literal": false, 10 | "variable-name": false, 11 | "max-line-length": false, 12 | "jsdoc-format": false, 13 | "object-literal-sort-keys": false, 14 | "no-console": false, 15 | "no-var-requires": false, 16 | "array-type": [ 17 | true, 18 | "generic" 19 | ], 20 | "semicolon": false, 21 | "quotemark": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/zipkin-core/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "no-string-literal": false, 10 | "variable-name": false, 11 | "max-line-length": false, 12 | "jsdoc-format": false, 13 | "no-bitwise": false, 14 | "object-literal-sort-keys": false, 15 | "no-console": false, 16 | "no-var-requires": false, 17 | "array-type": [ 18 | true, 19 | "generic" 20 | ], 21 | "semicolon": false, 22 | "quotemark": false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/tests/thrift/shared.thrift: -------------------------------------------------------------------------------- 1 | namespace cpp shared 2 | namespace d share // "shared" would collide with the eponymous D keyword. 3 | namespace dart shared 4 | namespace java shared 5 | namespace perl shared 6 | namespace php shared 7 | namespace haxe shared 8 | namespace netcore shared 9 | 10 | const i32 SHARED_INT = 45 11 | 12 | struct Code { 13 | 1: i64 status 14 | } 15 | 16 | struct SharedStruct { 17 | 1: required Code code 18 | 2: required string value 19 | } 20 | 21 | union SharedUnion { 22 | 1: string option1 23 | 2: string option2 24 | } 25 | 26 | service SharedService { 27 | SharedStruct getStruct(1: i32 key) 28 | SharedUnion getUnion(1: i32 index) 29 | } 30 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/thrift/common.thrift: -------------------------------------------------------------------------------- 1 | /** 2 | * Thrift files can namespace, package, or prefix their output in various 3 | * target languages. 4 | */ 5 | namespace cpp common 6 | namespace d common 7 | namespace dart common 8 | namespace java common 9 | namespace php common 10 | namespace perl common 11 | namespace haxe common 12 | namespace netcore common 13 | 14 | include "shared.thrift" 15 | 16 | typedef shared.SharedStruct CommonStruct 17 | typedef shared.SharedUnion CommonUnion 18 | typedef shared.SHARED_INT COMMON_INT 19 | 20 | exception AuthException { 21 | 1: i32 code 22 | 2: string message 23 | } 24 | 25 | struct Metadata { 26 | 1: required i32 traceId 27 | 2: optional i32 clientId 28 | } 29 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/tests/thrift/common.thrift: -------------------------------------------------------------------------------- 1 | /** 2 | * Thrift files can namespace, package, or prefix their output in various 3 | * target languages. 4 | */ 5 | namespace cpp common 6 | namespace d common 7 | namespace dart common 8 | namespace java common 9 | namespace php common 10 | namespace perl common 11 | namespace haxe common 12 | namespace netcore common 13 | 14 | include "shared.thrift" 15 | 16 | typedef shared.SharedStruct CommonStruct 17 | typedef shared.SharedUnion CommonUnion 18 | typedef shared.SHARED_INT COMMON_INT 19 | 20 | exception AuthException { 21 | 1: i32 code 22 | 2: string message 23 | } 24 | 25 | struct Metadata { 26 | 1: required i32 traceId 27 | 2: optional i32 clientId 28 | } 29 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/src/ttwitter/com/creditkarma/finagle/thrift/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /* 4 | * Autogenerated by @creditkarma/thrift-typescript v3.7.6 5 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 6 | */ 7 | export * from "./constants"; 8 | export * from "./AnnotationType"; 9 | export * from "./Endpoint"; 10 | export * from "./Annotation"; 11 | export * from "./BinaryAnnotation"; 12 | export * from "./Span"; 13 | export * from "./ClientId"; 14 | export * from "./RequestContext"; 15 | export * from "./Delegation"; 16 | export * from "./RequestHeader"; 17 | export * from "./ResponseHeader"; 18 | export * from "./ConnectionOptions"; 19 | export * from "./UpgradeReply"; 20 | -------------------------------------------------------------------------------- /packages/thrift-server-core/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier", 5 | "tslint-plugin-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | "variable-name": false, 10 | "only-arrow-functions": false, 11 | "max-classes-per-file": false, 12 | "max-line-length": false, 13 | "no-empty": false, 14 | "no-bitwise": false, 15 | "object-literal-sort-keys": false, 16 | "no-console": false, 17 | "no-var-requires": false, 18 | "array-type": [ 19 | true, 20 | "generic" 21 | ], 22 | "semicolon": false, 23 | "quotemark": false 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/config.ts: -------------------------------------------------------------------------------- 1 | export const APACHE_SERVER_CONFIG = { 2 | hostName: 'localhost', 3 | port: 8888, 4 | } 5 | 6 | export const HAPI_CALC_SERVER_STRICT_CONFIG = { 7 | hostName: 'localhost', 8 | port: 8092, 9 | path: '/thrift', 10 | } 11 | 12 | export const HAPI_CALC_SERVER_CONFIG = { 13 | hostName: 'localhost', 14 | port: 8090, 15 | path: '/thrift', 16 | } 17 | 18 | export const EXPRESS_CALC_SERVER_CONFIG = { 19 | hostName: 'localhost', 20 | port: 8091, 21 | path: '/thrift', 22 | } 23 | 24 | export const ADD_SERVER_CONFIG = { 25 | hostName: 'localhost', 26 | port: 8080, 27 | path: '/thrift', 28 | } 29 | 30 | export const CLIENT_CONFIG = { 31 | hostName: 'localhost', 32 | port: 9000, 33 | } 34 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/src/index.ts: -------------------------------------------------------------------------------- 1 | import { generate } from '@creditkarma/thrift-typescript' 2 | import { fork } from 'child_process' 3 | 4 | process.chdir(__dirname) 5 | 6 | generate({ 7 | rootDir: '.', 8 | outDir: 'generated', 9 | sourceDir: 'thrift', 10 | target: 'thrift-server', 11 | files: [], 12 | library: '@creditkarma/thrift-server-core', 13 | }) 14 | 15 | const clientProc = fork('./client.ts') 16 | const addProc = fork('./add-service.ts') 17 | const calculatorProc = fork('./calculator-service.ts') 18 | 19 | function exit(code: number) { 20 | clientProc.kill() 21 | addProc.kill() 22 | calculatorProc.kill() 23 | process.exitCode = code 24 | } 25 | 26 | process.on('SIGINT', () => { 27 | console.log('Caught interrupt signal') 28 | exit(0) 29 | }) 30 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/thrift/shared.thrift: -------------------------------------------------------------------------------- 1 | namespace cpp shared 2 | namespace d share // "shared" would collide with the eponymous D keyword. 3 | namespace dart shared 4 | namespace java shared 5 | namespace perl shared 6 | namespace php shared 7 | namespace haxe shared 8 | namespace netcore shared 9 | 10 | const i32 SHARED_INT = 45 11 | 12 | struct Code { 13 | 1: i64 status 14 | } 15 | 16 | struct SharedStruct { 17 | 1: required Code code 18 | 2: required string value 19 | } 20 | 21 | struct MappedStruct { 22 | 1: required map data 23 | } 24 | 25 | union SharedUnion { 26 | 1: string option1 27 | 2: string option2 28 | } 29 | 30 | service SharedService { 31 | SharedStruct getStruct(1: i32 key) 32 | SharedUnion getUnion(1: i32 index) 33 | MappedStruct getMappedStruct(1: i32 index) 34 | } 35 | -------------------------------------------------------------------------------- /packages/thrift-server-express/src/main/index.ts: -------------------------------------------------------------------------------- 1 | import { IThriftProcessor } from '@creditkarma/thrift-server-core' 2 | import * as bodyParser from 'body-parser' 3 | import * as express from 'express' 4 | 5 | import { ThriftServerExpress } from './ThriftServerExpress' 6 | 7 | import { ICreateExpressServerOptions } from './types' 8 | 9 | export * from './ThriftServerExpress' 10 | export * from './types' 11 | 12 | export function createThriftServer< 13 | TProcessor extends IThriftProcessor 14 | >(options: ICreateExpressServerOptions): express.Application { 15 | const app: express.Application = express() 16 | 17 | app.use( 18 | options.path || '/thrift', 19 | bodyParser.raw(), 20 | ThriftServerExpress(options.thriftOptions), 21 | ) 22 | 23 | return app 24 | } 25 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/src/ttwitter/com/creditkarma/finagle/thrift/constants.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /* 4 | * Autogenerated by @creditkarma/thrift-typescript v3.7.6 5 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 6 | */ 7 | export const CLIENT_SEND: string = "cs"; 8 | export const CLIENT_RECV: string = "cr"; 9 | export const SERVER_SEND: string = "ss"; 10 | export const SERVER_RECV: string = "sr"; 11 | export const WIRE_SEND: string = "ws"; 12 | export const WIRE_RECV: string = "wr"; 13 | export const CLIENT_SEND_FRAGMENT: string = "csf"; 14 | export const CLIENT_RECV_FRAGMENT: string = "crf"; 15 | export const SERVER_SEND_FRAGMENT: string = "ssf"; 16 | export const SERVER_RECV_FRAGMENT: string = "srf"; 17 | export const CLIENT_ADDR: string = "ca"; 18 | export const SERVER_ADDR: string = "sa"; 19 | -------------------------------------------------------------------------------- /packages/zipkin-core/src/main/types.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IRequestHeaders, 3 | LogFunction, 4 | ProtocolType, 5 | TransportType, 6 | } from '@creditkarma/thrift-server-core' 7 | 8 | export { ITraceId } from '@creditkarma/thrift-server-core' 9 | 10 | export type ZipkinVersion = 'v1' | 'v2' 11 | 12 | export interface IZipkinOptions { 13 | localServiceName: string 14 | port?: number 15 | isThrift?: boolean 16 | transport?: TransportType 17 | protocol?: ProtocolType 18 | tracerConfig?: IZipkinTracerConfig 19 | } 20 | 21 | export interface IZipkinClientOptions extends IZipkinOptions { 22 | remoteServiceName?: string 23 | } 24 | 25 | export interface IZipkinTracerConfig { 26 | debug?: boolean 27 | endpoint?: string 28 | sampleRate?: number 29 | headers?: IRequestHeaders 30 | httpInterval?: number 31 | httpTimeout?: number 32 | zipkinVersion?: ZipkinVersion 33 | logger?: LogFunction 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules/ 31 | 32 | # Optional npm cache directory 33 | .npm 34 | 35 | # Optional eslint cache 36 | .eslintcache 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | 41 | # Output of 'npm pack' 42 | *.tgz 43 | 44 | # Yarn Integrity file 45 | .yarn-integrity 46 | 47 | # dotenv environment variables file 48 | .env 49 | 50 | # Typescript artifacts 51 | dist 52 | 53 | # Codegen artifacts 54 | generated 55 | generated-strict 56 | generated-apache 57 | 58 | # Temp files 59 | *.tmp 60 | tmp 61 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/transports/index.ts: -------------------------------------------------------------------------------- 1 | import { ITransportConstructor, ITransportMap, TransportType } from '../types' 2 | 3 | import { BufferedTransport } from './BufferedTransport' 4 | 5 | export * from './BufferedTransport' 6 | // export * from './FramedTransport' 7 | export * from './TTransport' 8 | export * from './ThriftFrameCodec' 9 | 10 | const transports: ITransportMap = { 11 | buffered: BufferedTransport, 12 | // framed: FramedTransport, 13 | } 14 | 15 | export const supportedTransports: Array = Object.keys(transports) 16 | 17 | export function isTransportSupported(transport: TransportType): boolean { 18 | return supportedTransports.indexOf(transport) !== -1 19 | } 20 | 21 | export function getTransport( 22 | transport: TransportType = 'buffered', 23 | ): ITransportConstructor { 24 | if (!isTransportSupported(transport)) { 25 | throw new Error( 26 | `Invalid transport specified. Supported values: ${supportedTransports.join( 27 | ', ', 28 | )}`, 29 | ) 30 | } 31 | 32 | return transports[transport] 33 | } 34 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/protocols/index.ts: -------------------------------------------------------------------------------- 1 | import { IProtocolConstructor, IProtocolMap, ProtocolType } from '../types' 2 | 3 | import { BinaryProtocol } from './BinaryProtocol' 4 | 5 | import { CompactProtocol } from './CompactProtocol' 6 | 7 | import { JSONProtocol } from './JSONProtocol' 8 | 9 | export * from './BinaryProtocol' 10 | export * from './CompactProtocol' 11 | export * from './JSONProtocol' 12 | export * from './TProtocol' 13 | 14 | const protocols: IProtocolMap = { 15 | binary: BinaryProtocol, 16 | compact: CompactProtocol, 17 | json: JSONProtocol, 18 | } 19 | 20 | export const supportedProtocols: Array = Object.keys(protocols) 21 | 22 | export function isProtocolSupported(protocol: ProtocolType): boolean { 23 | return supportedProtocols.indexOf(protocol) !== -1 24 | } 25 | 26 | export function getProtocol( 27 | protocol: ProtocolType = 'binary', 28 | ): IProtocolConstructor { 29 | if (protocol && !isProtocolSupported(protocol)) { 30 | throw new Error( 31 | `Invalid protocol specified. Supported values: ${supportedProtocols.join( 32 | ', ', 33 | )}`, 34 | ) 35 | } 36 | 37 | return protocols[protocol] 38 | } 39 | -------------------------------------------------------------------------------- /packages/thrift-server-hapi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | "baseUrl": "./node_modules/@types/", 17 | "paths": { 18 | "@hapi/hapi": [ 19 | "hapi__hapi" 20 | ], 21 | "@hapi/shot": [ 22 | "hapi__shot" 23 | ], 24 | "@hapi/mimos": [ 25 | "hapi__mimos" 26 | ], 27 | "@hapi/iron": [ 28 | "hapi__iron" 29 | ], 30 | "@hapi/joi": [ 31 | "hapi__joi" 32 | ], 33 | "@hapi/podium": [ 34 | "hapi__podium" 35 | ], 36 | "@hapi/catbox": [ 37 | "hapi__catbox" 38 | ] 39 | }, 40 | }, 41 | "exclude": [ 42 | "node_modules", 43 | "dist" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | "baseUrl": "./node_modules/@types/", 17 | "paths": { 18 | "@hapi/hapi": [ 19 | "hapi__hapi" 20 | ], 21 | "@hapi/shot": [ 22 | "hapi__shot" 23 | ], 24 | "@hapi/mimos": [ 25 | "hapi__mimos" 26 | ], 27 | "@hapi/iron": [ 28 | "hapi__iron" 29 | ], 30 | "@hapi/joi": [ 31 | "hapi__joi" 32 | ], 33 | "@hapi/podium": [ 34 | "hapi__podium" 35 | ], 36 | "@hapi/catbox": [ 37 | "hapi__catbox" 38 | ] 39 | }, 40 | }, 41 | "exclude": [ 42 | "node_modules", 43 | "dist" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /packages/thrift-integration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "declaration": true, 8 | "rootDir": "./src", 9 | "outDir": "./dist", 10 | "noEmitOnError": true, 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | "pretty": true, 14 | "removeComments": true, 15 | "baseUrl": "./node_modules/@types/", 16 | "paths": { 17 | "@hapi/hapi": [ 18 | "hapi__hapi" 19 | ], 20 | "@hapi/shot": [ 21 | "hapi__shot" 22 | ], 23 | "@hapi/mimos": [ 24 | "hapi__mimos" 25 | ], 26 | "@hapi/iron": [ 27 | "hapi__iron" 28 | ], 29 | "@hapi/joi": [ 30 | "hapi__joi" 31 | ], 32 | "@hapi/podium": [ 33 | "hapi__podium" 34 | ], 35 | "@hapi/catbox": [ 36 | "hapi__catbox" 37 | ] 38 | }, 39 | }, 40 | "exclude": [ 41 | "node_modules", 42 | "dist", 43 | "example", 44 | "src/tests" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-hapi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2021"], 4 | "target": "es2021", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "rootDir": "./src", 10 | "outDir": "./dist", 11 | "noEmitOnError": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "pretty": true, 15 | "removeComments": true, 16 | "baseUrl": "./node_modules/@types/", 17 | "paths": { 18 | "@hapi/hapi": [ 19 | "hapi__hapi" 20 | ], 21 | "@hapi/shot": [ 22 | "hapi__shot" 23 | ], 24 | "@hapi/mimos": [ 25 | "hapi__mimos" 26 | ], 27 | "@hapi/iron": [ 28 | "hapi__iron" 29 | ], 30 | "@hapi/joi": [ 31 | "hapi__joi" 32 | ], 33 | "@hapi/podium": [ 34 | "hapi__podium" 35 | ], 36 | "@hapi/catbox": [ 37 | "hapi__catbox" 38 | ] 39 | }, 40 | }, 41 | "exclude": [ 42 | "node_modules", 43 | "dist", 44 | "example" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /packages/thrift-server-hapi/src/main/types.ts: -------------------------------------------------------------------------------- 1 | import * as Hapi from '@hapi/hapi' 2 | 3 | import { 4 | IThriftProcessor, 5 | IThriftServerOptions, 6 | ProtocolType, 7 | TransportType, 8 | } from '@creditkarma/thrift-server-core' 9 | 10 | export interface IServiceDetails { 11 | processor: IThriftProcessor 12 | } 13 | 14 | export interface IServiceDetailMap { 15 | [serviceName: string]: IServerDetails 16 | } 17 | 18 | export interface IServerDetails { 19 | transport: TransportType 20 | protocol: ProtocolType 21 | services: IServiceDetailMap 22 | } 23 | 24 | export interface IHandlerOptions { 25 | service: TProcessor 26 | } 27 | 28 | export type IHapiServerOptions< 29 | TProcessor extends IThriftProcessor 30 | > = IThriftServerOptions 31 | 32 | export interface IHapiPluginOptions< 33 | TProcessor extends IThriftProcessor 34 | > { 35 | path?: string 36 | auth?: false | string | Hapi.RouteOptionsAccess 37 | thriftOptions: IHapiServerOptions 38 | } 39 | 40 | export interface ICreateHapiServerOptions< 41 | TProcessor extends IThriftProcessor 42 | > extends IHapiPluginOptions { 43 | port: number 44 | } 45 | 46 | export type ThriftHapiPlugin = Hapi.Plugin 47 | -------------------------------------------------------------------------------- /packages/thrift-client/src/main/connections/pool.ts: -------------------------------------------------------------------------------- 1 | import { LogFunction } from '@creditkarma/thrift-server-core' 2 | import * as GenericPool from 'generic-pool' 3 | import { Connection, createConnection, IConnectionConfig } from './Connection' 4 | 5 | const defaultOptions: GenericPool.Options = { 6 | min: 0, 7 | max: 10, 8 | evictionRunIntervalMillis: 10000, 9 | idleTimeoutMillis: 10000, 10 | acquireTimeoutMillis: 1000, 11 | testOnBorrow: true, 12 | } 13 | 14 | export const createPool = ( 15 | config: IConnectionConfig, 16 | logger: LogFunction, 17 | options?: GenericPool.Options, 18 | ) => { 19 | const resolvedOptions = Object.assign(defaultOptions, options) 20 | const factory: GenericPool.Factory = { 21 | create: async () => { 22 | logger(['debug', 'createPool'], 'Creating new client connection') 23 | return await createConnection(config) 24 | }, 25 | destroy: async (connection) => { 26 | logger(['debug', 'createPool'], 'Destroying client connection') 27 | return connection.destroy().then(() => undefined) 28 | }, 29 | validate: async (connection) => { 30 | return connection.hasSession() 31 | }, 32 | } 33 | 34 | return GenericPool.createPool(factory, resolvedOptions) 35 | } 36 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/utils/index.ts: -------------------------------------------------------------------------------- 1 | import merge = require('lodash/merge') 2 | import * as url from 'url' 3 | 4 | export * from './appendThriftObject' 5 | export * from './readThriftObject' 6 | export * from './readThriftMetadata' 7 | export * from './normalizePath' 8 | 9 | export function deepMerge( 10 | base: Base, 11 | update: Update, 12 | ): Base & Update { 13 | return merge({}, base, update) 14 | } 15 | 16 | export function overlayObjects(a: A, b: B): A & B 17 | export function overlayObjects(a: A, b: B, c: C): A & B & C 18 | export function overlayObjects( 19 | a: A, 20 | b: B, 21 | c: C, 22 | d: D, 23 | ): A & B & C & D 24 | export function overlayObjects( 25 | a: A, 26 | b: B, 27 | c: C, 28 | d: D, 29 | e: E, 30 | ): A & B & B & C & D & E 31 | export function overlayObjects(...objs: Array): any { 32 | return objs.reduce((acc: any, next: any) => { 33 | return deepMerge(acc, next) 34 | }, {}) 35 | } 36 | 37 | export function formatUrl(requestUrl: string): string { 38 | const parsed = url.parse(url.format(requestUrl)) 39 | 40 | if (!parsed.pathname) { 41 | return `${parsed.hostname || ''}/` 42 | } else { 43 | return `${parsed.hostname || ''}${parsed.pathname}` 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/index.ts: -------------------------------------------------------------------------------- 1 | import { InputBufferUnderrunError } from './errors' 2 | 3 | import { 4 | IProtocolConstructor, 5 | IThriftProcessor, 6 | ITransportConstructor, 7 | } from './types' 8 | 9 | export * from './types' 10 | export * from './Int64' 11 | export * from './protocols' 12 | export * from './transports' 13 | export * from './errors' 14 | export * from './utils' 15 | export * from './binary' 16 | export { makeLogger } from './logger' 17 | 18 | export function process(args: { 19 | processor: IThriftProcessor 20 | buffer: Buffer 21 | Transport: ITransportConstructor 22 | Protocol: IProtocolConstructor 23 | context: Context 24 | }): Promise { 25 | const transportWithData = args.Transport.receiver(args.buffer) 26 | const input = new args.Protocol(transportWithData) 27 | 28 | return new Promise((resolve, reject): void => { 29 | const output = new args.Protocol(new args.Transport()) 30 | args.processor.process(input, output, args.context).then( 31 | (result: Buffer) => { 32 | resolve(result) 33 | transportWithData.commitPosition() 34 | }, 35 | (err: any) => { 36 | if (err instanceof InputBufferUnderrunError) { 37 | transportWithData.rollbackPosition() 38 | } 39 | reject(err) 40 | }, 41 | ) 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Motivation and Context 7 | 8 | 9 | 10 | ## How Has This Been Tested? 11 | 12 | 13 | 14 | 15 | ## Types of changes 16 | 17 | - [ ] Bug fix (non-breaking change which fixes an issue) 18 | - [ ] New feature (non-breaking change which adds functionality) 19 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 20 | 21 | ## Checklist: 22 | 23 | 24 | - [ ] My code follows the code style of this project. 25 | - [ ] My change requires a change to the documentation. 26 | - [ ] I have updated the documentation accordingly. 27 | - [ ] I have read the **CONTRIBUTING** document. 28 | - [ ] I have added tests to cover my changes. 29 | - [ ] All new and existing tests passed. 30 | -------------------------------------------------------------------------------- /packages/thrift-server-hapi/src/main/index.ts: -------------------------------------------------------------------------------- 1 | import { IThriftProcessor, LogFunction } from '@creditkarma/thrift-server-core' 2 | import * as Hapi from '@hapi/hapi' 3 | 4 | import { ThriftServerHapi } from './ThriftServerHapi' 5 | 6 | import { ICreateHapiServerOptions } from './types' 7 | 8 | import { defaultLogger } from './logger' 9 | 10 | export * from './ThriftServerHapi' 11 | export * from './types' 12 | 13 | /** 14 | * Creates and returns a Hapi server with the thrift plugin registered. 15 | * 16 | * @param options 17 | */ 18 | export function createThriftServer< 19 | TProcessor extends IThriftProcessor 20 | >(options: ICreateHapiServerOptions): Promise { 21 | const server = new Hapi.Server({ 22 | port: options.port, 23 | debug: { request: ['error'] }, 24 | }) 25 | 26 | const logger: LogFunction = options.thriftOptions.logger || defaultLogger 27 | 28 | return server 29 | .register({ 30 | plugin: ThriftServerHapi({ 31 | path: options.path, 32 | thriftOptions: options.thriftOptions, 33 | }), 34 | }) 35 | .then(() => { 36 | return server 37 | }) 38 | .catch((err: any) => { 39 | logger( 40 | ['error', 'createThriftServer'], 41 | `Unable to create Thrift server. ${err.message}`, 42 | ) 43 | throw err 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/logger.ts: -------------------------------------------------------------------------------- 1 | import { LogFunction } from './types' 2 | 3 | export const makeLogger = (name: string): LogFunction => { 4 | return (tags: Array, data?: string | object): void => { 5 | const allTags: Array = Array.from(new Set([name, ...tags])) 6 | if (allTags.indexOf('error') > -1) { 7 | if (data !== undefined) { 8 | console.error(`[${allTags.join(',')}] `, data) 9 | } else { 10 | console.error(`[${allTags.join(',')}]`) 11 | } 12 | } else if (allTags.indexOf('warn') > -1) { 13 | if (data !== undefined) { 14 | console.warn(`[${allTags.join(',')}] `, data) 15 | } else { 16 | console.warn(`[${allTags.join(',')}]`) 17 | } 18 | } else if (allTags.indexOf('info') > -1) { 19 | if (data !== undefined) { 20 | console.info(`[${allTags.join(',')}] `, data) 21 | } else { 22 | console.info(`[${allTags.join(',')}]`) 23 | } 24 | } else { 25 | if (data !== undefined && process.env.DUBUG !== undefined) { 26 | console.log(`[${allTags.join(',')}] `, data) 27 | } else if (process.env.DUBUG !== undefined) { 28 | console.log(`[${allTags.join(',')}]`) 29 | } 30 | } 31 | } 32 | } 33 | 34 | export const defaultLogger: LogFunction = makeLogger('thrift-server-core') 35 | -------------------------------------------------------------------------------- /packages/thrift-client-timing-filter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-client-timing-filter", 3 | "version": "1.0.4", 4 | "description": "Timing filter for thrift-client", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "files": [ 8 | "dist/main" 9 | ], 10 | "keywords": [], 11 | "scripts": { 12 | "clean": "rimraf ./dist ./src/tests/generated", 13 | "lint": "tslint --fix './src/**/*.ts'", 14 | "format": "prettier --write 'src/**/*.ts'", 15 | "prebuild": "npm run clean && npm run lint && npm run format", 16 | "build": "tsc", 17 | "test": "npm run test:only", 18 | "test:only": "echo 'No tests for thrift-client-timing-filter'; exit 0;" 19 | }, 20 | "peerDependencies": { 21 | "@creditkarma/thrift-client": "^1.0.0", 22 | "@creditkarma/thrift-server-core": "^1.0.0" 23 | }, 24 | "devDependencies": { 25 | "@creditkarma/thrift-client": "^1.0.4", 26 | "@creditkarma/thrift-server-core": "^1.0.4", 27 | "@hapi/code": "^8.0.7", 28 | "@hapi/lab": "^25.0.1", 29 | "@types/hapi__code": "^8.0.1", 30 | "@types/hapi__lab": "^18.1.0", 31 | "@types/node": "^16.11.0", 32 | "prettier": "^1.18.2", 33 | "rimraf": "^2.6.2", 34 | "tslint": "^5.11.0", 35 | "tslint-config-prettier": "^1.15.0", 36 | "tslint-plugin-prettier": "^2.0.0", 37 | "typescript": "4.6.x" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/transports/TTransport.ts: -------------------------------------------------------------------------------- 1 | export abstract class TTransport { 2 | public static receiver(data: Buffer): TTransport { 3 | throw new Error('Not implemented') 4 | } 5 | 6 | protected buffer: Buffer 7 | protected requestId: number | null 8 | 9 | constructor(buffer: Buffer) { 10 | this.buffer = buffer 11 | this.requestId = null 12 | } 13 | 14 | // Return any bytes remaining to be read 15 | public abstract remaining(): Buffer 16 | 17 | public abstract commitPosition(): void 18 | public abstract rollbackPosition(): void 19 | // public abstract getBytesRemaining(): number 20 | // public abstract getBufferPosition(): number 21 | public abstract consume(len: number): void 22 | 23 | // public abstract getBuffer(): Buffer 24 | public abstract isOpen(): boolean 25 | public abstract open(): boolean 26 | public abstract close(): boolean 27 | 28 | public abstract read(len: number): Buffer 29 | public abstract readByte(): number 30 | public abstract readI16(): number 31 | public abstract readI32(): number 32 | public abstract readDouble(): number 33 | public abstract readString(len: number): string 34 | public abstract readAll(): string 35 | 36 | /** 37 | * Writes buffer to the output 38 | */ 39 | public abstract write(buf: Buffer): void 40 | 41 | /** 42 | * Flush any pending data out of a transport buffer 43 | */ 44 | public abstract flush(): Buffer 45 | } 46 | -------------------------------------------------------------------------------- /packages/thrift-client-context-filter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-client-context-filter", 3 | "version": "1.0.4", 4 | "description": "Filter for appending a Thrift object to request payload", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "files": [ 8 | "dist/main" 9 | ], 10 | "keywords": [], 11 | "scripts": { 12 | "clean": "rimraf ./dist ./src/tests/generated", 13 | "lint": "tslint --fix './src/**/*.ts'", 14 | "format": "prettier --write 'src/**/*.ts'", 15 | "prebuild": "npm run clean && npm run lint && npm run format", 16 | "build": "tsc", 17 | "test": "npm run test:only", 18 | "test:only": "echo 'No tests for thrift-client-context-filter'; exit 0;" 19 | }, 20 | "peerDependencies": { 21 | "@creditkarma/thrift-client": "^1.0.0", 22 | "@creditkarma/thrift-server-core": "^1.0.0" 23 | }, 24 | "devDependencies": { 25 | "@creditkarma/thrift-client": "^1.0.4", 26 | "@creditkarma/thrift-server-core": "^1.0.4", 27 | "@hapi/code": "^8.0.7", 28 | "@hapi/lab": "^25.0.1", 29 | "@types/hapi__code": "^8.0.1", 30 | "@types/hapi__lab": "^18.1.0", 31 | "@types/node": "^16.11.0", 32 | "prettier": "^1.18.2", 33 | "rimraf": "^2.6.2", 34 | "tslint": "^5.11.0", 35 | "tslint-config-prettier": "^1.15.0", 36 | "tslint-plugin-prettier": "^2.0.0", 37 | "typescript": "4.6.x" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/zipkin-tracing-express", 3 | "version": "1.0.4", 4 | "description": "Zipkin tracing for thrift-server-express", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "files": [ 8 | "dist/main" 9 | ], 10 | "keywords": [], 11 | "scripts": { 12 | "clean": "rimraf ./dist", 13 | "lint": "tslint --fix './src/**/*.ts'", 14 | "format": "prettier --write 'src/**/*.ts'", 15 | "prebuild": "npm run clean && npm run lint && npm run format", 16 | "build": "tsc", 17 | "test": "npm run test:only", 18 | "test:only": "echo 'No tests for zipkin-tracing-express'; exit 0;" 19 | }, 20 | "dependencies": { 21 | "zipkin": "^0.15.0" 22 | }, 23 | "peerDependencies": { 24 | "@creditkarma/thrift-server-core": "^1.0.0", 25 | "@creditkarma/zipkin-core": "^1.0.0", 26 | "@types/express": ">=4.0.0 <5.0.0", 27 | "express": ">=4.0.0 <5.0.0" 28 | }, 29 | "devDependencies": { 30 | "@creditkarma/thrift-server-core": "^1.0.4", 31 | "@creditkarma/zipkin-core": "^1.0.4", 32 | "@types/express": "^4.16.0", 33 | "@types/node": "^16.11.0", 34 | "express": "^4.16.4", 35 | "prettier": "^1.18.2", 36 | "rimraf": "^2.6.2", 37 | "tslint": "^5.11.0", 38 | "tslint-config-prettier": "^1.15.0", 39 | "tslint-plugin-prettier": "^2.0.0", 40 | "typescript": "4.6.x" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/zipkin-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/zipkin-core", 3 | "version": "1.0.4", 4 | "description": "Shared utilities for Zipkin support in thrift-server packages", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "files": [ 8 | "dist/main" 9 | ], 10 | "keywords": [], 11 | "scripts": { 12 | "clean": "rimraf ./dist", 13 | "lint": "tslint --fix './src/**/*.ts'", 14 | "format": "prettier --write 'src/**/*.ts'", 15 | "prebuild": "npm run clean && npm run lint && npm run format", 16 | "build": "tsc", 17 | "test": "npm run test:only", 18 | "test:only": "lab --timeout 15000 --verbose -l -S -P spec dist/tests/unit" 19 | }, 20 | "dependencies": { 21 | "@types/bytebuffer": "^5.0.36", 22 | "bytebuffer": "^5.0.1", 23 | "zipkin": "^0.15.0", 24 | "zipkin-transport-http": "^0.14.2" 25 | }, 26 | "peerDependencies": { 27 | "@creditkarma/thrift-server-core": "^1.0.0" 28 | }, 29 | "devDependencies": { 30 | "@creditkarma/thrift-server-core": "^1.0.4", 31 | "@hapi/code": "^8.0.7", 32 | "@hapi/lab": "^25.0.1", 33 | "@types/hapi__code": "^8.0.1", 34 | "@types/hapi__lab": "^18.1.0", 35 | "@types/node": "^16.11.0", 36 | "prettier": "^1.18.2", 37 | "rimraf": "^2.6.2", 38 | "tslint": "^5.11.0", 39 | "tslint-config-prettier": "^1.15.0", 40 | "tslint-plugin-prettier": "^2.0.0", 41 | "typescript": "4.6.x" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thank you for considering contributing to Thrift Server! 2 | Please take a moment to review this document to make the contribution process easy and effective. 3 | 4 | ## Reporting a bug 5 | 6 | Thrift Server uses github issues to track bugs. Bugs are labelled as [bug](https://github.com/creditkarma/thrift-server/labels/bug). 7 | Before reporting a new bug first search the issues to find out if it's already reported and/or resolved and pending merge. 8 | 9 | When you create a new bug report please include following information: 10 | 11 | * Short description 12 | * Environment on which you are able to reproduce it 13 | * Steps to reproduce 14 | 15 | ## Feature requests 16 | 17 | Feature requests are labelled as [enhancement](https://github.com/creditkarma/thrift-server/labels/enhancement). 18 | We welcome new feature requests but first find out if a similar request is already in the queue. 19 | 20 | ## Pull requests (bug fix, features, etc) 21 | 22 | We welcome community contribution and help improving Thrift Server. Please keep your PRs focused to the scope. 23 | 24 | All PRs will be reviewed by a core team who maintains Thrift Server project. 25 | The core team will decide a PR should be merged to master and released as a patch, minor or major version. 26 | 27 | ### Testing your PR 28 | 29 | Because there are many interdependencies within the `thrift-server` repo there is a `thrift-integration` package for testing integrations between packages to avoid circular dependencies. Once you have completed your work in a given package it may be necessary to add a test to `thrift-integration`. 30 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/utils/appendThriftObject.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IProtocolConstructor, 3 | IStructCodec, 4 | ITransportConstructor, 5 | ProtocolType, 6 | TransportType, 7 | } from '../types' 8 | 9 | import { BinaryProtocol, getProtocol, TProtocol } from '../protocols' 10 | 11 | import { BufferedTransport, getTransport, TTransport } from '../transports' 12 | 13 | export function encode( 14 | thriftObject: LooseType, 15 | ThriftCodec: IStructCodec, 16 | Transport: ITransportConstructor = BufferedTransport, 17 | Protocol: IProtocolConstructor = BinaryProtocol, 18 | ): Promise { 19 | return new Promise((resolve, reject) => { 20 | const transport: TTransport = new Transport() 21 | const protocol: TProtocol = new Protocol(transport) 22 | ThriftCodec.encode(thriftObject, protocol) 23 | const data = protocol.flush() 24 | resolve(data) 25 | }) 26 | } 27 | 28 | export function appendThriftObject( 29 | value: LooseType, 30 | data: Buffer, 31 | ThriftCodec: IStructCodec, 32 | transportType: TransportType = 'buffered', 33 | protocolType: ProtocolType = 'binary', 34 | ): Promise { 35 | const Transport: ITransportConstructor = getTransport(transportType) 36 | const Protocol: IProtocolConstructor = getProtocol(protocolType) 37 | 38 | return encode(value, ThriftCodec, Transport, Protocol).then( 39 | (encoded: Buffer) => { 40 | return Buffer.concat([encoded, data]) 41 | }, 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/thrift/shared.thrift: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | /** 21 | * This Thrift file can be included by other Thrift files that want to share 22 | * these definitions. 23 | */ 24 | 25 | namespace cpp shared 26 | namespace d share // "shared" would collide with the eponymous D keyword. 27 | namespace dart shared 28 | namespace java shared 29 | namespace perl shared 30 | namespace php shared 31 | namespace haxe shared 32 | namespace netcore shared 33 | 34 | struct SharedStruct { 35 | 1: i32 key 36 | 2: string value 37 | } 38 | 39 | union SharedUnion { 40 | 1: string option1 41 | 2: string option2 42 | } 43 | 44 | service SharedService { 45 | SharedStruct getStruct(1: i32 key) 46 | SharedUnion getUnion(1: i32 index) 47 | } 48 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/transports/ThriftFrameCodec.ts: -------------------------------------------------------------------------------- 1 | import * as binary from '../binary' 2 | 3 | const MAX_PACKETS: number = 10 4 | 5 | export class ThriftFrameCodec { 6 | public encode(dateToSend: Buffer): Buffer { 7 | const msg = Buffer.alloc(dateToSend.length + 4) 8 | binary.writeI32(msg, dateToSend.length) 9 | dateToSend.copy(msg, 4, 0, dateToSend.length) 10 | 11 | return msg 12 | } 13 | 14 | public decode(dataToRead: Buffer): Buffer { 15 | const dataToReturn: Buffer = Buffer.alloc(dataToRead.length - 4) 16 | let writeCursor: number = 0 17 | let count: number = 0 18 | 19 | while (dataToRead.length > 0) { 20 | // We don't have enough to read frame size, something is wrong 21 | if (dataToRead.length < 4) { 22 | return Buffer.alloc(0) 23 | } else { 24 | const frameSize: number = binary.readI32(dataToRead, 0) 25 | if (dataToRead.length < 4 + frameSize) { 26 | return Buffer.alloc(0) 27 | } 28 | 29 | const frame: Buffer = dataToRead.slice(4, 4 + frameSize) 30 | const remaining: Buffer = dataToRead.slice(4 + frameSize) 31 | 32 | frame.copy(dataToReturn, 0, writeCursor) 33 | writeCursor += frame.length 34 | 35 | dataToRead = remaining 36 | count++ 37 | 38 | if (count >= MAX_PACKETS) { 39 | dataToRead = Buffer.alloc(0) 40 | } 41 | } 42 | } 43 | 44 | return dataToReturn 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-hapi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/zipkin-tracing-hapi", 3 | "version": "1.0.4", 4 | "description": "Zipkin tracing for thrift-server-hapi", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "files": [ 8 | "dist/main" 9 | ], 10 | "keywords": [], 11 | "scripts": { 12 | "clean": "rimraf ./dist", 13 | "lint": "tslint --fix './src/**/*.ts'", 14 | "format": "prettier --write 'src/**/*.ts'", 15 | "prebuild": "npm run clean && npm run lint && npm run format", 16 | "build": "tsc", 17 | "test": "npm run test:only", 18 | "test:only": "echo 'No tests for zipkin-tracing-hapi'; exit 0;" 19 | }, 20 | "dependencies": { 21 | "zipkin": "^0.15.0" 22 | }, 23 | "peerDependencies": { 24 | "@creditkarma/thrift-server-core": "^1.0.0", 25 | "@creditkarma/thrift-server-hapi": "^1.0.0", 26 | "@creditkarma/zipkin-core": "^1.0.0", 27 | "@hapi/hapi": ">=20.0.0", 28 | "@types/hapi": ">=17.0.0 <20.0.0" 29 | }, 30 | "devDependencies": { 31 | "@creditkarma/thrift-server-core": "^1.0.4", 32 | "@creditkarma/thrift-server-hapi": "^1.0.4", 33 | "@creditkarma/zipkin-core": "^1.0.4", 34 | "@hapi/hapi": "^20.0.0", 35 | "@types/hapi__hapi": "^20.0.0", 36 | "@types/node": "^16.11.0", 37 | "prettier": "^1.18.2", 38 | "rimraf": "^2.6.2", 39 | "tslint": "^5.11.0", 40 | "tslint-config-prettier": "^1.15.0", 41 | "tslint-plugin-prettier": "^2.0.0", 42 | "typescript": "4.6.x" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/README.md: -------------------------------------------------------------------------------- 1 | # Thrift Ecosystem 2 | 3 | ## Running the Sample Application 4 | 5 | Included in this repo is a sample application that uses `thrift-client` and `thrift-server-hapi`. To get the sample application up and running you need to do a few things. 6 | 7 | First, clone the `thrift-server` repo: 8 | 9 | ```sh 10 | $ git clone https://github.com/creditkarma/thrift-server.git 11 | ``` 12 | 13 | Then, `cd` into the `thrift-server` directory and run `npm install` and `npm run build`. 14 | 15 | ```sh 16 | $ cd thrift-server 17 | $ npm install 18 | $ npm run build 19 | ``` 20 | 21 | The `thrift-server` project uses [lerna](https://lernajs.io/) to manage inter-library dependencies. The `npm install` command will obviously install all your dependencies, but it will also perform a `lerna bootstrap` that will set up sym-links between all the libraries within the mono-repo. 22 | 23 | Now that everything is linked and built we can go to the `thrift-client` package and start the example application: 24 | 25 | ```sh 26 | $ cd packages/thrift-client 27 | $ npm start 28 | ``` 29 | 30 | This will start a web server on localhost:8080. The sample app has a UI you can visit from a web browser. 31 | 32 | ### Running Zipkin 33 | 34 | The example app is configured to emit Zipkin traces. To view these traces run the Zipkin Docker image: 35 | 36 | ```sh 37 | $ docker run -d -p 9411:9411 openzipkin/zipkin 38 | ``` 39 | 40 | ## Contributing 41 | 42 | For more information about contributing new features and bug fixes, see our [Contribution Guidelines](../../CONTRIBUTING.md). 43 | External contributors must sign Contributor License Agreement (CLA) 44 | 45 | ## License 46 | 47 | This project is licensed under [Apache License Version 2.0](./LICENSE) 48 | -------------------------------------------------------------------------------- /packages/thrift-client-zipkin-filter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-client-zipkin-filter", 3 | "version": "1.0.4", 4 | "description": "Zipkin tracing for thrift-client", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/creditkarma/thrift-server/tree/master/packages/thrift-client-zipkin-filter" 10 | }, 11 | "files": [ 12 | "dist/main" 13 | ], 14 | "keywords": [ 15 | "thrift", 16 | "typescript", 17 | "tracing", 18 | "zipkin" 19 | ], 20 | "scripts": { 21 | "clean": "rimraf ./dist", 22 | "lint": "tslint --fix './src/**/*.ts'", 23 | "format": "prettier --write 'src/**/*.ts'", 24 | "prebuild": "npm run clean && npm run lint && npm run format", 25 | "build": "tsc", 26 | "test": "npm run test:only", 27 | "test:only": "echo 'No tests for thrift-client-zipkin-filter'; exit 0;" 28 | }, 29 | "dependencies": { 30 | "got": "^11.8.3", 31 | "zipkin": "^0.15.0" 32 | }, 33 | "peerDependencies": { 34 | "@creditkarma/thrift-client": "^1.0.0", 35 | "@creditkarma/thrift-server-core": "^1.0.0", 36 | "@creditkarma/zipkin-core": "^1.0.0" 37 | }, 38 | "devDependencies": { 39 | "@creditkarma/thrift-client": "^1.0.4", 40 | "@creditkarma/thrift-server-core": "^1.0.4", 41 | "@creditkarma/zipkin-core": "^1.0.4", 42 | "@types/node": "^16.11.0", 43 | "prettier": "^1.18.2", 44 | "rimraf": "^2.6.2", 45 | "tslint": "^5.11.0", 46 | "tslint-config-prettier": "^1.15.0", 47 | "tslint-plugin-prettier": "^2.0.0", 48 | "typescript": "4.6.x" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-server-ecosystem", 3 | "version": "1.0.4", 4 | "description": "Example services built on thrift-server", 5 | "scripts": { 6 | "clean": "npm run clean-dist && npm run clean-gen", 7 | "clean-dist": "rimraf ./dist", 8 | "clean-gen": "rimraf ./src/generated", 9 | "lint": "tslint --fix './src/**/*.ts'", 10 | "format": "prettier --write 'src/**/*.ts'", 11 | "codegen": "thrift-typescript --target thrift-server --rootDir . --sourceDir ./thrift --outDir ./src/generated", 12 | "prebuild": "npm run clean && npm run lint && npm run format && npm run codegen", 13 | "build": "tsc", 14 | "test": "npm run test:only", 15 | "test:only": "echo 'No tests for thrift ecosystem'; exit 0;" 16 | }, 17 | "dependencies": { 18 | "@types/node": "^16.11.0" 19 | }, 20 | "devDependencies": { 21 | "@creditkarma/dynamic-config": "^1.0.0", 22 | "@creditkarma/thrift-client": "^1.0.4", 23 | "@creditkarma/thrift-client-zipkin-filter": "^1.0.4", 24 | "@creditkarma/thrift-server-core": "^1.0.4", 25 | "@creditkarma/thrift-server-express": "^1.0.4", 26 | "@creditkarma/thrift-server-hapi": "^1.0.4", 27 | "@creditkarma/thrift-typescript": "^3.7.6", 28 | "@creditkarma/zipkin-tracing-express": "^1.0.4", 29 | "@creditkarma/zipkin-tracing-hapi": "^1.0.4", 30 | "@hapi/hapi": "^20.0.0", 31 | "@types/express": "^4.16.0", 32 | "@types/hapi__hapi": "^20.0.0", 33 | "express": "^4.16.4", 34 | "prettier": "^1.15.2", 35 | "tslint": "^5.11.0", 36 | "tslint-config-prettier": "^1.15.0", 37 | "tslint-plugin-prettier": "^2.0.0", 38 | "typescript": "4.6.x" 39 | }, 40 | "publishConfig": { 41 | "access": "private" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/thrift-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-client", 3 | "version": "1.0.4", 4 | "description": "Thrift client library for NodeJS written in TypeScript.", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "files": [ 8 | "dist/main", 9 | "dist/ttwitter" 10 | ], 11 | "directories": { 12 | "example": "example" 13 | }, 14 | "keywords": [ 15 | "thrift", 16 | "typescript", 17 | "rpc", 18 | "microservices", 19 | "http client", 20 | "framework" 21 | ], 22 | "scripts": { 23 | "clean": "rimraf ./dist", 24 | "lint": "tslint --fix './src/**/*.ts'", 25 | "format": "prettier --write 'src/**/*.ts'", 26 | "prebuild": "npm run clean && npm run lint && npm run format", 27 | "build": "tsc", 28 | "test": "npm run test:only", 29 | "test:only": "echo 'No tests for thrift-client'; exit 0;" 30 | }, 31 | "author": "Credit Karma", 32 | "license": "Apache-2.0", 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/creditkarma/thrift-server/tree/master/packages/thrift-client" 36 | }, 37 | "peerDependencies": { 38 | "@creditkarma/thrift-server-core": "^1.0.0" 39 | }, 40 | "devDependencies": { 41 | "@creditkarma/thrift-server-core": "^1.0.4", 42 | "@types/node": "^16.11.0", 43 | "prettier": "^1.18.2", 44 | "rimraf": "^2.6.2", 45 | "tslint": "^5.11.0", 46 | "tslint-config-prettier": "^1.15.0", 47 | "tslint-plugin-prettier": "^2.0.0", 48 | "typescript": "4.6.x" 49 | }, 50 | "dependencies": { 51 | "@types/generic-pool": "^3.1.4", 52 | "generic-pool": "^3.4.2", 53 | "got": "^11.8.3" 54 | }, 55 | "publishConfig": { 56 | "access": "public" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/thrift-server-hapi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-server-hapi", 3 | "version": "1.0.4", 4 | "description": "A Hapi server plugin for the Apache Thrift protocol", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "files": [ 8 | "dist/main" 9 | ], 10 | "keywords": [ 11 | "thrift", 12 | "hapi", 13 | "typescript", 14 | "rpc", 15 | "microservices", 16 | "server", 17 | "framework" 18 | ], 19 | "scripts": { 20 | "clean": "rimraf ./dist ./src/tests/generated", 21 | "lint": "tslint --fix './src/**/*.ts'", 22 | "format": "prettier --write 'src/**/*.ts'", 23 | "prebuild": "npm run clean", 24 | "build": "npm run lint && npm run format && tsc", 25 | "test": "npm run test:only", 26 | "test:only": "echo 'No tests for thrift-server-hapi'; exit 0;" 27 | }, 28 | "author": "Credit Karma", 29 | "license": "Apache-2.0", 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/creditkarma/thrift-server/tree/master/packages/thrift-server-hapi" 33 | }, 34 | "peerDependencies": { 35 | "@creditkarma/thrift-server-core": "^1.0.0", 36 | "@hapi/hapi": ">=20.0.0", 37 | "@types/hapi__hapi": ">=20.0.0" 38 | }, 39 | "devDependencies": { 40 | "@creditkarma/thrift-server-core": "^1.0.4", 41 | "@hapi/hapi": "^20.0.0", 42 | "@types/hapi__hapi": "^20.0.0", 43 | "@types/node": "^16.11.0", 44 | "prettier": "^1.18.2", 45 | "rimraf": "^2.6.2", 46 | "tslint": "^5.11.0", 47 | "tslint-config-prettier": "^1.15.0", 48 | "tslint-plugin-prettier": "^2.0.0", 49 | "typescript": "4.6.x" 50 | }, 51 | "dependencies": { 52 | "zipkin": "^0.15.0" 53 | }, 54 | "publishConfig": { 55 | "access": "public" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/thrift-server-express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-server-express", 3 | "version": "1.0.4", 4 | "description": "Express server middleware for the Apache Thrift protocol", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "files": [ 8 | "dist/main" 9 | ], 10 | "keywords": [ 11 | "thrift", 12 | "express", 13 | "typescript", 14 | "rpc", 15 | "microservices", 16 | "server", 17 | "framework" 18 | ], 19 | "scripts": { 20 | "clean": "rimraf ./dist ./src/tests/generated", 21 | "lint": "tslint --fix './src/**/*.ts'", 22 | "format": "prettier --write 'src/**/*.ts'", 23 | "prebuild": "npm run clean", 24 | "build": "npm run lint && npm run format && tsc", 25 | "test": "npm run test:only", 26 | "test:only": "echo 'No tests for thrift-server-express'; exit 0;" 27 | }, 28 | "author": "Credit Karma", 29 | "license": "Apache-2.0", 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/creditkarma/thrift-server/tree/master/packages/thrift-server-express" 33 | }, 34 | "peerDependencies": { 35 | "@creditkarma/thrift-server-core": "^1.0.0", 36 | "@types/express": ">=4.0.0 <5.0.0", 37 | "express": ">=4.0.0 <5.0.0" 38 | }, 39 | "devDependencies": { 40 | "@creditkarma/thrift-server-core": "^1.0.4", 41 | "@types/body-parser": "^1.16.5", 42 | "@types/express": "^4.16.0", 43 | "@types/node": "^16.11.0", 44 | "express": "^4.16.3", 45 | "prettier": "^1.18.2", 46 | "rimraf": "^2.6.2", 47 | "tslint": "^5.11.0", 48 | "tslint-config-prettier": "^1.15.0", 49 | "tslint-plugin-prettier": "^2.0.0", 50 | "typescript": "4.6.x" 51 | }, 52 | "dependencies": { 53 | "zipkin": "^0.15.0" 54 | }, 55 | "publishConfig": { 56 | "access": "public" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-client-ttwitter-filter", 3 | "version": "1.0.4", 4 | "description": "Filter for appending TTwitter context onto thrift-client payload", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "files": [ 8 | "dist/main" 9 | ], 10 | "keywords": [], 11 | "scripts": { 12 | "clean": "npm run clean-dist && npm run clean-gen", 13 | "clean-dist": "rimraf ./dist", 14 | "clean-gen": "rimraf ./src/ttwitter", 15 | "lint": "tslint --fix './src/**/*.ts'", 16 | "format": "prettier --write 'src/**/*.ts'", 17 | "precodegen": "npm run clean-gen", 18 | "codegen": "thrift-typescript --target thrift-server --rootDir ./src --sourceDir ./thrift --outDir ./ttwitter", 19 | "prebuild": "npm run clean && npm run lint && npm run format && npm run codegen", 20 | "build": "tsc", 21 | "test": "npm run test:only", 22 | "test:only": "echo 'No tests for thrift-client-ttwitter-filter'; exit 0;" 23 | }, 24 | "dependencies": { 25 | "zipkin": "^0.15.0" 26 | }, 27 | "peerDependencies": { 28 | "@creditkarma/thrift-client": "^1.0.0", 29 | "@creditkarma/thrift-client-context-filter": "^1.0.0", 30 | "@creditkarma/thrift-server-core": "^1.0.0", 31 | "@creditkarma/zipkin-core": "^1.0.0" 32 | }, 33 | "devDependencies": { 34 | "@creditkarma/thrift-client": "^1.0.4", 35 | "@creditkarma/thrift-client-context-filter": "^1.0.4", 36 | "@creditkarma/thrift-server-core": "^1.0.4", 37 | "@creditkarma/thrift-typescript": "^3.7.6", 38 | "@creditkarma/zipkin-core": "^1.0.4", 39 | "@types/node": "^16.11.0", 40 | "prettier": "^1.18.2", 41 | "rimraf": "^2.6.2", 42 | "tslint": "^5.11.0", 43 | "tslint-config-prettier": "^1.15.0", 44 | "tslint-plugin-prettier": "^2.0.0", 45 | "typescript": "4.6.x" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/thrift-server-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-server-core", 3 | "version": "1.0.4", 4 | "description": "Thrift core library in TypeScript", 5 | "main": "dist/main/index.js", 6 | "types": "dist/main/index.d.ts", 7 | "files": [ 8 | "dist/main", 9 | "NOTICE" 10 | ], 11 | "keywords": [ 12 | "Thrift", 13 | "TypeScript", 14 | "RPC" 15 | ], 16 | "scripts": { 17 | "clean": "rimraf ./dist", 18 | "lint": "tslint --fix './src/**/*.ts'", 19 | "format": "prettier --write 'src/**/*.ts'", 20 | "codegen": "thrift-typescript --target thrift-server --rootDir ./src/tests --sourceDir ./thrift --outDir ./generated --library ../../../main", 21 | "prebuild": "npm run clean && npm run codegen", 22 | "build": "npm run lint && npm run format && tsc", 23 | "pretest": "npm run build", 24 | "test": "npm run test:only", 25 | "test:only": "lab --timeout 15000 --verbose -l -S -P spec dist/tests/unit" 26 | }, 27 | "author": "Credit Karma", 28 | "license": "Apache-2.0", 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/creditkarma/thrift-server/tree/master/packages/thrift-server-core" 32 | }, 33 | "devDependencies": { 34 | "@creditkarma/thrift-typescript": "^3.7.6", 35 | "@hapi/code": "^8.0.7", 36 | "@hapi/lab": "^25.0.1", 37 | "@types/hapi__code": "^8.0.1", 38 | "@types/hapi__lab": "^18.1.0", 39 | "@types/node": "^16.11.0", 40 | "@types/rimraf": "2.0.2", 41 | "prettier": "^1.18.2", 42 | "rimraf": "^2.6.2", 43 | "tslint": "^5.11.0", 44 | "tslint-config-prettier": "^1.15.0", 45 | "tslint-plugin-prettier": "^2.0.0", 46 | "typescript": "4.6.x" 47 | }, 48 | "dependencies": { 49 | "@types/lodash": "^4.14.136", 50 | "lodash": "^4.17.15" 51 | }, 52 | "publishConfig": { 53 | "access": "public" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/src/add-service.ts: -------------------------------------------------------------------------------- 1 | import { config } from '@creditkarma/dynamic-config' 2 | import { Int64 } from '@creditkarma/thrift-server-core' 3 | 4 | import { createThriftServer } from '@creditkarma/thrift-server-hapi' 5 | 6 | import { ZipkinTracingHapi } from '@creditkarma/zipkin-tracing-hapi' 7 | 8 | import * as Hapi from '@hapi/hapi' 9 | 10 | import { AddService } from './generated/add-service' 11 | 12 | async function init(): Promise { 13 | const SERVER_CONFIG = await config().get('add-service') 14 | 15 | /** 16 | * Implementation of our thrift service. 17 | * 18 | * Notice the second parameter, "context" - this is the Hapi request object, 19 | * passed along to our service by the Hapi thrift plugin. Thus, you have access to 20 | * all HTTP request data from within your service implementation. 21 | */ 22 | const impl = new AddService.Processor({ 23 | ping(): void { 24 | return 25 | }, 26 | add(a: number, b: number, context?: Hapi.Request): number { 27 | return a + b 28 | }, 29 | addInt64(a: Int64, b: Int64, context?: Hapi.Request): Int64 { 30 | return new Int64(a.toNumber() + b.toNumber()) 31 | }, 32 | }) 33 | 34 | /** 35 | * Creates Hapi server with thrift endpoint. 36 | */ 37 | const server: Hapi.Server = await createThriftServer({ 38 | port: SERVER_CONFIG.port, 39 | path: SERVER_CONFIG.path, 40 | thriftOptions: { 41 | serviceName: 'add-service', 42 | handler: impl, 43 | }, 44 | }) 45 | 46 | await server.register({ 47 | plugin: ZipkinTracingHapi({ 48 | localServiceName: 'add-service', 49 | tracerConfig: { 50 | endpoint: 'http://localhost:9411/api/v1/spans', 51 | sampleRate: 1.0, 52 | httpInterval: 1000, 53 | httpTimeout: 5000, 54 | }, 55 | }), 56 | }) 57 | 58 | await server.start() 59 | } 60 | 61 | init() 62 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/utils/readThriftObject.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IProtocolConstructor, 3 | IStructCodec, 4 | IThriftField, 5 | ITransportConstructor, 6 | ProtocolType, 7 | TransportType, 8 | TType, 9 | } from '../types' 10 | 11 | import { getProtocol, TProtocol } from '../protocols' 12 | 13 | import { getTransport, TTransport } from '../transports' 14 | 15 | export function readThriftObject( 16 | data: Buffer, 17 | ThriftCodec: IStructCodec, 18 | transportType: TransportType = 'buffered', 19 | protocolType: ProtocolType = 'binary', 20 | ): Promise<[StrictType, Buffer]> { 21 | return new Promise((resolve, reject) => { 22 | const Transport: ITransportConstructor = getTransport(transportType) 23 | const Protocol: IProtocolConstructor = getProtocol(protocolType) 24 | const receiver: TTransport = new Transport(data) 25 | const input: TProtocol = new Protocol(receiver) 26 | const decoded = ThriftCodec.decode(input) 27 | 28 | resolve([decoded, receiver.remaining()]) 29 | }) 30 | } 31 | 32 | export function stripStruct( 33 | data: Buffer, 34 | transportType: TransportType = 'buffered', 35 | protocolType: ProtocolType = 'binary', 36 | ): Buffer { 37 | try { 38 | const Transport: ITransportConstructor = getTransport(transportType) 39 | const Protocol: IProtocolConstructor = getProtocol(protocolType) 40 | const receiver: TTransport = new Transport(data) 41 | const input: TProtocol = new Protocol(receiver) 42 | 43 | input.readStructBegin() 44 | 45 | while (true) { 46 | const ret: IThriftField = input.readFieldBegin() 47 | const fieldType: TType = ret.fieldType 48 | if (fieldType === TType.STOP) { 49 | break 50 | } else { 51 | input.skip(fieldType) 52 | } 53 | input.readFieldEnd() 54 | } 55 | 56 | input.readStructEnd() 57 | 58 | return receiver.remaining() 59 | } catch (err) { 60 | return data 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/tests/thrift/calculator-service.thrift: -------------------------------------------------------------------------------- 1 | include "shared.thrift" 2 | include "common.thrift" 3 | include "operation.thrift" 4 | 5 | namespace java calculator-service 6 | 7 | typedef i32 MyInteger 8 | typedef operation.Operation Operation 9 | typedef common.CommonStruct CommonStruct 10 | 11 | const i32 INT32CONSTANT = 9853 12 | const map MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} 13 | 14 | struct Work { 15 | 1: required i32 num1 = 0, 16 | 2: required i32 num2, 17 | 3: required Operation op, 18 | 4: optional string comment, 19 | } 20 | 21 | struct FirstName { 22 | 1: string name 23 | } 24 | 25 | struct LastName { 26 | 1: string name 27 | } 28 | 29 | union Choice { 30 | 1: FirstName firstName 31 | 2: LastName lastName 32 | } 33 | 34 | service Calculator extends shared.SharedService { 35 | 36 | void ping(), 37 | 38 | i32 add(1: i32 num1, 2: i32 num2) throws (1: operation.JankyResult exp), 39 | 40 | i64 addInt64(1: i64 num1, 2: i64 num2), 41 | 42 | i32 addWithContext(1: i32 num1, 2: i32 num2), 43 | 44 | i32 calculate(1:i32 logid, 2:Work work) throws (1: operation.JankyOperation ouch), 45 | 46 | string echoBinary(1: binary word) 47 | 48 | string echoString(1: string word) 49 | 50 | string checkName(1: Choice choice), 51 | 52 | string checkOptional(1: optional string type), 53 | 54 | list mapOneList(1: list arg) 55 | 56 | list mapValues(1: map arg) 57 | 58 | map listToMap(1: list> arg) 59 | 60 | common.CommonStruct fetchThing() 61 | 62 | /** 63 | * This method has a oneway modifier. That means the client only makes 64 | * a request and does not listen for any response at all. Oneway methods 65 | * must be void. 66 | 67 | */ 68 | oneway void zip() 69 | 70 | } 71 | 72 | /** 73 | * That just about covers the basics. Take a look in the test/ folder for more 74 | * detailed examples. After you run this file, your generated code shows up 75 | * in folders with names gen-. The generated code isn't too scary 76 | * to look at. It even has pretty indentation. 77 | */ 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-server", 3 | "description": "Thrift core library in TypeScript", 4 | "private": true, 5 | "scripts": { 6 | "build": "lerna exec -- npm run build", 7 | "clean": "rimraf **/package-lock.json **/node_modules **/dist **/coverage **/generated **/generated-apache", 8 | "lint": "lerna exec -- npm run lint", 9 | "updated": "lerna updated", 10 | "postinstall": "lerna bootstrap --no-ci", 11 | "pretest": "npm run build", 12 | "test": "npm run test:only", 13 | "test:only": "lerna exec -- npm run test:only", 14 | "release:default": "lerna version --conventional-commits --no-push --yes --force-publish=*", 15 | "release:patch": "lerna version --cd-version patch --no-push --yes --force-publish=*", 16 | "release:minor": "lerna version --cd-version minor --no-push --yes --force-publish=*", 17 | "release:major": "lerna version --cd-version major --no-push --yes --force-publish=*", 18 | "release:prepatch": "lerna version --cd-version prepatch --no-push --yes --force-publish=*", 19 | "release:preminor": "lerna version --cd-version preminor --no-push --yes --force-publish=*", 20 | "release:premajor": "lerna version --cd-version premajor --no-push --yes --force-publish=*", 21 | "release:prerelease": "lerna version --cd-version prerelease --no-push --yes --force-publish=*", 22 | "release:publish": "lerna exec --ignore '@creditkarma/thrift-{integration,server-ecosystem}' 'npm publish --access public'", 23 | "release:publish-fix": "lerna exec --ignore '@creditkarma/thrift-{integration,server-ecosystem}' 'npm publish --tag fix --access public'", 24 | "release:publish-next": "lerna exec --ignore '@creditkarma/thrift-{integration,server-ecosystem}' 'npm publish --tag next --access public'" 25 | }, 26 | "keywords": [ 27 | "thrift", 28 | "typescript", 29 | "microservices", 30 | "rpc", 31 | "framework" 32 | ], 33 | "author": "Credit Karma", 34 | "license": "Apache-2.0", 35 | "repository": { 36 | "type": "git", 37 | "url": "https://github.com/creditkarma/thrift-server" 38 | }, 39 | "devDependencies": { 40 | "lerna": "^5.1.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/thrift-server-express/src/main/ThriftServerExpress.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getProtocol, 3 | getTransport, 4 | IProtocolConstructor, 5 | IThriftProcessor, 6 | ITransportConstructor, 7 | process, 8 | readThriftMethod, 9 | } from '@creditkarma/thrift-server-core' 10 | 11 | import * as express from 'express' 12 | 13 | import { IExpressServerOptions } from './types' 14 | 15 | type ThriftRequest = express.Request & { 16 | thrift?: { 17 | requestMethod: string 18 | processor: IThriftProcessor 19 | transport: string 20 | protocol: string 21 | } 22 | } 23 | 24 | export function ThriftServerExpress< 25 | TProcessor extends IThriftProcessor 26 | >(pluginOptions: IExpressServerOptions): express.RequestHandler { 27 | return ( 28 | request: ThriftRequest, 29 | response: express.Response, 30 | next: express.NextFunction, 31 | ): void => { 32 | const Transport: ITransportConstructor = getTransport( 33 | pluginOptions.transport, 34 | ) 35 | const Protocol: IProtocolConstructor = getProtocol( 36 | pluginOptions.protocol, 37 | ) 38 | const buffer: Buffer = request.body 39 | 40 | const method: string = readThriftMethod(buffer, Transport, Protocol) 41 | 42 | request.thrift = { 43 | requestMethod: method, 44 | processor: pluginOptions.handler, 45 | transport: pluginOptions.transport || 'buffered', 46 | protocol: pluginOptions.protocol || 'binary', 47 | } 48 | 49 | try { 50 | process({ 51 | processor: pluginOptions.handler, 52 | buffer, 53 | Transport, 54 | Protocol, 55 | context: request, 56 | }).then( 57 | (result: any) => { 58 | response.status(200).end(result) 59 | }, 60 | (err: any) => { 61 | next(err) 62 | }, 63 | ) 64 | } catch (err) { 65 | next(err) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/src/ttwitter/com/creditkarma/finagle/thrift/UpgradeReply.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /* 4 | * Autogenerated by @creditkarma/thrift-typescript v3.7.6 5 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 6 | */ 7 | import * as thrift from "@creditkarma/thrift-server-core"; 8 | export interface IUpgradeReply { 9 | } 10 | export interface IUpgradeReplyArgs { 11 | } 12 | export const UpgradeReplyCodec: thrift.IStructCodec = { 13 | encode(args: IUpgradeReplyArgs, output: thrift.TProtocol): void { 14 | output.writeStructBegin("UpgradeReply"); 15 | output.writeFieldStop(); 16 | output.writeStructEnd(); 17 | return; 18 | }, 19 | decode(input: thrift.TProtocol): IUpgradeReply { 20 | input.readStructBegin(); 21 | while (true) { 22 | const ret: thrift.IThriftField = input.readFieldBegin(); 23 | const fieldType: thrift.TType = ret.fieldType; 24 | const fieldId: number = ret.fieldId; 25 | if (fieldType === thrift.TType.STOP) { 26 | break; 27 | } 28 | switch (fieldId) { 29 | default: { 30 | input.skip(fieldType); 31 | } 32 | } 33 | input.readFieldEnd(); 34 | } 35 | input.readStructEnd(); 36 | return {}; 37 | } 38 | }; 39 | export class UpgradeReply extends thrift.StructLike implements IUpgradeReply { 40 | public readonly _annotations: thrift.IThriftAnnotations = {}; 41 | public readonly _fieldAnnotations: thrift.IFieldAnnotations = {}; 42 | constructor(args: IUpgradeReplyArgs = {}) { 43 | super(); 44 | } 45 | public static read(input: thrift.TProtocol): UpgradeReply { 46 | return new UpgradeReply(UpgradeReplyCodec.decode(input)); 47 | } 48 | public static write(args: IUpgradeReplyArgs, output: thrift.TProtocol): void { 49 | return UpgradeReplyCodec.encode(args, output); 50 | } 51 | public write(output: thrift.TProtocol): void { 52 | return UpgradeReplyCodec.encode(this, output); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/thrift/calculator-service.thrift: -------------------------------------------------------------------------------- 1 | include "shared.thrift" 2 | include "common.thrift" 3 | include "operation.thrift" 4 | 5 | namespace java calculator-service 6 | 7 | typedef i32 MyInteger 8 | typedef operation.Operation Operation 9 | typedef common.CommonStruct CommonStruct 10 | 11 | const i32 INT32CONSTANT = 9853 12 | const map MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} 13 | 14 | struct Work { 15 | 1: required i32 num1 = 0, 16 | 2: required i32 num2, 17 | 3: required Operation op, 18 | 4: optional string comment, 19 | } 20 | 21 | struct FirstName { 22 | 1: string name 23 | } 24 | 25 | struct LastName { 26 | 1: string name 27 | } 28 | 29 | union Choice { 30 | 1: FirstName firstName 31 | 2: LastName lastName 32 | } 33 | 34 | service Calculator extends shared.SharedService { 35 | 36 | void ping(), 37 | 38 | i32 add(1: i32 num1, 2: i32 num2) throws (1: operation.JankyResult exp), 39 | 40 | i64 addInt64(1: i64 num1, 2: i64 num2), 41 | 42 | i32 addWithContext(1: i32 num1, 2: i32 num2), 43 | 44 | i32 calculate(1:i32 logid, 2:Work work) throws (1: operation.JankyOperation ouch), 45 | 46 | string echoBinary(1: binary word) 47 | 48 | string echoString(1: string word) 49 | 50 | string checkName(1: Choice choice), 51 | 52 | string checkOptional(1: optional string type), 53 | 54 | list mapOneList(1: list arg) 55 | 56 | list mapValues(1: map arg) 57 | 58 | map listToMap(1: list> arg) 59 | 60 | common.CommonStruct fetchThing() 61 | 62 | common.CommonUnion fetchUnion() 63 | 64 | /** 65 | * This method has a oneway modifier. That means the client only makes 66 | * a request and does not listen for any response at all. Oneway methods 67 | * must be void. 68 | 69 | */ 70 | oneway void zip() 71 | 72 | void broken() 73 | 74 | } 75 | 76 | /** 77 | * That just about covers the basics. Take a look in the test/ folder for more 78 | * detailed examples. After you run this file, your generated code shows up 79 | * in folders with names gen-. The generated code isn't too scary 80 | * to look at. It even has pretty indentation. 81 | */ 82 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/src/ttwitter/com/creditkarma/finagle/thrift/ConnectionOptions.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /* 4 | * Autogenerated by @creditkarma/thrift-typescript v3.7.6 5 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 6 | */ 7 | import * as thrift from "@creditkarma/thrift-server-core"; 8 | export interface IConnectionOptions { 9 | } 10 | export interface IConnectionOptionsArgs { 11 | } 12 | export const ConnectionOptionsCodec: thrift.IStructCodec = { 13 | encode(args: IConnectionOptionsArgs, output: thrift.TProtocol): void { 14 | output.writeStructBegin("ConnectionOptions"); 15 | output.writeFieldStop(); 16 | output.writeStructEnd(); 17 | return; 18 | }, 19 | decode(input: thrift.TProtocol): IConnectionOptions { 20 | input.readStructBegin(); 21 | while (true) { 22 | const ret: thrift.IThriftField = input.readFieldBegin(); 23 | const fieldType: thrift.TType = ret.fieldType; 24 | const fieldId: number = ret.fieldId; 25 | if (fieldType === thrift.TType.STOP) { 26 | break; 27 | } 28 | switch (fieldId) { 29 | default: { 30 | input.skip(fieldType); 31 | } 32 | } 33 | input.readFieldEnd(); 34 | } 35 | input.readStructEnd(); 36 | return {}; 37 | } 38 | }; 39 | export class ConnectionOptions extends thrift.StructLike implements IConnectionOptions { 40 | public readonly _annotations: thrift.IThriftAnnotations = {}; 41 | public readonly _fieldAnnotations: thrift.IFieldAnnotations = {}; 42 | constructor(args: IConnectionOptionsArgs = {}) { 43 | super(); 44 | } 45 | public static read(input: thrift.TProtocol): ConnectionOptions { 46 | return new ConnectionOptions(ConnectionOptionsCodec.decode(input)); 47 | } 48 | public static write(args: IConnectionOptionsArgs, output: thrift.TProtocol): void { 49 | return ConnectionOptionsCodec.encode(args, output); 50 | } 51 | public write(output: thrift.TProtocol): void { 52 | return ConnectionOptionsCodec.encode(this, output); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/thrift-client/src/main/connections/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BinaryProtocol, 3 | BufferedTransport, 4 | deepMerge, 5 | IClientConstructor, 6 | ThriftClient, 7 | } from '@creditkarma/thrift-server-core' 8 | 9 | import { HttpConnection } from './HttpConnection' 10 | import { NullConnection } from './NullConnection' 11 | 12 | import { 13 | ICreateHttpClientOptions, 14 | ICreateTcpClientOptions, 15 | RequestOptions, 16 | } from '../types' 17 | 18 | import { TcpConnection } from './TcpConnection' 19 | 20 | export * from './HttpConnection' 21 | export * from './TcpConnection' 22 | 23 | export function createClient>( 24 | ServiceClient: IClientConstructor, 25 | options: ICreateHttpClientOptions, 26 | ): TClient { 27 | console.warn(`[Deprecated]: Please use 'createHttpClient' instead`) 28 | return createHttpClient(ServiceClient, options) 29 | } 30 | 31 | export function createTcpClient>( 32 | ServiceClient: IClientConstructor, 33 | options: ICreateTcpClientOptions, 34 | ): TClient { 35 | const connection: TcpConnection = new TcpConnection(options) 36 | 37 | connection.register(...(options.register || [])) 38 | 39 | return new ServiceClient(connection) 40 | } 41 | 42 | export function createHttpClient>( 43 | ServiceClient: IClientConstructor, 44 | options: ICreateHttpClientOptions, 45 | ): TClient { 46 | let serviceName: string = '' 47 | if ((ServiceClient as any).serviceName !== 'undefined') { 48 | serviceName = (ServiceClient as any).serviceName 49 | } else { 50 | const nullConnection: NullConnection = new NullConnection( 51 | BufferedTransport, 52 | BinaryProtocol, 53 | ) 54 | const nullClient: TClient = new ServiceClient(nullConnection) 55 | serviceName = nullClient._serviceName 56 | } 57 | 58 | const connection: HttpConnection = new HttpConnection( 59 | deepMerge(options, { serviceName }), 60 | ) 61 | 62 | // Register optional middleware 63 | connection.register(...(options.register || [])) 64 | 65 | return new ServiceClient(connection) 66 | } 67 | -------------------------------------------------------------------------------- /packages/thrift-server-core/README.md: -------------------------------------------------------------------------------- 1 | # Thrift Core 2 | 3 | Base package for the other Thrift libraries. This will usually not be used directly. Usually consumers will get everything they need from the consuming libraries: 4 | 5 | * [Thrift-Client](https://github.com/creditkarma/thrift-server/tree/master/packages/thrift-client) 6 | * [Thrift-Server-Express](https://github.com/creditkarma/thrift-server/tree/master/packages/thrift-server-express) 7 | * [Thrift-Server-Hapi](https://github.com/creditkarma/thrift-server/tree/master/packages/thrift-server-hapi) 8 | 9 | ## Usage 10 | 11 | ```sh 12 | $ npm install --save @creditkarma/thrift-server-core 13 | ``` 14 | 15 | ### Int64 16 | 17 | For representing 64-bit integers in JavaScript we use the [node-int64 library](https://github.com/broofa/node-int64). We extend the base class from the library with static methods for working with 64-bit integers written as strings. These functions are largely taken from the apache thrift libs and added as static methods for convinience. 18 | 19 | #### `fromDecimalString` 20 | 21 | Given a string of decimal digits, return a `Int64` object instance. 22 | 23 | ```typescript 24 | import { Int64 } from '@creditkarma/thrift-server-core' 25 | 26 | const i64: Int64 = Int64.fromDecimalString("89374875") 27 | ``` 28 | 29 | #### `toDecimalString` 30 | 31 | ```typescript 32 | import { Int64 } from '@creditkarma/thrift-server-core' 33 | 34 | const i64: Int64 = Int64.fromDecimalString("89374875") 35 | const val: string = i64.toDecimalString() 36 | 37 | // val === "89374875" 38 | ``` 39 | 40 | ##### `toDecimalString` static 41 | 42 | For consistency the `toDecimalString` method is also available statically on the `Int64` class. The static method can take a `number` or `Int64` instance. 43 | 44 | ```typescript 45 | import { Int64 } from '@creditkarma/thrift-server-core' 46 | 47 | const i64: Int64 = new Int64(64) 48 | const val: string = Int64.toDecimalString(i64) 49 | 50 | // val === "64" 51 | ``` 52 | 53 | ## Roadmap 54 | 55 | * Support CompactProtocol, JsonProtocol and FramedTransport 56 | 57 | ## Contributing 58 | 59 | For more information about contributing new features and bug fixes, see our [Contribution Guidelines](../../CONTRIBUTING.md). 60 | External contributors must sign Contributor License Agreement (CLA) 61 | 62 | ## License 63 | 64 | This project is licensed under [Apache License Version 2.0](./LICENSE) 65 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/tests/unit/types.ts: -------------------------------------------------------------------------------- 1 | import { IInt64, Int64 } from '../../main' 2 | 3 | /** 4 | * Test class compatible with Int64. 5 | */ 6 | export class LikeInt64 implements IInt64 { 7 | /** @inheritDoc */ 8 | public readonly buffer: Buffer 9 | 10 | private readonly i64: Int64 11 | 12 | constructor(value: string) { 13 | this.i64 = Int64.fromDecimalString(value) 14 | this.buffer = this.i64.buffer 15 | } 16 | 17 | /** @inheritDoc */ 18 | public toDecimalString(): string { 19 | return this.i64.toDecimalString() 20 | } 21 | } 22 | 23 | /** 24 | * Test class with no buffer. 25 | */ 26 | export class NoBufferInt64 implements Omit { 27 | private readonly i64: Int64 28 | 29 | constructor(value: string) { 30 | this.i64 = Int64.fromDecimalString(value) 31 | } 32 | 33 | /** @inheritDoc */ 34 | public toDecimalString(): string { 35 | return this.i64.toDecimalString() 36 | } 37 | } 38 | 39 | /** 40 | * Test class with no toDecimalString(). 41 | */ 42 | export class NoStringInt64 implements Omit { 43 | /** @inheritDoc */ 44 | public readonly buffer: Buffer 45 | 46 | private readonly i64: Int64 47 | 48 | constructor(value: string) { 49 | this.i64 = Int64.fromDecimalString(value) 50 | this.buffer = this.i64.buffer 51 | } 52 | } 53 | 54 | /** 55 | * Test class with a shorter buffer. 56 | */ 57 | export class Int56 implements IInt64 { 58 | /** @inheritDoc */ 59 | public readonly buffer: Buffer 60 | 61 | private readonly i64: Int64 62 | 63 | constructor(value: string) { 64 | this.i64 = Int64.fromDecimalString(value) 65 | this.buffer = this.i64.buffer.slice(1) 66 | } 67 | 68 | /** @inheritDoc */ 69 | public toDecimalString(): string { 70 | return this.i64.toDecimalString() 71 | } 72 | } 73 | 74 | /** 75 | * Test class with a longer buffer. 76 | */ 77 | export class Int72 implements IInt64 { 78 | /** @inheritDoc */ 79 | public readonly buffer: Buffer 80 | 81 | private readonly i64: Int64 82 | 83 | constructor(value: string) { 84 | this.i64 = Int64.fromDecimalString(value) 85 | this.buffer = Buffer.alloc(9) 86 | this.i64.buffer.copy(this.buffer, 1) 87 | } 88 | 89 | /** @inheritDoc */ 90 | public toDecimalString(): string { 91 | return this.i64.toDecimalString() 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/tests/unit/parseJson.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from '@hapi/code' 2 | import * as Lab from '@hapi/lab' 3 | 4 | import { parseJson } from '../../main/parseJson' 5 | 6 | export const lab = Lab.script() 7 | const { describe, it } = lab 8 | 9 | describe('parse_json', () => { 10 | describe('objects', () => { 11 | it('parse when empty', () => { 12 | expect(parseJson('{}')).to.equal({}) 13 | }) 14 | 15 | it('parse when containing strings', () => { 16 | expect(parseJson('{"foo": "bar"}')).to.equal({ foo: 'bar' }) 17 | }) 18 | }) 19 | 20 | describe('arrays', () => { 21 | it('parse when empty', () => { 22 | expect(parseJson('[]')).to.equal([]) 23 | }) 24 | 25 | it('parse when containing numbers', () => { 26 | expect(parseJson('[1, 2,3]')).to.equal([1, 2, 3]) 27 | }) 28 | 29 | it('parse when containing strings', () => { 30 | expect(parseJson('["foo", "bar","baz"]')).to.equal([ 31 | 'foo', 32 | 'bar', 33 | 'baz', 34 | ]) 35 | }) 36 | }) 37 | 38 | describe('strings', () => { 39 | it('parse when empty', () => { 40 | expect(parseJson('""')).to.equal('') 41 | }) 42 | 43 | it('parse when not empty', () => { 44 | expect(parseJson('"foo"')).to.equal('foo') 45 | }) 46 | }) 47 | 48 | describe('numbers', () => { 49 | it('parse when negative', () => { 50 | expect(parseJson('-1')).to.equal(-1) 51 | expect(parseJson('-.1')).to.equal(-0.1) 52 | }) 53 | 54 | it('parse when positive', () => { 55 | expect(parseJson('1')).to.equal(1) 56 | expect(parseJson('3403')).to.equal(3403) 57 | }) 58 | 59 | it('parse when starting with a colon', () => { 60 | expect(parseJson('.1')).to.equal(0.1) 61 | expect(parseJson('.8304')).to.equal(0.8304) 62 | }) 63 | }) 64 | 65 | describe('booleans', () => { 66 | it('parse when true', () => { 67 | expect(parseJson('true')).to.equal(true) 68 | }) 69 | 70 | it('parse when false', () => { 71 | expect(parseJson('false')).to.equal(false) 72 | }) 73 | }) 74 | 75 | describe('null', () => { 76 | it('parses', () => { 77 | expect(parseJson('null')).to.equal(null) 78 | }) 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /packages/thrift-client-timing-filter/README.md: -------------------------------------------------------------------------------- 1 | # Thrift Client Timing Filter 2 | 3 | The timing filter is for emitting logs about the usage of a specific client. The filter will emit logs at a sepcified interval. The metric event emitted will tell you the longest request duration over the interval, the average duration of request over the interval and how many successes and errors the client recieved. 4 | 5 | ## Installation 6 | 7 | `ThriftClientTimingFilter` has a few `peerDependencies`. 8 | 9 | ```sh 10 | npm install --save @creditkarma/thrift-server-core 11 | npm install --save @creditkarma/thrift-client 12 | npm install --save @creditkarma/thrift-client-timing-filter 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```typescript 18 | import { 19 | createHttpClient, 20 | } from '@creditkarma/thrift-client' 21 | 22 | import { 23 | ThriftClientTimingFilter, 24 | ITimingEvent, 25 | } from '@creditkarma/thrift-client-context-filter' 26 | 27 | import { 28 | Calculator, 29 | } from './codegen/calculator' 30 | 31 | const thriftClient: Calculator.Client = 32 | createHttpClient(Calculator.Client, { 33 | hostName: 'localhost', 34 | port: 8080, 35 | register: [ 36 | ThriftClientTimingFilter({ 37 | remoteServiceName: 'calculator-service', 38 | interval: 5000, 39 | logger: (tags: Array, event: ITimingEvent) { 40 | console.log('client metrics: ', event) 41 | }, 42 | tags: [], 43 | }) 44 | ] 45 | }) 46 | 47 | thriftClient.add(5, 6, new RequestContext({ traceId: 3827293 })).then((response: number) => { 48 | // Do stuff... 49 | }) 50 | ``` 51 | 52 | ### Options 53 | 54 | Available options for `ThriftClientTimingFilter`: 55 | 56 | * remoteServiceName (required): Name of the remote service this client connects to. 57 | * interval (optional): How often to emit events in milliseconds. Defaults to 5000. 58 | * logger (optional): A function to log the events. Defaults to a logger using `console.log`. 59 | * tags (optional): An array of strings to attach as tags to the emitted event. Defaults to empty array. 60 | 61 | ## Contributing 62 | 63 | For more information about contributing new features and bug fixes, see our [Contribution Guidelines](../../CONTRIBUTING.md). 64 | External contributors must sign Contributor License Agreement (CLA) 65 | 66 | ## License 67 | 68 | This project is licensed under [Apache License Version 2.0](./LICENSE) 69 | -------------------------------------------------------------------------------- /packages/thrift-client-context-filter/README.md: -------------------------------------------------------------------------------- 1 | # Thrift Client Context Filter 2 | 3 | When using Thrift over HTTP we can use HTTP headers to pass context/metadata between services (tracing, auth). When using TCP we don't have this. Among the options to solve this is to prepend an object onto the head of our TCP payload. `@creditkarma/thrift-client` comes with two filters for helping with this situation. 4 | 5 | ## Installation 6 | 7 | `ThriftClientContextFilter` has a few `peerDependencies`. 8 | 9 | ```sh 10 | npm install --save @creditkarma/thrift-server-core 11 | npm install --save @creditkarma/thrift-client 12 | npm install --save @creditkarma/thrift-client-context-filter 13 | ``` 14 | 15 | ## Usage 16 | 17 | This plugin writes a Thrift struct onto the head of an outgoing payload and reads a struct off of the head of an incoming payload. 18 | 19 | ```typescript 20 | import { 21 | createTcpClient, 22 | } from '@creditkarma/thrift-client' 23 | 24 | import { 25 | ThriftClientContextFilter, 26 | } from '@creditkarma/thrift-client-context-filter' 27 | 28 | import { 29 | RequestContext, 30 | ResponseContext, 31 | } from './codegen/metadata' 32 | 33 | import { 34 | Calculator, 35 | } from './codegen/calculator' 36 | 37 | const thriftClient: Calculator.Client = 38 | createTcpClient(Calculator.Client, { 39 | hostName: 'localhost', 40 | port: 8080, 41 | register: [ ThriftClientContextFilter({ 42 | RequestContextClass: RequestContext, 43 | }) ] 44 | }) 45 | 46 | thriftClient.add(5, 6, new RequestContext({ traceId: 3827293 })).then((response: number) => { 47 | // Do stuff... 48 | }) 49 | ``` 50 | 51 | ### Options 52 | 53 | Available options for ThriftClientContextFilter: 54 | 55 | * RequestContextClass (required): A class (extending StructLike) that is to be prepended to outgoing requests. 56 | * ResponseContextClass (optional): A class (extending StructLike) that is prepended to incoming responses. Defaults to nothing. 57 | * transportType (optional): The type of transport to use. Currently only 'buffered'. 58 | * protocolType (optional): The type of protocol to use, either 'binary' or 'compact'. 59 | 60 | ## Contributing 61 | 62 | For more information about contributing new features and bug fixes, see our [Contribution Guidelines](../../CONTRIBUTING.md). 63 | External contributors must sign Contributor License Agreement (CLA) 64 | 65 | ## License 66 | 67 | This project is licensed under [Apache License Version 2.0](./LICENSE) 68 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/utils/readThriftMetadata.ts: -------------------------------------------------------------------------------- 1 | import { defaultLogger } from '../logger' 2 | 3 | import { 4 | IProtocolConstructor, 5 | IThriftMessage, 6 | ITransportConstructor, 7 | LogFunction, 8 | } from '../types' 9 | 10 | import { BinaryProtocol, TProtocol } from '../protocols' 11 | 12 | import { BufferedTransport, TTransport } from '../transports' 13 | 14 | export function readThriftMetadata( 15 | buffer: Buffer, 16 | Transport: ITransportConstructor, 17 | Protocol: IProtocolConstructor, 18 | logger: LogFunction, 19 | ): IThriftMessage { 20 | try { 21 | const transportWithData: TTransport = new Transport(buffer) 22 | const input: TProtocol = new Protocol(transportWithData) 23 | return input.readMessageBegin() 24 | } catch (err) { 25 | logger( 26 | ['warn', 'readThriftMetadata'], 27 | `Unable to read Thrift message. ${ 28 | err instanceof Error ? err.message : 'Unexpected error thrown' 29 | }`, 30 | ) 31 | throw err 32 | } 33 | } 34 | 35 | export function readThriftMethod( 36 | buffer: Buffer, 37 | Transport: ITransportConstructor = BufferedTransport, 38 | Protocol: IProtocolConstructor = BinaryProtocol, 39 | logger: LogFunction = defaultLogger, 40 | ): string { 41 | try { 42 | const { fieldName } = readThriftMetadata( 43 | buffer, 44 | Transport, 45 | Protocol, 46 | logger, 47 | ) 48 | return fieldName 49 | } catch (err) { 50 | logger( 51 | ['warn', 'readThrfitMethod'], 52 | `Unable to read Thrift method name. ${ 53 | err instanceof Error ? err.message : 'Unexpected error thrown' 54 | }`, 55 | ) 56 | return '' 57 | } 58 | } 59 | 60 | export function readRequestId( 61 | buffer: Buffer, 62 | Transport: ITransportConstructor, 63 | Protocol: IProtocolConstructor, 64 | logger: LogFunction = defaultLogger, 65 | ): number { 66 | try { 67 | const { requestId } = readThriftMetadata( 68 | buffer, 69 | Transport, 70 | Protocol, 71 | logger, 72 | ) 73 | return requestId 74 | } catch (err) { 75 | logger( 76 | ['warn', 'readRequestId'], 77 | `Unable to read Thrift requestId. ${ 78 | err instanceof Error ? err.message : 'Unexpected error thrown' 79 | }`, 80 | ) 81 | return 0 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /packages/thrift-client/src/main/types.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IRequestHeaders, 3 | LogFunction, 4 | ProtocolType, 5 | TransportType, 6 | } from '@creditkarma/thrift-server-core' 7 | 8 | import * as GenericPool from 'generic-pool' 9 | 10 | import got, { OptionsOfBufferResponseBody } from 'got' 11 | import * as tls from 'tls' 12 | 13 | export type RequestOptions = Partial 14 | 15 | export interface IRequestResponse { 16 | statusCode: number 17 | headers: IRequestHeaders 18 | body: Buffer 19 | } 20 | 21 | export interface IRequest { 22 | headers: IRequestHeaders 23 | } 24 | 25 | export interface IThriftRequest { 26 | data: Buffer 27 | methodName: string 28 | uri: string 29 | context: Context 30 | logger?: LogFunction 31 | } 32 | 33 | export type ClientOptionsFunction = () => IThriftRequest 34 | 35 | export interface IConnectionOptions { 36 | hostName: string 37 | port: number 38 | timeout?: number 39 | transport?: TransportType 40 | protocol?: ProtocolType 41 | tls?: tls.TlsOptions 42 | pool?: GenericPool.Options 43 | logger?: LogFunction 44 | } 45 | 46 | export interface ICreateTcpClientOptions extends IConnectionOptions { 47 | serviceName?: string 48 | register?: Array> 49 | } 50 | 51 | export interface IHttpConnectionOptions { 52 | hostName: string 53 | port: number 54 | path?: string 55 | https?: boolean 56 | transport?: TransportType 57 | protocol?: ProtocolType 58 | serviceName?: string 59 | context?: 60 | | IThriftRequest 61 | | ClientOptionsFunction 62 | requestOptions?: RequestOptions 63 | withEndpointPerMethod?: boolean 64 | headerBlacklist?: Array 65 | gotImpl?: typeof got 66 | } 67 | 68 | export interface ICreateHttpClientOptions extends IHttpConnectionOptions { 69 | register?: Array> 70 | } 71 | 72 | export type NextFunction = ( 73 | data?: Buffer, 74 | options?: Options, 75 | ) => Promise 76 | 77 | export type RequestHandler = ( 78 | request: IThriftRequest, 79 | next: NextFunction, 80 | ) => Promise 81 | 82 | export interface IThriftClientFilter { 83 | methods: Array 84 | handler: RequestHandler 85 | } 86 | 87 | export interface IThriftClientFilterConfig { 88 | methods?: Array 89 | handler: RequestHandler 90 | } 91 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/hapi-add-service.ts: -------------------------------------------------------------------------------- 1 | import { Int64 } from '@creditkarma/thrift-server-core' 2 | 3 | import { createThriftServer } from '@creditkarma/thrift-server-hapi' 4 | 5 | import { ZipkinTracingHapi } from '@creditkarma/zipkin-tracing-hapi' 6 | 7 | import * as Hapi from '@hapi/hapi' 8 | 9 | import { ADD_SERVER_CONFIG } from './config' 10 | 11 | import { AddService } from './generated/add-service' 12 | 13 | export async function createServer( 14 | sampleRate: number = 0, 15 | ): Promise { 16 | /** 17 | * Implementation of our thrift service. 18 | * 19 | * Notice the second parameter, "context" - this is the Hapi request object, 20 | * passed along to our service by the Hapi thrift plugin. Thus, you have access to 21 | * all HTTP request data from within your service implementation. 22 | */ 23 | const impl = new AddService.Processor({ 24 | ping(): void { 25 | return 26 | }, 27 | add(a: number, b: number, context?: Hapi.Request): number { 28 | return a + b 29 | }, 30 | addInt64(a: Int64, b: Int64, context?: Hapi.Request): Int64 { 31 | return new Int64(a.toNumber() + b.toNumber()) 32 | }, 33 | }) 34 | 35 | /** 36 | * Creates Hapi server with thrift endpoint. 37 | */ 38 | const server: Hapi.Server = await createThriftServer({ 39 | port: ADD_SERVER_CONFIG.port, 40 | path: ADD_SERVER_CONFIG.path, 41 | thriftOptions: { 42 | serviceName: 'add-service', 43 | handler: impl, 44 | }, 45 | }) 46 | 47 | if (sampleRate > 0) { 48 | await server.register({ 49 | plugin: ZipkinTracingHapi({ 50 | localServiceName: 'add-service', 51 | tracerConfig: { 52 | endpoint: process.env.ZIPKIN_ENDPOINT, 53 | zipkinVersion: 54 | process.env.ZIPKIN_VERSION === 'v2' ? 'v2' : 'v1', 55 | sampleRate, 56 | httpInterval: 0, 57 | }, 58 | }), 59 | }) 60 | } 61 | 62 | /** 63 | * The Hapi server can process requests that are not targeted to the thrift 64 | * service 65 | */ 66 | server.route({ 67 | method: 'GET', 68 | path: '/control', 69 | handler( 70 | request: Hapi.Request, 71 | reply: Hapi.ResponseToolkit, 72 | ): Hapi.ResponseObject { 73 | return reply.response('PASS') 74 | }, 75 | }) 76 | 77 | return server 78 | } 79 | -------------------------------------------------------------------------------- /packages/thrift-integration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@creditkarma/thrift-integration", 3 | "version": "1.0.4", 4 | "description": "Integration tests for thrift-server packages", 5 | "scripts": { 6 | "clean": "rimraf dist", 7 | "lint": "tslint --fix './src/**/*.ts'", 8 | "format": "prettier --write 'src/**/*.ts'", 9 | "codegen-ts": "thrift-typescript --target thrift-server --rootDir ./src --sourceDir ./thrift --outDir ./generated", 10 | "codegen-strict": "thrift-typescript --target thrift-server --strictUnions --rootDir ./src --sourceDir ./thrift --outDir ./generated-strict", 11 | "codegen-apache": "thrift-typescript --target apache --rootDir ./src --sourceDir ./thrift --outDir ./generated-apache", 12 | "codegen": "npm run codegen-ts && npm run codegen-strict && npm run codegen-apache", 13 | "prebuild": "npm run clean && npm run lint && npm run format && npm run codegen", 14 | "build": "tsc", 15 | "pretest": "npm run build", 16 | "test": "npm run test:only", 17 | "test:only": "DEBUG=true lab --timeout 60000 --verbose -l -S -P spec dist" 18 | }, 19 | "devDependencies": { 20 | "@creditkarma/dynamic-config": "^1.0.0", 21 | "@creditkarma/thrift-client": "^1.0.4", 22 | "@creditkarma/thrift-client-context-filter": "^1.0.4", 23 | "@creditkarma/thrift-client-timing-filter": "^1.0.4", 24 | "@creditkarma/thrift-client-ttwitter-filter": "^1.0.4", 25 | "@creditkarma/thrift-client-zipkin-filter": "^1.0.4", 26 | "@creditkarma/thrift-server-core": "^1.0.4", 27 | "@creditkarma/thrift-server-express": "^1.0.4", 28 | "@creditkarma/thrift-server-hapi": "^1.0.4", 29 | "@creditkarma/thrift-typescript": "^3.7.6", 30 | "@creditkarma/zipkin-core": "^1.0.4", 31 | "@creditkarma/zipkin-tracing-express": "^1.0.4", 32 | "@creditkarma/zipkin-tracing-hapi": "^1.0.4", 33 | "@hapi/code": "^8.0.7", 34 | "@hapi/hapi": "^20.0.0", 35 | "@hapi/lab": "^25.0.1", 36 | "@types/body-parser": "^1.16.5", 37 | "@types/express": "^4.16.0", 38 | "@types/hapi__code": "^8.0.1", 39 | "@types/hapi__hapi": "^20.0.0", 40 | "@types/hapi__lab": "^18.1.0", 41 | "@types/node": "^16.11.0", 42 | "@types/thrift": "^0.10.7", 43 | "body-parser": "^1.18.3", 44 | "express": "^4.17.1", 45 | "got": "^11.8.3", 46 | "prettier": "^1.18.2", 47 | "rimraf": "^2.6.2", 48 | "thrift": "^0.11.0", 49 | "tslint": "^5.11.0", 50 | "tslint-config-prettier": "^1.15.0", 51 | "tslint-plugin-prettier": "^2.0.0", 52 | "typescript": "4.6.x" 53 | }, 54 | "publishConfig": { 55 | "access": "private" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/errors/TApplicationException.ts: -------------------------------------------------------------------------------- 1 | import { TProtocol } from '../protocols' 2 | import { IStructCodec, IThriftField, TType } from '../types' 3 | 4 | export enum TApplicationExceptionType { 5 | UNKNOWN = 0, 6 | UNKNOWN_METHOD = 1, 7 | INVALID_MESSAGE_TYPE = 2, 8 | WRONG_METHOD_NAME = 3, 9 | BAD_SEQUENCE_ID = 4, 10 | MISSING_RESULT = 5, 11 | INTERNAL_ERROR = 6, 12 | PROTOCOL_ERROR = 7, 13 | INVALID_TRANSFORM = 8, 14 | INVALID_PROTOCOL = 9, 15 | UNSUPPORTED_CLIENT_TYPE = 10, 16 | } 17 | 18 | export class TApplicationException extends Error { 19 | public readonly name: string = 'TApplicationException' 20 | public type: TApplicationExceptionType 21 | public message: string 22 | 23 | constructor(type: TApplicationExceptionType, message: string) { 24 | super(message) 25 | this.type = type 26 | this.message = message 27 | } 28 | } 29 | 30 | export const TApplicationExceptionCodec: IStructCodec< 31 | TApplicationException, 32 | TApplicationException 33 | > = { 34 | encode(obj: TApplicationException, output: TProtocol): void { 35 | output.writeStructBegin('TApplicationException') 36 | 37 | if (obj.message) { 38 | output.writeFieldBegin('message', TType.STRING, 1) 39 | output.writeString(obj.message) 40 | output.writeFieldEnd() 41 | } 42 | 43 | if (obj.type) { 44 | output.writeFieldBegin('type', TType.I32, 2) 45 | output.writeI32(obj.type) 46 | output.writeFieldEnd() 47 | } 48 | 49 | output.writeFieldStop() 50 | output.writeStructEnd() 51 | }, 52 | decode(input: TProtocol): TApplicationException { 53 | input.readStructBegin() 54 | const args: any = {} 55 | 56 | while (true) { 57 | const ret: IThriftField = input.readFieldBegin() 58 | if (ret.fieldType === TType.STOP) { 59 | break 60 | } 61 | 62 | switch (ret.fieldId) { 63 | case 1: 64 | if (ret.fieldType === TType.STRING) { 65 | args.message = input.readString() 66 | } else { 67 | input.skip(ret.fieldType) 68 | } 69 | 70 | break 71 | 72 | case 2: 73 | if (ret.fieldType === TType.I32) { 74 | args.type = input.readI32() 75 | } else { 76 | input.skip(ret.fieldType) 77 | } 78 | break 79 | 80 | default: 81 | input.skip(ret.fieldType) 82 | break 83 | } 84 | 85 | input.readFieldEnd() 86 | } 87 | 88 | input.readStructEnd() 89 | 90 | return new TApplicationException(args.type, args.message) 91 | }, 92 | } 93 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/README.md: -------------------------------------------------------------------------------- 1 | # ThriftClientTTwitterFilter 2 | 3 | This plugin can be used in conjuction with Twitter's open source [Finagle](https://github.com/twitter/finagle) project to add and receive the headers that project expects. 4 | 5 | ## Installation 6 | 7 | `ThriftClientTTwitterFilter` has a few `peerDependencies`. 8 | 9 | ```sh 10 | npm install --save @creditkarma/thrift-server-core 11 | npm install --save @creditkarma/thrift-client 12 | npm install --save @creditkarma/thrift-client-context-filter 13 | npm install --save @creditkarma/thrift-client-ttwitter-filter 14 | ``` 15 | 16 | ## Usage 17 | 18 | ```typescript 19 | import { 20 | createTcpClient, 21 | } from '@creditkarma/thrift-client' 22 | 23 | import { 24 | ThriftClientTTwitterFilter, 25 | } from '@creditkarma/thrift-client-ttwitter-filter' 26 | 27 | import { 28 | Calculator, 29 | } from './codegen/calculator' 30 | 31 | const thriftClient: Calculator.Client = 32 | createTcpClient(Calculator.Client, { 33 | hostName: 'localhost', 34 | port: 8080, 35 | register: [ ThriftClientTTwitterFilter({ 36 | localServiceName: 'calculator-client', 37 | remoteServiceName: 'calculator-service', 38 | destHeader: 'calculator-service', 39 | endpoint: 'http://localhost:9411/api/v1/spans', 40 | sampleRate: 1, 41 | }) ] 42 | }) 43 | 44 | thriftClient.add(5, 6).then((response: number) => { 45 | // Do stuff... 46 | }) 47 | ``` 48 | 49 | *Note: The Twitter types are generated and exported under the name `TTwitter`* 50 | 51 | ## Options 52 | 53 | Available options for ThriftClientTTwitterFilter: 54 | 55 | * localServiceName (required): The name of your local service/application. 56 | * remoteServiceName (required): The name of the remote service you are calling. 57 | * destHeader (optional): A name for the destination added to the RequestHeader object Finagle expects. Defaults to the value of `remoteServiceName`. 58 | * isUpgraded (optional): Is the service using TTwitter context. Defaults to true. 59 | * clientId (optional): A unique identifier for the client. Defaults to undefined. 60 | * transportType (optional): The type of transport to use. Currently only 'buffered'. 61 | * protocolType (optional): The type of protocol to use. Currently only 'binary'. 62 | * tracerConfig.debug (optional): Zipkin debug mode. Defaults to false. 63 | * tracerConfig.endpoint (optional): Zipkin endpoint. Defaults to ''. 64 | * tracerConfig.sampleRate (optional): Zipkin samplerate. Defaults to 0.1. 65 | * tracerConfig.httpInterval (optional): Rate (in milliseconds) at which to send traces to Zipkin collector. Defaults to 1000. 66 | 67 | ## Contributing 68 | 69 | For more information about contributing new features and bug fixes, see our [Contribution Guidelines](../../CONTRIBUTING.md). 70 | External contributors must sign Contributor License Agreement (CLA) 71 | 72 | ## License 73 | 74 | This project is licensed under [Apache License Version 2.0](./LICENSE) 75 | -------------------------------------------------------------------------------- /packages/thrift-client-context-filter/src/main/ThriftClientContextFilter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | appendThriftObject, 3 | IStructCodec, 4 | LogFunction, 5 | ProtocolType, 6 | readThriftObject, 7 | TransportType, 8 | } from '@creditkarma/thrift-server-core' 9 | 10 | import { 11 | IRequestResponse, 12 | IThriftClientFilterConfig, 13 | IThriftRequest, 14 | NextFunction, 15 | } from '@creditkarma/thrift-client' 16 | 17 | import { defaultLogger } from './logger' 18 | 19 | export interface IThriftContextOptions { 20 | RequestCodec: IStructCodec 21 | ResponseCodec: IStructCodec 22 | transportType?: TransportType 23 | protocolType?: ProtocolType 24 | logger?: LogFunction 25 | } 26 | 27 | export function ThriftClientContextFilter({ 28 | RequestCodec, 29 | ResponseCodec, 30 | transportType = 'buffered', 31 | protocolType = 'binary', 32 | logger = defaultLogger, 33 | }: IThriftContextOptions< 34 | RequestContext, 35 | ResponseContext 36 | >): IThriftClientFilterConfig { 37 | return { 38 | handler( 39 | request: IThriftRequest, 40 | next: NextFunction, 41 | ): Promise { 42 | return appendThriftObject( 43 | request.context, 44 | request.data, 45 | RequestCodec, 46 | transportType, 47 | protocolType, 48 | ).then((extended: Buffer) => { 49 | return next(extended, request.context).then( 50 | (response: IRequestResponse): Promise => { 51 | return readThriftObject( 52 | response.body, 53 | ResponseCodec, 54 | transportType, 55 | protocolType, 56 | ).then( 57 | (result: [any, Buffer]): IRequestResponse => { 58 | return { 59 | statusCode: response.statusCode, 60 | headers: { 61 | thriftContext: result[0], 62 | }, 63 | body: result[1], 64 | } 65 | }, 66 | (err: any) => { 67 | logger( 68 | ['warn', 'ThriftClientContextFilter'], 69 | `Error reading context from Thrift response: ${err.message}`, 70 | ) 71 | return response 72 | }, 73 | ) 74 | }, 75 | ) 76 | }) 77 | }, 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | working_directory: ~/thrift-server 3 | docker: 4 | - image: circleci/node:16.11.1 5 | 6 | version: 2 7 | jobs: 8 | test_node_14: 9 | <<: *defaults 10 | docker: 11 | - image: circleci/node:14.16.0 12 | steps: 13 | - checkout 14 | - run: 15 | name: Install NPM Dependencies 16 | command: npm install 17 | - run: 18 | name: Run Test Suite 19 | command: npm test 20 | 21 | test_node_16: 22 | <<: *defaults 23 | docker: 24 | - image: circleci/node:16.11.1 25 | steps: 26 | - checkout 27 | - run: 28 | name: Install NPM Dependencies 29 | command: npm install 30 | - run: 31 | name: Run Test Suite 32 | command: npm test 33 | 34 | publish: 35 | <<: *defaults 36 | steps: 37 | - checkout 38 | - run: 39 | name: Create .npmrc 40 | command: 'echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc' 41 | - run: 42 | name: Install NPM Dependencies 43 | command: npm install 44 | - run: 45 | name: Build Publish Assets 46 | command: npm run build 47 | - run: 48 | name: Publish to NPM 49 | command: npm run release:publish 50 | 51 | # With npm8's stricter peerDependencies, a number of steps need to be made 52 | # on prerelease, the peerDependencies need to be updated to point to exact versions 53 | # Move the tag to the new commit 54 | # --legacy-peer-deps is needed here to use them before they are published 55 | publish_next: 56 | <<: *defaults 57 | steps: 58 | - checkout 59 | - run: 60 | name: Generate .npmrc File 61 | command: 'echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc' 62 | - run: 63 | name: Install NPM Dependencies 64 | command: npm install --legacy-peer-deps 65 | - run: 66 | name: Build Publish Assets 67 | command: npm run build 68 | - run: 69 | name: Publish to NPM 70 | command: npm run release:publish-next 71 | 72 | workflows: 73 | version: 2 74 | build_test: 75 | jobs: 76 | - test_node_16: 77 | filters: 78 | tags: 79 | only: /.*/ 80 | 81 | - test_node_14: 82 | filters: 83 | tags: 84 | only: /.*/ 85 | 86 | - publish: 87 | requires: 88 | - test_node_16 89 | - test_node_14 90 | filters: 91 | tags: 92 | only: /^v\d+\.\d+\.\d+$/ 93 | branches: 94 | ignore: /.*/ 95 | 96 | - publish_next: 97 | requires: 98 | - test_node_16 99 | - test_node_14 100 | filters: 101 | tags: 102 | only: /^v\d+\.\d+\.\d+-alpha\.\d+$/ 103 | branches: 104 | ignore: /.*/ 105 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/src/ttwitter/com/creditkarma/finagle/thrift/ClientId.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /* 4 | * Autogenerated by @creditkarma/thrift-typescript v3.7.6 5 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 6 | */ 7 | import * as thrift from "@creditkarma/thrift-server-core"; 8 | export interface IClientId { 9 | name?: string; 10 | } 11 | export interface IClientIdArgs { 12 | name?: string; 13 | } 14 | export const ClientIdCodec: thrift.IStructCodec = { 15 | encode(args: IClientIdArgs, output: thrift.TProtocol): void { 16 | const obj: any = { 17 | name: args.name 18 | }; 19 | output.writeStructBegin("ClientId"); 20 | if (obj.name != null) { 21 | output.writeFieldBegin("name", thrift.TType.STRING, 1); 22 | output.writeString(obj.name); 23 | output.writeFieldEnd(); 24 | } 25 | output.writeFieldStop(); 26 | output.writeStructEnd(); 27 | return; 28 | }, 29 | decode(input: thrift.TProtocol): IClientId { 30 | let _args: any = {}; 31 | input.readStructBegin(); 32 | while (true) { 33 | const ret: thrift.IThriftField = input.readFieldBegin(); 34 | const fieldType: thrift.TType = ret.fieldType; 35 | const fieldId: number = ret.fieldId; 36 | if (fieldType === thrift.TType.STOP) { 37 | break; 38 | } 39 | switch (fieldId) { 40 | case 1: 41 | if (fieldType === thrift.TType.STRING) { 42 | const value_1: string = input.readString(); 43 | _args.name = value_1; 44 | } 45 | else { 46 | input.skip(fieldType); 47 | } 48 | break; 49 | default: { 50 | input.skip(fieldType); 51 | } 52 | } 53 | input.readFieldEnd(); 54 | } 55 | input.readStructEnd(); 56 | return { 57 | name: _args.name 58 | }; 59 | } 60 | }; 61 | export class ClientId extends thrift.StructLike implements IClientId { 62 | public name?: string; 63 | public readonly _annotations: thrift.IThriftAnnotations = {}; 64 | public readonly _fieldAnnotations: thrift.IFieldAnnotations = {}; 65 | constructor(args: IClientIdArgs = {}) { 66 | super(); 67 | if (args.name != null) { 68 | const value_2: string = args.name; 69 | this.name = value_2; 70 | } 71 | } 72 | public static read(input: thrift.TProtocol): ClientId { 73 | return new ClientId(ClientIdCodec.decode(input)); 74 | } 75 | public static write(args: IClientIdArgs, output: thrift.TProtocol): void { 76 | return ClientIdCodec.encode(args, output); 77 | } 78 | public write(output: thrift.TProtocol): void { 79 | return ClientIdCodec.encode(this, output); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/binary.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This functions were originally taken from the Apache project and reimplemented in TypeScript. 3 | * They've been re-implemented using the Node.js Buffer methods to read and write numeric values. 4 | * 5 | * The original project can be found here: 6 | * https://github.com/apache/thrift/blob/master/lib/nodejs/lib/thrift/binary.js 7 | */ 8 | 9 | /** 10 | * Read a byte as a signed value. 11 | * 12 | * @param byte IInt8 value 13 | */ 14 | export function readByte(byte: number): number { 15 | return byte > 127 ? byte - 256 : byte 16 | } 17 | 18 | /** 19 | * Read a 16-bit integer from the buffer at the given offset. 20 | * 21 | * Big-endian order. 22 | * 23 | * @param bytes Buffer 24 | * @param offset Offset 25 | */ 26 | export function readI16(bytes: Buffer, offset: number = 0): number { 27 | return bytes.readInt16BE(offset) 28 | } 29 | 30 | /** 31 | * Read a 32-bit integer from the buffer at the given offset. 32 | * 33 | * Big-endian order. 34 | * 35 | * @param bytes Buffer 36 | * @param offset Offset 37 | */ 38 | export function readI32(bytes: Buffer, offset: number = 0): number { 39 | return bytes.readInt32BE(offset) 40 | } 41 | 42 | /** 43 | * Read a double from the buffer at the given offset. 44 | * 45 | * Big-endian order. 46 | * 47 | * @param bytes Buffer 48 | * @param offset Offset 49 | */ 50 | export function readDouble(bytes: Buffer, offset: number = 0): number { 51 | return bytes.readDoubleBE(offset) 52 | } 53 | 54 | /** 55 | * Write a 16-bit integer to the buffer at the given offset. 56 | * 57 | * Big-endian order. 58 | * 59 | * @param bytes Buffer 60 | * @param i16 16-bit integer 61 | */ 62 | export function writeI16(bytes: Buffer, i16: number): Buffer { 63 | bytes.writeInt16BE(i16, 0) 64 | return bytes 65 | } 66 | 67 | /** 68 | * Write a 32-bit integer to the buffer at the given offset. 69 | * 70 | * Big-endian order. 71 | * 72 | * @param bytes Buffer 73 | * @param i32 32-bit integer 74 | */ 75 | export function writeI32(bytes: Buffer, i32: number): Buffer { 76 | bytes.writeInt32BE(i32, 0) 77 | return bytes 78 | } 79 | 80 | /** 81 | * Write a double to the buffer at the given offset. 82 | * 83 | * Big-endian order. 84 | * 85 | * @param bytes Buffer 86 | * @param dub Double 87 | */ 88 | export function writeDouble(bytes: Buffer, dub: number): Buffer { 89 | bytes.writeDoubleBE(dub, 0) 90 | return bytes 91 | } 92 | 93 | /** 94 | * Read a double from the buffer at the given offset. 95 | * 96 | * Little-endian order. 97 | * 98 | * @param bytes Buffer 99 | * @param offset Offset 100 | */ 101 | export function readDoubleLE(bytes: Buffer, offset: number = 0): number { 102 | return bytes.readDoubleLE(offset) 103 | } 104 | 105 | /** 106 | * Write a double to the buffer at the given offset. 107 | * 108 | * Little-endian order. 109 | * 110 | * @param bytes Buffer 111 | * @param dub Double 112 | */ 113 | export function writeDoubleLE(bytes: Buffer, dub: number): Buffer { 114 | bytes.writeDoubleLE(dub, 0) 115 | return bytes 116 | } 117 | -------------------------------------------------------------------------------- /packages/zipkin-tracing-express/src/main/ZipkinTracingExpress.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getProtocol, 3 | getTransport, 4 | IRequestHeaders, 5 | readThriftMethod, 6 | } from '@creditkarma/thrift-server-core' 7 | 8 | import { 9 | getTracerForService, 10 | headersForTraceId, 11 | IZipkinOptions, 12 | normalizeHeaders, 13 | } from '@creditkarma/zipkin-core' 14 | 15 | import { Instrumentation, option, TraceId, Tracer } from 'zipkin' 16 | 17 | import * as express from 'express' 18 | import * as url from 'url' 19 | 20 | function formatRequestUrl(req: express.Request): string { 21 | const parsed = url.parse(req.originalUrl) 22 | return url.format({ 23 | protocol: req.protocol, 24 | host: req.get('host'), 25 | pathname: parsed.pathname || '/', 26 | search: parsed.search, 27 | }) 28 | } 29 | 30 | export function ZipkinTracingExpress({ 31 | localServiceName, 32 | port = 0, 33 | isThrift = true, 34 | transport = 'buffered', 35 | protocol = 'binary', 36 | tracerConfig = {}, 37 | }: IZipkinOptions): express.RequestHandler { 38 | const tracer: Tracer = getTracerForService(localServiceName, tracerConfig) 39 | const instrumentation = new Instrumentation.HttpServer({ tracer, port }) 40 | 41 | return ( 42 | request: express.Request, 43 | response: express.Response, 44 | next: express.NextFunction, 45 | ): void => { 46 | tracer.scoped(() => { 47 | const requestMethod: string = 48 | isThrift === true 49 | ? readThriftMethod( 50 | request.body, 51 | getTransport(transport), 52 | getProtocol(protocol), 53 | ) 54 | : request.method 55 | 56 | const normalHeaders: IRequestHeaders = normalizeHeaders( 57 | request.headers, 58 | ) 59 | 60 | function readHeader(header: string): option.IOption { 61 | const val = normalHeaders[header.toLocaleLowerCase()] 62 | if (val !== null && val !== undefined) { 63 | return new option.Some(val) 64 | } else { 65 | return option.None 66 | } 67 | } 68 | 69 | const traceId: TraceId = instrumentation.recordRequest( 70 | requestMethod, 71 | formatRequestUrl(request), 72 | readHeader, 73 | ) 74 | 75 | const traceHeaders: IRequestHeaders = headersForTraceId(traceId) 76 | 77 | // Update headers on request object 78 | for (const key in traceHeaders) { 79 | if (traceHeaders.hasOwnProperty(key)) { 80 | request.headers[key] = traceHeaders[key] 81 | } 82 | } 83 | 84 | response.on('finish', () => { 85 | tracer.scoped(() => { 86 | instrumentation.recordResponse( 87 | traceId as any, 88 | `${response.statusCode}`, 89 | ) 90 | }) 91 | }) 92 | 93 | next() 94 | }) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/src/calculator-service.ts: -------------------------------------------------------------------------------- 1 | import { config } from '@creditkarma/dynamic-config' 2 | import { createThriftServer } from '@creditkarma/thrift-server-hapi' 3 | 4 | import { ZipkinTracingHapi } from '@creditkarma/zipkin-tracing-hapi' 5 | 6 | import { createHttpClient, IRequest } from '@creditkarma/thrift-client' 7 | 8 | import { ThriftClientZipkinFilter } from '@creditkarma/thrift-client-zipkin-filter' 9 | 10 | import * as Hapi from '@hapi/hapi' 11 | 12 | import { AddService } from './generated/add-service' 13 | 14 | import { Calculator, IWork, Operation } from './generated/calculator-service' 15 | 16 | async function init(): Promise { 17 | const SERVER_CONFIG = await config().get('calculator-service') 18 | const ADD_SERVER_CONFIG = await config().get('add-service') 19 | 20 | // Create thrift client 21 | const thriftClient: AddService.Client = createHttpClient( 22 | AddService.Client, 23 | { 24 | hostName: ADD_SERVER_CONFIG.host, 25 | port: ADD_SERVER_CONFIG.port, 26 | register: [ 27 | ThriftClientZipkinFilter({ 28 | localServiceName: 'calculator-service', 29 | remoteServiceName: 'add-service', 30 | tracerConfig: { 31 | endpoint: 'http://localhost:9411/api/v1/spans', 32 | httpInterval: 1000, 33 | httpTimeout: 5000, 34 | sampleRate: 1.0, 35 | }, 36 | }), 37 | ], 38 | }, 39 | ) 40 | 41 | const impl = new Calculator.Processor({ 42 | ping(): void { 43 | return 44 | }, 45 | add(a: number, b: number): Promise { 46 | return thriftClient.add(a, b) 47 | }, 48 | calculate( 49 | logId: number, 50 | work: IWork, 51 | context: Hapi.Request, 52 | ): Promise | number { 53 | switch (work.op) { 54 | case Operation.ADD: 55 | return thriftClient.add(work.num1, work.num2, { 56 | headers: context.headers, 57 | }) 58 | case Operation.SUBTRACT: 59 | return work.num1 - work.num2 60 | case Operation.DIVIDE: 61 | return work.num1 / work.num2 62 | case Operation.MULTIPLY: 63 | return work.num1 * work.num2 64 | default: 65 | throw new Error(`Invalid operation[${work.op}]`) 66 | } 67 | }, 68 | }) 69 | 70 | const server: Hapi.Server = await createThriftServer({ 71 | port: SERVER_CONFIG.port, 72 | path: SERVER_CONFIG.path, 73 | thriftOptions: { 74 | serviceName: 'calculator-service', 75 | handler: impl, 76 | }, 77 | }) 78 | 79 | await server.register({ 80 | plugin: ZipkinTracingHapi({ 81 | localServiceName: 'calculator-service', 82 | tracerConfig: { 83 | endpoint: 'http://localhost:9411/api/v1/spans', 84 | httpInterval: 1000, 85 | httpTimeout: 5000, 86 | sampleRate: 1.0, 87 | }, 88 | }), 89 | }) 90 | 91 | await server.start() 92 | } 93 | 94 | init() 95 | -------------------------------------------------------------------------------- /packages/zipkin-core/src/tests/unit/zipkin.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from '@hapi/code' 2 | import * as Lab from '@hapi/lab' 3 | 4 | import { TraceId } from 'zipkin' 5 | 6 | import { 7 | addL5Dheaders, 8 | deserializeLinkerdHeader, 9 | serializeLinkerdHeader, 10 | traceIdFromTraceId, 11 | traceIdValues, 12 | } from '../../main' 13 | 14 | export const lab = Lab.script() 15 | 16 | const describe = lab.describe 17 | const it = lab.it 18 | 19 | describe('Zipkin', () => { 20 | const traceId: TraceId = traceIdFromTraceId({ 21 | traceId: '411d1802c9151ded', 22 | spanId: 'c3ba1a6560ca0c48', 23 | parentId: '2b5189ffa013ad73', 24 | sampled: true, 25 | traceIdHigh: false, 26 | }) 27 | 28 | const traceId128: TraceId = traceIdFromTraceId({ 29 | traceId: '411d1802c9151ded2b5189ffa013ad73', 30 | spanId: 'c3ba1a6560ca0c48', 31 | parentId: '2b5189ffa013ad73', 32 | sampled: true, 33 | traceIdHigh: true, 34 | }) 35 | 36 | const serializedTraceId: string = 37 | 'w7oaZWDKDEgrUYn/oBOtc0EdGALJFR3tAAAAAAAAAAY=' 38 | 39 | const serializedTraceId128: string = 40 | 'w7oaZWDKDEgrUYn/oBOtc0EdGALJFR3tAAAAAAAAAAYrUYn/oBOtcw==' 41 | 42 | describe('deserializeLikerdHeader', () => { 43 | it('should correctly deserialize TraceId object with 64-bit ids', async () => { 44 | expect(traceId).to.equal( 45 | deserializeLinkerdHeader(serializedTraceId), 46 | ) 47 | }) 48 | 49 | it('should correctly deserialize TraceId object with 128-bit ids', async () => { 50 | expect(traceId128).to.equal( 51 | deserializeLinkerdHeader(serializedTraceId128), 52 | ) 53 | }) 54 | }) 55 | 56 | describe('serializeLinkerdHeader', () => { 57 | it('should correctly serialize TraceId object with 64-bit ids', async () => { 58 | expect(serializeLinkerdHeader(traceId)).to.equal(serializedTraceId) 59 | }) 60 | 61 | it('should correctly serialize TraceId object with 128-bit ids', async () => { 62 | expect(serializeLinkerdHeader(traceId128)).to.equal( 63 | serializedTraceId128, 64 | ) 65 | }) 66 | }) 67 | 68 | describe('addL5Dheaders', () => { 69 | it('should add l5d header to headers', async () => { 70 | const actual = addL5Dheaders({ 71 | 'x-b3-traceId': '411d1802c9151ded', 72 | 'x-b3-spanId': 'c3ba1a6560ca0c48', 73 | 'x-b3-parentspanid': '2b5189ffa013ad73', 74 | 'x-b3-sampled': '1', 75 | }) 76 | 77 | expect(actual).to.equal({ 78 | 'x-b3-traceid': '411d1802c9151ded', 79 | 'x-b3-spanid': 'c3ba1a6560ca0c48', 80 | 'x-b3-parentspanid': '2b5189ffa013ad73', 81 | 'x-b3-sampled': '1', 82 | 'l5d-ctx-trace': 'w7oaZWDKDEgrUYn/oBOtc0EdGALJFR3tAAAAAAAAAAY=', 83 | }) 84 | 85 | expect( 86 | traceIdValues( 87 | deserializeLinkerdHeader(actual['l5d-ctx-trace'] as string), 88 | ), 89 | ).to.equal({ 90 | traceId: '411d1802c9151ded', 91 | spanId: 'c3ba1a6560ca0c48', 92 | parentId: '2b5189ffa013ad73', 93 | sampled: true, 94 | traceIdHigh: false, 95 | }) 96 | }) 97 | }) 98 | }) 99 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team. All complaints will be reviewed and 59 | investigated and will result in a response that is deemed necessary and 60 | appropriate to the circumstances. The project team is obligated to maintain 61 | confidentiality with regard to the reporter of an incident. Further details of 62 | specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /packages/thrift-server-core/src/main/protocols/TProtocol.ts: -------------------------------------------------------------------------------- 1 | import { TTransport } from '../transports' 2 | 3 | import { defaultLogger } from '../logger' 4 | import { 5 | IInt64, 6 | Int64, 7 | IThriftField, 8 | IThriftList, 9 | IThriftMap, 10 | IThriftMessage, 11 | IThriftSet, 12 | IThriftStruct, 13 | LogFunction, 14 | MessageType, 15 | TType, 16 | } from '../types' 17 | 18 | export abstract class TProtocol { 19 | protected transport: TTransport 20 | protected logger: LogFunction 21 | protected requestId: number | null 22 | 23 | constructor(trans: TTransport, logger: LogFunction = defaultLogger) { 24 | this.transport = trans 25 | this.logger = logger 26 | this.requestId = null 27 | } 28 | 29 | public getTransport(): TTransport { 30 | return this.transport 31 | } 32 | 33 | public flush(): Buffer { 34 | return this.transport.flush() 35 | } 36 | 37 | public abstract writeMessageBegin( 38 | name: string, 39 | type: MessageType, 40 | seqid: number, 41 | ): void 42 | 43 | public abstract writeMessageEnd(): void 44 | 45 | public abstract writeStructBegin(name: string): void 46 | 47 | public abstract writeStructEnd(): void 48 | 49 | public abstract writeFieldBegin(name: string, type: TType, id: number): void 50 | 51 | public abstract writeFieldEnd(): void 52 | 53 | public abstract writeFieldStop(): void 54 | 55 | public abstract writeMapBegin( 56 | keyType: TType, 57 | valueType: TType, 58 | size: number, 59 | ): void 60 | 61 | public abstract writeMapEnd(): void 62 | 63 | public abstract writeListBegin(elementType: TType, size: number): void 64 | 65 | public abstract writeListEnd(): void 66 | 67 | public abstract writeSetBegin(elementType: TType, size: number): void 68 | 69 | public abstract writeSetEnd(): void 70 | 71 | public abstract writeBool(bool: boolean): void 72 | 73 | public abstract writeByte(b: number): void 74 | 75 | public abstract writeI16(i16: number): void 76 | 77 | public abstract writeI32(i32: number): void 78 | 79 | public abstract writeI64(i64: number | string | IInt64): void 80 | 81 | public abstract writeDouble(dbl: number): void 82 | 83 | public abstract writeString(arg: string): void 84 | 85 | public abstract writeBinary(arg: string | Buffer): void 86 | 87 | public abstract readMessageBegin(): IThriftMessage 88 | 89 | public abstract readMessageEnd(): void 90 | 91 | public abstract readStructBegin(): IThriftStruct 92 | 93 | public abstract readStructEnd(): void 94 | 95 | public abstract readFieldBegin(): IThriftField 96 | 97 | public abstract readFieldEnd(): void 98 | 99 | public abstract readMapBegin(): IThriftMap 100 | 101 | public abstract readMapEnd(): void 102 | 103 | public abstract readListBegin(): IThriftList 104 | 105 | public abstract readListEnd(): void 106 | 107 | public abstract readSetBegin(): IThriftSet 108 | 109 | public abstract readSetEnd(): void 110 | 111 | public abstract readBool(): boolean 112 | 113 | public abstract readByte(): number 114 | 115 | public abstract readI16(): number 116 | 117 | public abstract readI32(): number 118 | 119 | public abstract readI64(): Int64 120 | 121 | public abstract readDouble(): number 122 | 123 | public abstract readBinary(): Buffer 124 | 125 | public abstract readString(): string 126 | 127 | public abstract skip(type: TType): void 128 | } 129 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/client/tracing/mock-collector.ts: -------------------------------------------------------------------------------- 1 | import * as bodyParser from 'body-parser' 2 | import * as express from 'express' 3 | import * as net from 'net' 4 | 5 | import { COLLECTOR_CONFIG } from './config' 6 | 7 | // http://localhost:9411/api/v1/spans 8 | 9 | export function serviceName(span: any): string | undefined { 10 | if (span.annotations && span.annotations.length) { 11 | if (span.annotations[0].endpoint) { 12 | return span.annotations[0].endpoint.serviceName 13 | } 14 | } 15 | } 16 | 17 | export interface IMockCollector { 18 | server: net.Server 19 | reset: () => void 20 | traces: () => any 21 | close: () => Promise 22 | } 23 | 24 | export function createServer(): Promise { 25 | // Get express instance 26 | const app = express() 27 | 28 | app.use(bodyParser.json()) 29 | 30 | let traces: any = {} 31 | 32 | function recordTraces(body: Array): void { 33 | body.forEach((next: any) => { 34 | const traceId = next.traceId 35 | const id = next.id 36 | if (traces[traceId] === undefined) { 37 | traces[traceId] = {} 38 | } 39 | 40 | // traces[traceId][id] = next 41 | traces[traceId][id] = { 42 | traceId: next.traceId, 43 | id: next.id, 44 | parentId: next.parentId, 45 | duration: next.duration, 46 | serviceName: serviceName(next), 47 | } 48 | }) 49 | } 50 | 51 | app.post( 52 | '/api/v1/spans', 53 | (req: express.Request, res: express.Response): void => { 54 | if (req.body && req.body.length) { 55 | recordTraces(req.body) 56 | } 57 | 58 | res.sendStatus(202) 59 | }, 60 | ) 61 | 62 | app.post( 63 | '/api/v2/spans', 64 | (req: express.Request, res: express.Response): void => { 65 | if (req.body && req.body.length) { 66 | recordTraces(req.body) 67 | } 68 | 69 | res.sendStatus(202) 70 | }, 71 | ) 72 | 73 | return new Promise((resolve, reject) => { 74 | const server: net.Server = app.listen( 75 | COLLECTOR_CONFIG.port, 76 | (err?: any) => { 77 | if (err) { 78 | console.error( 79 | `MockCollection unable to start: ${err.message}`, 80 | ) 81 | reject(err) 82 | } else { 83 | console.log( 84 | `MockCollector listening on port[${COLLECTOR_CONFIG.port}]`, 85 | ) 86 | resolve({ 87 | server, 88 | reset() { 89 | traces = {} 90 | }, 91 | traces(): any { 92 | const tracesToReturn = traces 93 | traces = {} 94 | return tracesToReturn 95 | }, 96 | close(): Promise { 97 | return new Promise((res, rej) => { 98 | server.close(() => { 99 | console.log('MockCollector closed') 100 | server.unref() 101 | res() 102 | }) 103 | }) 104 | }, 105 | }) 106 | } 107 | }, 108 | ) 109 | }) 110 | } 111 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/hapi/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHttpClient, RequestOptions } from '@creditkarma/thrift-client' 2 | 3 | import { Int64, TApplicationException } from '@creditkarma/thrift-server-core' 4 | 5 | import { expect } from '@hapi/code' 6 | import * as Hapi from '@hapi/hapi' 7 | import * as Lab from '@hapi/lab' 8 | import got from 'got' 9 | 10 | import { HAPI_CALC_SERVER_CONFIG } from '../config' 11 | 12 | import { Calculator } from '../generated/calculator-service' 13 | 14 | import { ISharedStruct } from '../generated/shared' 15 | 16 | import { createServer as createCalcServer } from '../hapi-calculator-service' 17 | 18 | import { createServer as createAddServer } from '../hapi-add-service' 19 | 20 | export const lab = Lab.script() 21 | 22 | const describe = lab.describe 23 | const it = lab.it 24 | const before = lab.before 25 | const after = lab.after 26 | 27 | describe('Thrift Server Hapi', () => { 28 | let calcServer: Hapi.Server 29 | let addServer: Hapi.Server 30 | let client: Calculator.Client 31 | 32 | before(async () => { 33 | calcServer = await createCalcServer() 34 | addServer = await createAddServer() 35 | client = createHttpClient(Calculator.Client, HAPI_CALC_SERVER_CONFIG) 36 | 37 | return Promise.all([calcServer.start(), addServer.start()]).then(() => { 38 | console.log('Thrift server started') 39 | }) 40 | }) 41 | 42 | after(async () => { 43 | return Promise.all([calcServer.stop(), addServer.stop()]).then(() => { 44 | console.log('Thrift server stopped') 45 | }) 46 | }) 47 | 48 | it('should corrently handle a service client request', async () => { 49 | return client.add(5, 7).then((response: number) => { 50 | expect(response).to.equal(12) 51 | }) 52 | }) 53 | 54 | it('should corrently handle a service client request for a struct', async () => { 55 | return client.getStruct(1).then((response: ISharedStruct) => { 56 | const expected = { 57 | code: { 58 | status: new Int64(0), 59 | }, 60 | value: 'test', 61 | } 62 | expect(response).to.equal(expected) 63 | }) 64 | }) 65 | 66 | it('should correctly handle a service request with context', async () => { 67 | return client 68 | .addWithContext(5, 7, { headers: { 'x-fake-token': 'fake-token' } }) 69 | .then((response: number) => { 70 | expect(response).to.equal(12) 71 | }) 72 | }) 73 | 74 | it('should reject service call with incorrect context', async () => { 75 | return client 76 | .addWithContext(5, 7, { headers: { 'x-fake-token': 'wrong' } }) 77 | .then( 78 | (response: number) => { 79 | throw new Error('Should reject with incorrect context') 80 | }, 81 | (err: any) => { 82 | expect(err.message).to.equal('Unauthorized') 83 | }, 84 | ) 85 | }) 86 | 87 | it('should reject for service call that throws', async () => { 88 | return client.broken().then( 89 | (response: void) => { 90 | throw new Error('Should reject') 91 | }, 92 | (err: any) => { 93 | expect(err).to.be.instanceOf(TApplicationException) 94 | }, 95 | ) 96 | }) 97 | 98 | it('should handle requests not pointed to thrift service', async () => { 99 | return got( 100 | `http://${HAPI_CALC_SERVER_CONFIG.hostName}:${HAPI_CALC_SERVER_CONFIG.port}/control`, 101 | ).then((val) => { 102 | expect(val.body).to.equal('PASS') 103 | }) 104 | }) 105 | }) 106 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/express/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from '@hapi/code' 2 | import * as Hapi from '@hapi/hapi' 3 | import * as Lab from '@hapi/lab' 4 | import * as express from 'express' 5 | import got from 'got' 6 | import * as net from 'net' 7 | 8 | import { createHttpClient, RequestOptions } from '@creditkarma/thrift-client' 9 | 10 | import { Int64 } from '@creditkarma/thrift-server-core' 11 | 12 | import { EXPRESS_CALC_SERVER_CONFIG } from '../config' 13 | 14 | import { Calculator } from '../generated/calculator-service' 15 | 16 | import { ISharedStruct } from '../generated/shared' 17 | 18 | import { createServer as createCalcServer } from '../express-calculator-service' 19 | 20 | import { createServer as createAddServer } from '../hapi-add-service' 21 | 22 | export const lab = Lab.script() 23 | 24 | const describe = lab.describe 25 | const it = lab.it 26 | const before = lab.before 27 | const after = lab.after 28 | 29 | describe('Thrift Server Express', () => { 30 | let calcServer: net.Server 31 | let addServer: Hapi.Server 32 | let client: Calculator.Client 33 | 34 | before(async () => { 35 | addServer = await createAddServer() 36 | 37 | return new Promise((resolve, reject) => { 38 | const app: express.Application = createCalcServer() 39 | client = createHttpClient( 40 | Calculator.Client, 41 | EXPRESS_CALC_SERVER_CONFIG, 42 | ) 43 | 44 | calcServer = app.listen(EXPRESS_CALC_SERVER_CONFIG.port, () => { 45 | console.log( 46 | `Express server listening on port: ${EXPRESS_CALC_SERVER_CONFIG.port}`, 47 | ) 48 | addServer.start().then(() => resolve()) 49 | }) 50 | }) 51 | }) 52 | 53 | after(async () => { 54 | return new Promise((resolve, reject) => { 55 | calcServer.close(() => { 56 | addServer.stop().then(() => resolve()) 57 | }) 58 | }) 59 | }) 60 | 61 | it('should corrently handle a service client request', async () => { 62 | return client.add(5, 7).then((response: number) => { 63 | expect(response).to.equal(12) 64 | }) 65 | }) 66 | 67 | it('should corrently handle a service client request for a struct', async () => { 68 | return client.getStruct(1).then((response: ISharedStruct) => { 69 | const expected = { 70 | code: { 71 | status: new Int64(0), 72 | }, 73 | value: 'test', 74 | } 75 | expect(response).to.equal(expected) 76 | }) 77 | }) 78 | 79 | it('should correctly handle a service request with context', async () => { 80 | return client 81 | .addWithContext(5, 7, { headers: { 'x-fake-token': 'fake-token' } }) 82 | .then((response: number) => { 83 | expect(response).to.equal(12) 84 | }) 85 | }) 86 | 87 | it('should reject service call with incorrect context', async () => { 88 | return client 89 | .addWithContext(5, 7, { headers: { 'x-fake-token': 'wrong' } }) 90 | .then( 91 | (response: number) => { 92 | throw new Error('Should reject for incorrect context') 93 | }, 94 | (err: any) => { 95 | expect(err.message).to.equal('Unauthorized') 96 | }, 97 | ) 98 | }) 99 | 100 | it('should handle requests not pointed to thrift service', async () => { 101 | return got( 102 | `http://${EXPRESS_CALC_SERVER_CONFIG.hostName}:${EXPRESS_CALC_SERVER_CONFIG.port}/control`, 103 | ).then((val) => { 104 | expect(val.body).to.equal('PASS') 105 | }) 106 | }) 107 | }) 108 | -------------------------------------------------------------------------------- /packages/thrift-server-ecosystem/src/client.ts: -------------------------------------------------------------------------------- 1 | import { config } from '@creditkarma/dynamic-config' 2 | import { ZipkinTracingExpress } from '@creditkarma/zipkin-tracing-express' 3 | 4 | import { createHttpClient, IRequest } from '@creditkarma/thrift-client' 5 | 6 | import { ThriftClientZipkinFilter } from '@creditkarma/thrift-client-zipkin-filter' 7 | 8 | import * as express from 'express' 9 | import * as path from 'path' 10 | 11 | import { Calculator, Operation, Work } from './generated/calculator-service' 12 | 13 | async function init(): Promise { 14 | const SERVER_CONFIG = await config().get('calculator-service') 15 | const CLIENT_CONFIG = await config().get('client') 16 | 17 | // Get express instance 18 | const app = express() 19 | 20 | app.use( 21 | ZipkinTracingExpress({ 22 | localServiceName: 'calculator-client', 23 | tracerConfig: { 24 | endpoint: 'http://localhost:9411/api/v1/spans', 25 | httpInterval: 1000, 26 | httpTimeout: 5000, 27 | sampleRate: 1.0, 28 | }, 29 | }), 30 | ) 31 | 32 | // Create thrift client 33 | const thriftClient: Calculator.Client = createHttpClient( 34 | Calculator.Client, 35 | { 36 | hostName: SERVER_CONFIG.host, 37 | port: SERVER_CONFIG.port, 38 | register: [ 39 | ThriftClientZipkinFilter({ 40 | localServiceName: 'calculator-client', 41 | remoteServiceName: 'calculator-service', 42 | tracerConfig: { 43 | endpoint: 'http://localhost:9411/api/v1/spans', 44 | httpInterval: 1000, 45 | httpTimeout: 5000, 46 | sampleRate: 1.0, 47 | }, 48 | }), 49 | ], 50 | }, 51 | ) 52 | 53 | function symbolToOperation(sym: string): Operation { 54 | switch (sym) { 55 | case 'add': 56 | return Operation.ADD 57 | case 'subtract': 58 | return Operation.SUBTRACT 59 | case 'multiply': 60 | return Operation.MULTIPLY 61 | case 'divide': 62 | return Operation.DIVIDE 63 | default: 64 | throw new Error(`Unrecognized operation: ${sym}`) 65 | } 66 | } 67 | 68 | app.get('/', (req: express.Request, res: express.Response): void => { 69 | res.sendFile(path.join(__dirname, './index.html')) 70 | }) 71 | 72 | app.get('/ping', (req: express.Request, res: express.Response): void => { 73 | thriftClient.ping({ headers: req.headers }).then( 74 | () => { 75 | res.send('success') 76 | }, 77 | (err: any) => { 78 | console.log('err: ', err) 79 | res.status(500).send(err) 80 | }, 81 | ) 82 | }) 83 | 84 | app.get( 85 | '/calculate', 86 | (req: express.Request, res: express.Response): void => { 87 | const work: Work = new Work({ 88 | num1: Number(req.query.left), 89 | num2: Number(req.query.right), 90 | op: symbolToOperation(req.query.op as string), 91 | }) 92 | 93 | thriftClient.calculate(1, work, { headers: req.headers }).then( 94 | (val: number) => { 95 | res.send(`result: ${val}`) 96 | }, 97 | (err: any) => { 98 | res.status(500).send(err) 99 | }, 100 | ) 101 | }, 102 | ) 103 | 104 | app.listen(CLIENT_CONFIG.port, () => { 105 | console.log( 106 | `Web server listening at http://${CLIENT_CONFIG.host}:${CLIENT_CONFIG.port}`, 107 | ) 108 | }) 109 | } 110 | 111 | init() 112 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/src/ttwitter/com/creditkarma/finagle/thrift/Delegation.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /* 4 | * Autogenerated by @creditkarma/thrift-typescript v3.7.6 5 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 6 | */ 7 | import * as thrift from "@creditkarma/thrift-server-core"; 8 | export interface IDelegation { 9 | src?: string; 10 | dst?: string; 11 | } 12 | export interface IDelegationArgs { 13 | src?: string; 14 | dst?: string; 15 | } 16 | export const DelegationCodec: thrift.IStructCodec = { 17 | encode(args: IDelegationArgs, output: thrift.TProtocol): void { 18 | const obj: any = { 19 | src: args.src, 20 | dst: args.dst 21 | }; 22 | output.writeStructBegin("Delegation"); 23 | if (obj.src != null) { 24 | output.writeFieldBegin("src", thrift.TType.STRING, 1); 25 | output.writeString(obj.src); 26 | output.writeFieldEnd(); 27 | } 28 | if (obj.dst != null) { 29 | output.writeFieldBegin("dst", thrift.TType.STRING, 2); 30 | output.writeString(obj.dst); 31 | output.writeFieldEnd(); 32 | } 33 | output.writeFieldStop(); 34 | output.writeStructEnd(); 35 | return; 36 | }, 37 | decode(input: thrift.TProtocol): IDelegation { 38 | let _args: any = {}; 39 | input.readStructBegin(); 40 | while (true) { 41 | const ret: thrift.IThriftField = input.readFieldBegin(); 42 | const fieldType: thrift.TType = ret.fieldType; 43 | const fieldId: number = ret.fieldId; 44 | if (fieldType === thrift.TType.STOP) { 45 | break; 46 | } 47 | switch (fieldId) { 48 | case 1: 49 | if (fieldType === thrift.TType.STRING) { 50 | const value_1: string = input.readString(); 51 | _args.src = value_1; 52 | } 53 | else { 54 | input.skip(fieldType); 55 | } 56 | break; 57 | case 2: 58 | if (fieldType === thrift.TType.STRING) { 59 | const value_2: string = input.readString(); 60 | _args.dst = value_2; 61 | } 62 | else { 63 | input.skip(fieldType); 64 | } 65 | break; 66 | default: { 67 | input.skip(fieldType); 68 | } 69 | } 70 | input.readFieldEnd(); 71 | } 72 | input.readStructEnd(); 73 | return { 74 | src: _args.src, 75 | dst: _args.dst 76 | }; 77 | } 78 | }; 79 | export class Delegation extends thrift.StructLike implements IDelegation { 80 | public src?: string; 81 | public dst?: string; 82 | public readonly _annotations: thrift.IThriftAnnotations = {}; 83 | public readonly _fieldAnnotations: thrift.IFieldAnnotations = {}; 84 | constructor(args: IDelegationArgs = {}) { 85 | super(); 86 | if (args.src != null) { 87 | const value_3: string = args.src; 88 | this.src = value_3; 89 | } 90 | if (args.dst != null) { 91 | const value_4: string = args.dst; 92 | this.dst = value_4; 93 | } 94 | } 95 | public static read(input: thrift.TProtocol): Delegation { 96 | return new Delegation(DelegationCodec.decode(input)); 97 | } 98 | public static write(args: IDelegationArgs, output: thrift.TProtocol): void { 99 | return DelegationCodec.encode(args, output); 100 | } 101 | public write(output: thrift.TProtocol): void { 102 | return DelegationCodec.encode(this, output); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /packages/zipkin-core/src/main/zipkin.ts: -------------------------------------------------------------------------------- 1 | import { IRequestHeaders, LogFunction } from '@creditkarma/thrift-server-core' 2 | 3 | import { 4 | BatchRecorder, 5 | ConsoleRecorder, 6 | Context, 7 | ExplicitContext, 8 | JsonEncoder, 9 | jsonEncoder, 10 | Recorder, 11 | sampler, 12 | TraceId, 13 | Tracer, 14 | } from 'zipkin' 15 | 16 | import { ZipkinHeaders } from './constants' 17 | 18 | import { HttpLogger } from 'zipkin-transport-http' 19 | 20 | import { IZipkinTracerConfig } from './types' 21 | 22 | class MaybeMap extends Map { 23 | public getOrElse(key: K, orElse: () => V): V { 24 | const value: V | undefined = this.get(key) 25 | if (value === undefined) { 26 | const newValue: V = orElse() 27 | this.set(key, newValue) 28 | return newValue 29 | } else { 30 | return value 31 | } 32 | } 33 | } 34 | 35 | interface IHttpLoggerOptions { 36 | endpoint: string 37 | httpInterval?: number 38 | httpTimeout?: number 39 | headers?: IRequestHeaders 40 | jsonEncoder?: JsonEncoder 41 | } 42 | 43 | // Save tracers by service name 44 | const TRACER_CACHE: MaybeMap = new MaybeMap() 45 | 46 | /** 47 | * `http://localhost:9411/api/v1/spans` 48 | */ 49 | function recorderForOptions(options: IZipkinTracerConfig): Recorder { 50 | if (options.endpoint !== undefined) { 51 | const httpOptions: IHttpLoggerOptions = { 52 | endpoint: options.endpoint, 53 | headers: options.headers, 54 | httpInterval: options.httpInterval, 55 | httpTimeout: options.httpTimeout, 56 | jsonEncoder: 57 | options.zipkinVersion === 'v2' 58 | ? jsonEncoder.JSON_V2 59 | : jsonEncoder.JSON_V1, 60 | } 61 | 62 | const logger: LogFunction | undefined = options.logger 63 | const httpLogger: any = new HttpLogger(httpOptions) 64 | 65 | if (logger !== undefined) { 66 | httpLogger.on('error', (err: Error) => { 67 | logger( 68 | ['error', 'zipkin', 'recorderForOptions'], 69 | `An error occurred sending trace: ${err.message}`, 70 | ) 71 | }) 72 | 73 | httpLogger.on('success', () => { 74 | logger(['debug', 'zipkin'], `Zipkin trace sent successfully`) 75 | }) 76 | } 77 | 78 | return new BatchRecorder({ logger: httpLogger }) 79 | } else { 80 | return new ConsoleRecorder() 81 | } 82 | } 83 | 84 | interface IHeaderMap { 85 | [name: string]: string 86 | } 87 | 88 | export function getHeadersForTraceId(traceId?: TraceId): IHeaderMap { 89 | if (traceId !== null && traceId !== undefined) { 90 | const headers: IHeaderMap = {} 91 | headers[ZipkinHeaders.TraceId] = traceId.traceId 92 | headers[ZipkinHeaders.SpanId] = traceId.spanId 93 | headers[ZipkinHeaders.ParentId] = traceId.parentId || '' 94 | 95 | traceId.sampled.ifPresent((sampled: boolean) => { 96 | headers[ZipkinHeaders.Sampled] = sampled ? '1' : '0' 97 | }) 98 | 99 | return headers 100 | } else { 101 | return {} 102 | } 103 | } 104 | 105 | export function getTracerForService( 106 | serviceName: string, 107 | options: IZipkinTracerConfig = {}, 108 | ): Tracer { 109 | return TRACER_CACHE.getOrElse(serviceName, () => { 110 | const ctxImpl: Context = new ExplicitContext() 111 | const recorder: Recorder = recorderForOptions(options) 112 | return new Tracer({ 113 | ctxImpl, 114 | recorder, 115 | sampler: new sampler.CountingSampler( 116 | options.debug 117 | ? 100 118 | : options.sampleRate !== undefined 119 | ? options.sampleRate 120 | : 0.1, 121 | ), 122 | localServiceName: serviceName, // name of this application 123 | }) 124 | }) 125 | } 126 | -------------------------------------------------------------------------------- /packages/thrift-client-zipkin-filter/README.md: -------------------------------------------------------------------------------- 1 | # Thrift Client Zipking Filter 2 | 3 | One of the problems that arise in microservice architectures is tracing the life of a request. Where do errors occur? Where are latencies? Just understanding all of the services a request touches can be non-trivial in complex systems. 4 | 5 | This filter for [@creditkarma/thrift-client](https://github.com/creditkarma/thrift-server/tree/master/packages/thrift-client) helps to solve this problem by adding support for distributed tracing with [Zipkin](https://github.com/openzipkin/zipkin-js). 6 | 7 | ## Installation 8 | 9 | `ThriftClientZipkinFilter` has a few `peerDependencies`. 10 | 11 | ```sh 12 | npm install --save @creditkarma/thrift-server-core 13 | npm install --save @creditkarma/thrift-client 14 | npm install --save @creditkarma/zipkin-core 15 | npm install --save @creditkarma/thrift-client-zipkin-filter 16 | ``` 17 | 18 | ## Usage 19 | 20 | Zipkin tracing is added to your Thrift client through filters. 21 | 22 | ```typescript 23 | import { 24 | createHttpClient, 25 | RequestOptions, 26 | } from '@creditkaram/thrift-client' 27 | 28 | import { 29 | ThriftClientZipkinFilter, 30 | } from '@creditkarma/thrift-client-zipkin-filter' 31 | 32 | import { Calculator } from './codegen/calculator' 33 | 34 | const thriftClient: Calculator.Client> = 35 | createHttpClient(Calculator.Client, { 36 | hostName: 'localhost', 37 | port: 8080, 38 | register: [ ThriftClientZipkinFilter({ 39 | localServiceName: 'calculator-client', 40 | remoteServiceName: 'calculator-service', 41 | tracerConfig: { 42 | endpoint: 'http://localhost:9411/api/v1/spans', 43 | sampleRate: 0.1, 44 | } 45 | }) ] 46 | }) 47 | ``` 48 | 49 | In order for tracing to be useful the services you are communicating with will also need to be setup with Zipkin tracing. Plugins are available for `thrift-server-hapi` and `thrift-server-express`. The provided plugins in Thrift Server only support HTTP transport at the moment. 50 | 51 | ### Options 52 | 53 | * localServiceName (required): The name of your service. 54 | * remoteServiceName (optional): The name of the service you are calling. 55 | * tracerConfig.debug (optional): In debug mode all requests are sampled. 56 | * tracerConfig.endpoint (optional): URL of your collector (where to send traces). 57 | * tracerConfig.sampleRate (optional): Percentage (from 0 to 1) of requests to sample. Defaults to 0.1. 58 | * tracerConfig.httpInterval (optional): Sampling data is batched to reduce network load. This is the rate (in milliseconds) at which to empty the sample queue. Defaults to 1000. 59 | 60 | If the endpoint is set then the plugin will send sampling data to the given endpoint over HTTP. If the endpoint is not set then sampling data will just be logged to the console. 61 | 62 | ### Tracing Non-Thrift Endpoints 63 | 64 | Sometimes, as part of completing your service request, you may need to gather data from both Thrift and non-Thrift endpoints. To get a complete picture you need to trace all of these calls. You can add Zipkin to other requests with instrumentation provided by the [OpenZipkin](https://github.com/openzipkin/zipkin-js) project. 65 | 66 | When constructing instrumentation provided by another library you need to use the same `Tracer` in order to maintain the correct trace context. You can import this shared `Tracer` through a call to `getTracerForService`. This assumes a `Tracer` has already been created for your service by usage of one of the Thrift Zipkin plugins. 67 | 68 | ```typescript 69 | import { getTracerForService } from '@creditkarma/thrift-server-core' 70 | import * as wrapRequest from 'zipkin-instrumentation-request' 71 | import * as request from 'request' 72 | 73 | const tracer = getTracerForService('calculator-client') 74 | const zipkinRequest = wrapRequest(request, { tracer, remoteServiceName: 'calculator-service' }) 75 | zipkinRequest.get(url, (err, resp, body) => { 76 | // Do something 77 | }) 78 | ``` 79 | 80 | ## Contributing 81 | 82 | For more information about contributing new features and bug fixes, see our [Contribution Guidelines](../../CONTRIBUTING.md). 83 | External contributors must sign Contributor License Agreement (CLA) 84 | 85 | ## License 86 | 87 | This project is licensed under [Apache License Version 2.0](./LICENSE) 88 | -------------------------------------------------------------------------------- /packages/thrift-client-ttwitter-filter/src/ttwitter/com/creditkarma/finagle/thrift/RequestContext.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /* 4 | * Autogenerated by @creditkarma/thrift-typescript v3.7.6 5 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 6 | */ 7 | import * as thrift from "@creditkarma/thrift-server-core"; 8 | export interface IRequestContext { 9 | key?: Buffer; 10 | value?: Buffer; 11 | } 12 | export interface IRequestContextArgs { 13 | key?: string | Buffer; 14 | value?: string | Buffer; 15 | } 16 | export const RequestContextCodec: thrift.IStructCodec = { 17 | encode(args: IRequestContextArgs, output: thrift.TProtocol): void { 18 | const obj: any = { 19 | key: (typeof args.key === "string" ? Buffer.from(args.key) : args.key), 20 | value: (typeof args.value === "string" ? Buffer.from(args.value) : args.value) 21 | }; 22 | output.writeStructBegin("RequestContext"); 23 | if (obj.key != null) { 24 | output.writeFieldBegin("key", thrift.TType.STRING, 1); 25 | output.writeBinary(obj.key); 26 | output.writeFieldEnd(); 27 | } 28 | if (obj.value != null) { 29 | output.writeFieldBegin("value", thrift.TType.STRING, 2); 30 | output.writeBinary(obj.value); 31 | output.writeFieldEnd(); 32 | } 33 | output.writeFieldStop(); 34 | output.writeStructEnd(); 35 | return; 36 | }, 37 | decode(input: thrift.TProtocol): IRequestContext { 38 | let _args: any = {}; 39 | input.readStructBegin(); 40 | while (true) { 41 | const ret: thrift.IThriftField = input.readFieldBegin(); 42 | const fieldType: thrift.TType = ret.fieldType; 43 | const fieldId: number = ret.fieldId; 44 | if (fieldType === thrift.TType.STOP) { 45 | break; 46 | } 47 | switch (fieldId) { 48 | case 1: 49 | if (fieldType === thrift.TType.STRING) { 50 | const value_1: Buffer = input.readBinary(); 51 | _args.key = value_1; 52 | } 53 | else { 54 | input.skip(fieldType); 55 | } 56 | break; 57 | case 2: 58 | if (fieldType === thrift.TType.STRING) { 59 | const value_2: Buffer = input.readBinary(); 60 | _args.value = value_2; 61 | } 62 | else { 63 | input.skip(fieldType); 64 | } 65 | break; 66 | default: { 67 | input.skip(fieldType); 68 | } 69 | } 70 | input.readFieldEnd(); 71 | } 72 | input.readStructEnd(); 73 | return { 74 | key: _args.key, 75 | value: _args.value 76 | }; 77 | } 78 | }; 79 | export class RequestContext extends thrift.StructLike implements IRequestContext { 80 | public key?: Buffer; 81 | public value?: Buffer; 82 | public readonly _annotations: thrift.IThriftAnnotations = {}; 83 | public readonly _fieldAnnotations: thrift.IFieldAnnotations = {}; 84 | constructor(args: IRequestContextArgs = {}) { 85 | super(); 86 | if (args.key != null) { 87 | const value_3: Buffer = (typeof args.key === "string" ? Buffer.from(args.key) : args.key); 88 | this.key = value_3; 89 | } 90 | if (args.value != null) { 91 | const value_4: Buffer = (typeof args.value === "string" ? Buffer.from(args.value) : args.value); 92 | this.value = value_4; 93 | } 94 | } 95 | public static read(input: thrift.TProtocol): RequestContext { 96 | return new RequestContext(RequestContextCodec.decode(input)); 97 | } 98 | public static write(args: IRequestContextArgs, output: thrift.TProtocol): void { 99 | return RequestContextCodec.encode(args, output); 100 | } 101 | public write(output: thrift.TProtocol): void { 102 | return RequestContextCodec.encode(this, output); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /packages/thrift-client-timing-filter/src/main/ThriftClientTimingFilter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IRequestResponse, 3 | IThriftClientFilterConfig, 4 | IThriftRequest, 5 | NextFunction, 6 | } from '@creditkarma/thrift-client' 7 | 8 | import { LogFunction } from '@creditkarma/thrift-server-core' 9 | import { defaultLogger } from './logger' 10 | 11 | function getStartTime(): [number, number] { 12 | return process.hrtime() 13 | } 14 | 15 | function getTimings(startTime: [number, number]): number { 16 | const duration = process.hrtime(startTime) 17 | return duration[0] * 1e3 + duration[1] * 1e-6 // In ms 18 | } 19 | 20 | interface ITimingUpdate { 21 | methodName: string 22 | duration: number 23 | status: 'error' | 'success' 24 | } 25 | 26 | export interface IStatusCount { 27 | error: number 28 | success: number 29 | } 30 | 31 | export interface ITimingFilterOptions { 32 | remoteServiceName: string 33 | interval?: number 34 | logger?: LogFunction 35 | tags?: Array 36 | } 37 | 38 | export interface IRequestsPerMethod { 39 | [name: string]: number 40 | } 41 | 42 | export interface ITimingEvent { 43 | status: IStatusCount 44 | maxDuration: number 45 | averageDuration: number 46 | requestsPerMethod: IRequestsPerMethod 47 | } 48 | 49 | export function ThriftClientTimingFilter({ 50 | remoteServiceName, 51 | interval = 5000, 52 | logger = defaultLogger, 53 | tags = [], 54 | }: ITimingFilterOptions): IThriftClientFilterConfig { 55 | let maxTime: number = 0 56 | let totalTime: number = 0 57 | let count: number = 0 58 | const statusCount: IStatusCount = { 59 | success: 0, 60 | error: 0, 61 | } 62 | const requestsPerMethod: IRequestsPerMethod = {} 63 | 64 | function logTimings(): void { 65 | const timingEvent: ITimingEvent = { 66 | status: statusCount, 67 | maxDuration: maxTime, 68 | averageDuration: totalTime / count, 69 | requestsPerMethod, 70 | } 71 | 72 | logger( 73 | ['info', 'metrics', 'RequestDuration', remoteServiceName, ...tags], 74 | timingEvent, 75 | ) 76 | 77 | // Reset for next interval 78 | statusCount.error = 0 79 | statusCount.success = 0 80 | maxTime = 0 81 | totalTime = 0 82 | count = 0 83 | 84 | for (const key in requestsPerMethod) { 85 | if (requestsPerMethod.hasOwnProperty(key)) { 86 | requestsPerMethod[key] = 0 87 | } 88 | } 89 | } 90 | 91 | function updateData({ methodName, duration, status }: ITimingUpdate) { 92 | count += 1 93 | statusCount[status] += 1 94 | totalTime += duration 95 | 96 | if (requestsPerMethod[methodName] === undefined) { 97 | requestsPerMethod[methodName] = 0 98 | } 99 | 100 | requestsPerMethod[methodName] += 1 101 | 102 | if (duration > maxTime) { 103 | maxTime = duration 104 | } 105 | } 106 | 107 | function scheduleUpdate() { 108 | const timer = setTimeout(() => { 109 | logTimings() 110 | scheduleUpdate() 111 | }, interval) 112 | 113 | timer.unref() 114 | } 115 | 116 | scheduleUpdate() 117 | 118 | return { 119 | handler( 120 | request: IThriftRequest, 121 | next: NextFunction, 122 | ): Promise { 123 | const startTime = getStartTime() 124 | return next() 125 | .then((res: IRequestResponse) => { 126 | updateData({ 127 | methodName: request.methodName, 128 | duration: getTimings(startTime), 129 | status: 'success', 130 | }) 131 | 132 | return res 133 | }) 134 | .catch((err) => { 135 | updateData({ 136 | methodName: request.methodName, 137 | duration: getTimings(startTime), 138 | status: 'error', 139 | }) 140 | 141 | return Promise.reject(err) 142 | }) 143 | }, 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /packages/thrift-integration/src/apache-calculator-service.ts: -------------------------------------------------------------------------------- 1 | import * as net from 'net' 2 | import * as thrift from 'thrift' 3 | 4 | import { 5 | Calculator, 6 | Choice, 7 | CommonStruct, 8 | Operation, 9 | Work, 10 | } from './generated-apache/calculator-service' 11 | 12 | import { 13 | Code, 14 | MappedStruct, 15 | SharedStruct, 16 | SharedUnion, 17 | } from './generated-apache/shared' 18 | 19 | import { CommonUnion } from './generated-apache/common' 20 | 21 | export function createServer(): net.Server { 22 | const impl: Calculator.IHandler = { 23 | ping(): void { 24 | return 25 | }, 26 | add(a: number, b: number): number { 27 | return a + b 28 | }, 29 | addInt64(a: thrift.Int64, b: thrift.Int64): thrift.Int64 { 30 | return new thrift.Int64(a.toNumber() + b.toNumber()) 31 | }, 32 | addWithContext(a: number, b: number): number { 33 | return a + b 34 | }, 35 | calculate(logId: number, work: Work): number { 36 | switch (work.op) { 37 | case Operation.ADD: 38 | return work.num1 + work.num2 39 | case Operation.SUBTRACT: 40 | return work.num1 - work.num2 41 | case Operation.DIVIDE: 42 | return work.num1 / work.num2 43 | case Operation.MULTIPLY: 44 | return work.num1 * work.num2 45 | } 46 | }, 47 | zip(): void { 48 | return 49 | }, 50 | getStruct(key: number): SharedStruct { 51 | return new SharedStruct({ 52 | code: new Code({ status: 5 }), 53 | value: 'test', 54 | }) 55 | }, 56 | getUnion(index: number): SharedUnion { 57 | if (index === 1) { 58 | return SharedUnion.fromOption1('foo') 59 | } else { 60 | return SharedUnion.fromOption2('bar') 61 | } 62 | }, 63 | getMappedStruct(index: number): MappedStruct { 64 | const map = new Map() 65 | map.set('one', { 66 | code: { 67 | status: 5, 68 | }, 69 | value: 'test', 70 | }) 71 | 72 | return new MappedStruct({ 73 | data: map, 74 | }) 75 | }, 76 | echoBinary(word: Buffer): string { 77 | return word.toString('utf-8') 78 | }, 79 | echoString(word: string): string { 80 | return word 81 | }, 82 | checkName(choice: Choice): string { 83 | if (choice.firstName !== undefined) { 84 | return `FirstName: ${choice.firstName.name}` 85 | } else if (choice.lastName !== undefined) { 86 | return `LastName: ${choice.lastName.name}` 87 | } else { 88 | throw new Error(`Unknown choice`) 89 | } 90 | }, 91 | checkOptional(type?: string): string { 92 | if (type === undefined) { 93 | return 'undefined' 94 | } else { 95 | return type 96 | } 97 | }, 98 | mapOneList(list: Array): Array { 99 | return list.map((next: number) => next + 1) 100 | }, 101 | mapValues(map: Map): Array { 102 | return Array.from(map.values()) 103 | }, 104 | listToMap(list: Array>): Map { 105 | return list.reduce( 106 | (acc: Map, next: Array) => { 107 | acc.set(next[0], next[1]) 108 | return acc 109 | }, 110 | new Map(), 111 | ) 112 | }, 113 | fetchThing(): CommonStruct { 114 | return new CommonStruct({ 115 | code: new Code({ status: 5 }), 116 | value: 'test', 117 | }) 118 | }, 119 | fetchUnion(): CommonUnion { 120 | return new CommonUnion({ option1: 'test' }) 121 | }, 122 | broken(): void { 123 | throw new Error(`Yeah, this didn't work`) 124 | }, 125 | } 126 | 127 | return thrift.createServer( 128 | Calculator.Processor, 129 | impl, 130 | { 131 | transport: thrift.TFramedTransport, 132 | }, 133 | ) 134 | } 135 | -------------------------------------------------------------------------------- /packages/thrift-client-zipkin-filter/src/main/ThriftClientZipkinFilter.ts: -------------------------------------------------------------------------------- 1 | import { Instrumentation, TraceId, Tracer } from 'zipkin' 2 | 3 | import { 4 | formatUrl, 5 | IRequestContext, 6 | IRequestHeaders, 7 | LogFunction, 8 | } from '@creditkarma/thrift-server-core' 9 | 10 | import { 11 | IRequest, 12 | IRequestResponse, 13 | IThriftClientFilter, 14 | IThriftRequest, 15 | NextFunction, 16 | RequestOptions, 17 | } from '@creditkarma/thrift-client' 18 | 19 | import { 20 | addL5Dheaders, 21 | containsZipkinHeaders, 22 | getTracerForService, 23 | hasL5DHeader, 24 | IZipkinClientOptions, 25 | traceIdForHeaders, 26 | traceIdFromTraceId, 27 | } from '@creditkarma/zipkin-core' 28 | 29 | function applyL5DHeaders( 30 | requestHeaders: IRequestHeaders, 31 | headers: IRequestHeaders, 32 | ): IRequestHeaders { 33 | if (hasL5DHeader(requestHeaders)) { 34 | return addL5Dheaders(headers) 35 | } else { 36 | return headers 37 | } 38 | } 39 | 40 | function readRequestContext( 41 | requestHeaders: IRequestHeaders, 42 | tracer: Tracer, 43 | logger?: LogFunction, 44 | ): IRequestContext { 45 | if (containsZipkinHeaders(requestHeaders)) { 46 | return { 47 | traceId: traceIdForHeaders(requestHeaders), 48 | headers: requestHeaders, 49 | logger, 50 | } 51 | } else { 52 | const rootId = tracer.createRootId() 53 | return { 54 | traceId: { 55 | traceId: rootId.traceId, 56 | spanId: rootId.spanId, 57 | parentId: rootId.parentId, 58 | sampled: rootId.sampled.getOrElse(false), 59 | }, 60 | headers: {}, 61 | logger, 62 | } 63 | } 64 | } 65 | 66 | function readRequestHeaders( 67 | request: IThriftRequest, 68 | ): IRequestHeaders { 69 | if (request.context && request.context.headers) { 70 | return request.context.headers 71 | } else { 72 | return {} 73 | } 74 | } 75 | 76 | export function ThriftClientZipkinFilter({ 77 | localServiceName, 78 | remoteServiceName, 79 | tracerConfig = {}, 80 | }: IZipkinClientOptions): IThriftClientFilter { 81 | const serviceName: string = remoteServiceName || localServiceName 82 | const tracer: Tracer = getTracerForService(serviceName, tracerConfig) 83 | const instrumentation = new Instrumentation.HttpClient({ 84 | tracer, 85 | serviceName: localServiceName, 86 | remoteServiceName, 87 | }) 88 | 89 | return { 90 | methods: [], 91 | handler( 92 | request: IThriftRequest, 93 | next: NextFunction, 94 | ): Promise { 95 | const requestHeaders: IRequestHeaders = readRequestHeaders(request) 96 | const requestContext: IRequestContext = readRequestContext( 97 | requestHeaders, 98 | tracer, 99 | tracerConfig.logger, 100 | ) 101 | 102 | if (requestContext.traceId !== undefined) { 103 | tracer.setId(traceIdFromTraceId(requestContext.traceId)) 104 | 105 | return tracer.scoped(() => { 106 | const updatedHeaders: IRequestHeaders = instrumentation.recordRequest( 107 | { headers: {} }, 108 | formatUrl(request.uri), 109 | request.methodName || '', 110 | ).headers 111 | 112 | const traceId: TraceId = tracer.id 113 | const withLD5Headers: IRequestHeaders = applyL5DHeaders( 114 | requestHeaders, 115 | updatedHeaders, 116 | ) 117 | 118 | return next(request.data, { headers: withLD5Headers }).then( 119 | (res: IRequestResponse) => { 120 | instrumentation.recordResponse( 121 | traceId as any, 122 | `${res.statusCode}`, 123 | ) 124 | return res 125 | }, 126 | (err: any) => { 127 | instrumentation.recordError(traceId as any, err) 128 | return Promise.reject(err) 129 | }, 130 | ) 131 | }) 132 | } else { 133 | return next() 134 | } 135 | }, 136 | } 137 | } 138 | --------------------------------------------------------------------------------