Manweill / swagger-axios-codegen

swagger client to use axios and typescript
MIT License
306 stars 83 forks source link

Problems building and testing locally the library #105

Closed azakordonets closed 4 years ago

azakordonets commented 4 years ago

Hi, i'm trying to add a feature to your library that would allow to create multiple instances of the same service with different settings. However, i faced with some weird issues that i cannot figure out and i was hoping that you could help me. Here's a basic setup that doesn't want to work for me :

  1. I have created a test project
  2. I have run npm install {local-path-to-your-forked-library} ( i called it test-swagger-axios-codegen just to be able to easier to recognize)
  3. This project has next index.js file :
    "use strict";
    exports.__esModule = true;
    var swagger_axios_codegen_1 = require("test-swagger-axios-codegen");
    var defaultOptions = {
    serviceNameSuffix: 'Service',
    enumNamePrefix: 'Enum',
    methodNameMode: 'operationId',
    outputDir: './service',
    fileName: 'service.index.ts',
    useStaticMethod: false,
    useCustomerRequestInstance: true,
    include: [],
    strictNullChecks: true,
    /** definition Class mode ,auto use interface mode to streamlined code*/
    modelMode: 'interface',
    useClassTransformer: false,
    source: require('./swagger.json')
    };
    swagger_axios_codegen_1.codegen(defaultOptions);
  4. When i run this file using local version of the library, my service template has this broken part of the code :

    
    function serviceHeader(options, basePath) {
    const classTransformerImport = options.useClassTransformer
    ? `import { Expose, Transform, Type, plainToClass } from 'class-transformer';
    `
    : '';
    return `/** Generate by swagger-axios-codegen */
    // tslint:disable
    /* eslint-disable */
    import axiosStatic, { AxiosInstance } from 'axios';
    
    const basePath = '${utils_1.trimString(basePath, '/', 'right')}'
    ${classTransformerImport}
    
    export interface IRequestOptions {
    headers?: any;
    baseURL?: string;
    responseType?: string;
    }
    
    export interface IRequestConfig {
    method?: any;
    headers?: any;
    url?: any;
    data?: any;
    params?: any;
    }
    
    // Add options interface
    export interface ServiceOptions {
    axios?: AxiosInstance,
    }
    
    ${requestHeader()}
    `;
    }

export interface IList extends Array {} export interface List extends Array {} export interface IDictionary {

}


This is because `utils_1.trimString` is not recognized as a function. 

I tried my test code with your package from npm and it works just fine. Before installing this local library i did run `npm run prepublish` command. 

This is how transpiled `serviceHeader.js` looks like. 

```javascript
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const genericTypeDefinitionTemplate_1 = require("./genericTypeDefinitionTemplate");
const utils_1 = require("../utils");
function serviceHeader(options, basePath) {
    const classTransformerImport = options.useClassTransformer
        ? `import { Expose, Transform, Type, plainToClass } from 'class-transformer';
  ` : '';
    return `/** Generate by swagger-axios-codegen */
  // tslint:disable
  /* eslint-disable */
  import axiosStatic, { AxiosInstance } from 'axios';

  const basePath = '${utils_1.trimString(basePath, '/', 'right')}'
  ${classTransformerImport}

  export interface IRequestOptions {
    headers?: any;
    baseURL?: string;
    responseType?: string;
  }

  export interface IRequestConfig {
    method?: any;
    headers?: any;
    url?: any;
    data?: any;
    params?: any;
  }

  // Add options interface
  export interface ServiceOptions {
    axios?: AxiosInstance,
  }

  ${requestHeader()}
  `;
}
exports.serviceHeader = serviceHeader;
function customerServiceHeader(options, basePath) {
    return `/** Generate by swagger-axios-codegen */
  // tslint:disable
  /* eslint-disable */
  export interface IRequestOptions {
    headers?: any;
  }

  export interface IRequestPromise<T=any> extends Promise<IRequestResponse<T>> {}

  export interface IRequestResponse<T=any> {
    data: T;
    status: number;
    statusText: string;
    headers: any;
    config: any;
    request?: any;
  }

  export interface IRequestInstance {
    (config: any): IRequestPromise;
    (url: string, config?: any): IRequestPromise;
    request<T = any>(config: any): IRequestPromise<T>;
  }

  export interface IRequestConfig {
    method?: any;
    headers?: any;
    url?: any;
    data?: any;
    params?: any;
  }

  const basePath = '${basePath}'

  // Add options interface
  export interface ServiceOptions {
    axios?: IRequestInstance,
  }

  ${requestHeader()}

  `;
}
exports.customerServiceHeader = customerServiceHeader;
function requestHeader() {
    return `

  // Add default options
  export const serviceOptions: ServiceOptions = {
  };

  // Instance selector
  export function axios(configs: IRequestConfig, resolve: (p: any) => void, reject: (p: any) => void): Promise<any> {
    if (serviceOptions.axios) {
      return serviceOptions.axios.request(configs).then(res => {
        resolve(res.data);
      })
        .catch(err => {
          reject(err);
        });
    } else {
      throw new Error('please inject yourself instance like axios  ')
    }
  }

  export function getConfigs(method: string, contentType: string, url: string,options: any):IRequestConfig {
    url = basePath + url
    const configs: IRequestConfig = { ...options, method, url };
    configs.headers = {
      ...options.headers,
      'Content-Type': contentType,
    };
    return configs
  }
  `;
}
function definitionHeader(fileDir) {
    let fileStr = '// empty ';
    if (!!fileDir) {
        console.log('extendDefinitionFile url : ', path.resolve(fileDir));
        if (fs.existsSync(path.resolve(fileDir))) {
            const buffs = fs.readFileSync(path.resolve(fileDir));
            fileStr = buffs.toString('utf8');
        }
    }
    return `
  ${genericTypeDefinitionTemplate_1.universalGenericTypeDefinition()}
  ${genericTypeDefinitionTemplate_1.abpGenericTypeDefinition()}
  // customer definition
  ${fileStr}
  `;
}
exports.definitionHeader = definitionHeader;
//# sourceMappingURL=serviceHeader.js.map

Am i missing something in my setup ? Tried to run this with node 14 and node 10 - same result.

Manweill commented 4 years ago

@azakordonets First, I am also testing local lib with yarn link, see this. And then, can you provide your testing repo on github.

azakordonets commented 4 years ago

Well, that's the problem - i just forked your project and was trying to test it out locally generation - https://github.com/azakordonets/swagger-axios-codegen . It didn't work for me. I created a dummy project - https://github.com/azakordonets/test-axios-codegen and there you can see a result in src/service folder. Swagger file is taken from your example. in package.json there's a file reference to https://github.com/azakordonets/swagger-axios-codegen with no changes. When i run code generation agains local version - i see failed generated file like you can see in demo repo under src/service. If i generate the same file but with library from npm - everything works fine.

Manweill commented 4 years ago

@azakordonets use yarn build or yarn watch on bash.

azakordonets commented 4 years ago

I already did that. In my local copy of https://github.com/azakordonets/swagger-axios-codegen i did yarn build and then in the test project https://github.com/azakordonets/test-axios-codegen i did yarn add {path-to-swagger-axios-codegen-repo}. After that in the https://github.com/azakordonets/test-axios-codegen repo i do next :

test_axios_generator on  master [✘!?] is 📦 v1.0.0 via ⬢ v10.0.0 using ☁️ new10-dev at ☸️  minikube took 7s 
➜ node -v
v10.0.0

test_axios_generator on  master [✘!?] is 📦 v1.0.0 via ⬢ v10.0.0 using ☁️ new10-dev at ☸️  minikube 
➜ yarn -v
1.22.4

test_axios_generator on  master [✘!?] is 📦 v1.0.0 via ⬢ v10.0.0 using ☁️ new10-dev at ☸️  minikube 
➜ node  src/index.js
openApi version: 2.0
finish: 237.416ms

test_axios_generator on  master [✘!?] is 📦 v1.0.0 via ⬢ v10.0.0 using ☁️ new10-dev at ☸️  minikube 
➜ cat service/service.index.ts
function serviceHeader(options, basePath) {
  const classTransformerImport = options.useClassTransformer
    ? `import { Expose, Transform, Type, plainToClass } from 'class-transformer';
  `
    : '';
  return `/** Generate by swagger-axios-codegen */
  // tslint:disable
  /* eslint-disable */
  import axiosStatic, { AxiosInstance } from 'axios';

  const basePath = '${utils_1.trimString(basePath, '/', 'right')}'
  ${classTransformerImport}

  export interface IRequestOptions {
    headers?: any;
    baseURL?: string;
    responseType?: string;
  }

  export interface IRequestConfig {
    method?: any;
    headers?: any;
    url?: any;
    data?: any;
    params?: any;
  }

  // Add options interface
  export interface ServiceOptions {
    axios?: AxiosInstance,
  }

  ${requestHeader()}
  `;
}

export interface IList<T> extends Array<T> {}
export interface List<T> extends Array<T> {}
export interface IDictionary<TValue> {
  [key: string]: TValue;
}
export interface Dictionary<TValue> extends IDictionary<TValue> {}

export interface IListResult<T> {
  items?: T[];
}

export class ListResultDto<T> implements IListResult<T> {
  items?: T[];
}

export interface IPagedResult<T> extends IListResult<T> {
  totalCount: number;
}

export class PagedResultDto<T> implements IPagedResult<T> {
  totalCount!: number;
}

// customer definition
// empty

export class MappersService {
  /**
   *
   */
  updateMapper(
    params: {
      /**  */
      mapperId: string;
    } = {} as any,
    options: IRequestOptions = {}
  ): Promise<string> {
    return new Promise((resolve, reject) => {
      let url = '/crawler/v1/mapper/{mapper.id}';
      url = url.replace('{mapper.id}', params['mapperId'] + '');

      const configs: IRequestConfig = getConfigs('put', 'application/json', url, options);

      let data = null;

      configs.data = data;
      axios(configs, resolve, reject);
    });
  }
}

export class AuditLogService {
  /**
   *
   */
  getAuditLogs(
    params: {
      /**  */
      startDate?: string;
    } = {} as any,
    options: IRequestOptions = {}
  ): Promise<IPagedResult<AuditLogListDto>> {
    return new Promise((resolve, reject) => {
      let url = '/api/services/app/AuditLog/GetAuditLogs';

      const configs: IRequestConfig = getConfigs('get', 'application/json', url, options);
      configs.params = { StartDate: params['startDate'] };
      let data = null;

      configs.data = data;
      axios(configs, resolve, reject);
    });
  }
}

export class YmTicketTypicalService {
  /**
   * 新增或编辑标准票, 需走审批流程
   */
  addOrUpdateTicketTypical(options: IRequestOptions = {}): Promise<JsonResult_Guid> {
    return new Promise((resolve, reject) => {
      let url = '/api/services/app/YmTicketTypical/AddOrUpdateTicketTypical';

      const configs: IRequestConfig = getConfigs('post', 'application/json', url, options);

      let data = null;

      configs.data = data;
      axios(configs, resolve, reject);
    });
  }
}

export class DeviceService {
  /**
   * 分页上传设备地理坐标信息, 参数list的值为 List<DeviceLngLat>, 返回Result对象
   */
  uploadLngLatUsingPost(
    params: {
      /**  */
      clientId?: string;
      /** 设备地理坐标信息集合 */
      list?: List<AuditLogListDto>;
      /**  */
      xToken?: string;
    } = {} as any,
    options: IRequestOptions = {}
  ): Promise<any> {
    return new Promise((resolve, reject) => {
      let url = '/dev/uploadLngLat';

      const configs: IRequestConfig = getConfigs('post', 'application/json', url, options);

      let data = params['list'];

      configs.data = data;
      axios(configs, resolve, reject);
    });
  }
}

export interface Product {
  /** Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles. */
  product_id: string;

  /** Description of product. */
  description: string;

  /** Display name of product. */
  display_name: string;

  /** Capacity of product. For example, 4 people. */
  capacity: string;

  /** Image URL representing the product. */
  image: string;
}

export interface PriceEstimate {
  /** Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles */
  product_id: string;

  /** [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code. */
  currency_code: string;

  /** Display name of product. */
  display_name: string;

  /** Formatted string of estimate in local currency of the start location. Estimate could be a range, a single number (flat rate) or "Metered" for TAXI. */
  estimate: string;

  /** Lower bound of the estimated price. */
  low_estimate: number;

  /** Upper bound of the estimated price. */
  high_estimate: number;

  /** Expected surge multiplier. Surge is active if surge_multiplier is greater than 1. Price estimate already factors in the surge multiplier. */
  surge_multiplier: number;
}

export interface Profile {
  /** First name of the Uber user. */
  first_name: string;

  /** Last name of the Uber user. */
  last_name: string;

  /** Email address of the Uber user */
  email: string;

  /** Image URL of the Uber user. */
  picture: string;

  /** Promo code of the Uber user. */
  promo_code: string;

  /**  */
  role: RoleType;
}

export interface Activity {
  /** Unique identifier for the activity */
  uuid: string;
}

export interface Activities {
  /** Position in pagination. */
  offset: number;

  /** Number of items to retrieve (100 max). */
  limit: number;

  /** Total number of items available. */
  count: number;

  /**  */
  history: Activity[];
}

export interface CreateOrUpdateUserInput {
  /** 用户ID(ID来自User表) */
  userId: number;

  /** 用户权限 */
  userRights: EnumCreateOrUpdateUserInputUserRights[];
}

export interface Abc {
  /**  */
  id: string;

  /**  */
  isLow: boolean;
}

export interface NumberArrayEnumModel {
  /**  */
  foo: INumberArrayEnumModelFoo[];

  /**  */
  bar: INumberArrayEnumModelBar[];
}

export interface Error {
  /**  */
  code: number;

  /**  */
  message: string;

  /**  */
  fields: string;
}

export interface AuditLogListDto {
  /**  */
  userId: number;

  /**  */
  id: number;
}

export interface JsonResult_Guid {
  /** 结果 */
  result: boolean;

  /** 代码 */
  code: EnumJsonResult_GuidCode;

  /** 数据 */
  data: string;

  /** 消息 */
  message: string;
}

export enum RoleType {
  'Admin' = 'Admin',
  'User' = 'User',
  'SetSms' = 'SetSms'
}
export enum EnumCreateOrUpdateUserInputUserRights {
  'View' = 'View',
  'Operate' = 'Operate',
  'Auth' = 'Auth',
  'Search' = 'Search',
  'Delete' = 'Delete',
  'UserManage' = 'UserManage',
  'UserConfig' = 'UserConfig',
  'SetTime' = 'SetTime',
  'SetNetwork' = 'SetNetwork',
  'SetSms' = 'SetSms',
  'SystemManage' = 'SystemManage'
}
type INumberArrayEnumModelFoo = 0 | 1 | 2 | 4 | 8;
type INumberArrayEnumModelBar = 0 | 1 | 2 | 3;
export enum EnumJsonResult_GuidCode {
  'Success' = 'Success',
  'Timeout' = 'Timeout',
  'Fail' = 'Fail',
  'Expired' = 'Expired',
  'Error' = 'Error',
  'InternalServerError' = 'InternalServerError',
  'InvalidAnonymousAccess' = 'InvalidAnonymousAccess',
  'UserSessionExpired' = 'UserSessionExpired',
  'UserIsBinded' = 'UserIsBinded'
}

How do you usually test your local changes of the library ? Do you run code generation in the same project, or you use separate project for this testing ?

A bit more about my setup :

 tsc -v
Version 3.9.7

node 10
yarn 1.22.4
Manweill commented 4 years ago

@azakordonets Hi, see in https://github.com/Manweill/swagger-axios-codegen/blob/master/CONTRIBUTING.md

azakordonets commented 4 years ago

Thanks for keeping helping me!. Unfortunately it didn't help me. I even tried to run this in docker :

docker run -it --volume $(pwd):/app sandrokeil/typescript node src/index.js; cat service/service.index.ts
openApi version: 2.0
finish: 291.027ms
function serviceHeader(options, basePath) {
  const classTransformerImport = options.useClassTransformer
    ? `import { Expose, Transform, Type, plainToClass } from 'class-transformer';
  `
    : '';
  return `/** Generate by swagger-axios-codegen */
  // tslint:disable
  /* eslint-disable */
  import axiosStatic, { AxiosInstance } from 'axios';

  const basePath = '${utils_1.trimString(basePath, '/', 'right')}'
  ${classTransformerImport}

  export interface IRequestOptions {
    headers?: any;
    baseURL?: string;
    responseType?: string;
  }

  export interface IRequestConfig {
    method?: any;
    headers?: any;
    url?: any;
    data?: any;
    params?: any;
  }

  // Add options interface
  export interface ServiceOptions {
    axios?: AxiosInstance,
  }

  ${requestHeader()}
  `;
}

export interface IList<T> extends Array<T> {}
export interface List<T> extends Array<T> {}
export interface IDictionary<TValue> {
  [key: string]: TValue;
}
export interface Dictionary<TValue> extends IDictionary<TValue> {}
.....

The project i'm using is https://github.com/azakordonets/test-axios-codegen . test-swagger-axios-codegen is local copy (unmodified!) of https://github.com/Manweill/swagger-axios-codegen

Manweill commented 4 years ago

i did not get it, what happen with you. If you don't understand the yarn link command, you can see this

fairking commented 4 years ago

if you use npm then please refer to npm link command described here: https://stackoverflow.com/a/18778516/1143349 I was linking the swagger-axios-codegen module to my project locally some time ago to debug some functionality. Everything was working so far.

azakordonets commented 4 years ago

I tried that all already and something is not right. I have updated my test project - https://github.com/azakordonets/test-axios-codegen . It contains both your library and test project where i try to generate client. This project had Dockerfile which contains clear steps. If you will clone this repo and then will run docker build -t axios-codegen-test . - you will see at the end code of generated client with a problem. Can you please try clone it and give it a try ? I'm banging my head here and can't figure out what is wrong with my setup.

azakordonets commented 4 years ago

@Manweill any chance you can spot the problem ?

Manweill commented 4 years ago

I don't know much about docker. Can you try it on your local machine instead of using docker.

azakordonets commented 4 years ago

Well, my initial problem was that locally i do get this error as well. I thought that maybe it's my environment setup, so i created repetitive isolated environment where this problem is 100% reproducible. So, it's not just my local environment, but something else. If you just run the command i have mentioned above - docker build -t axios-codegen-test . you will see the error i was mentioning. Maybe you can share with me your local setup of yarn, node and other tools you use to build this lib ? So that i could isolate some discrepancies between your and mine environment ?

azakordonets commented 4 years ago

Sorry, i just realized that https://github.com/azakordonets/test-axios-codegen repo had no code in the folders. Some weird git error from my side. I have pushed things again to the repo.

azakordonets commented 4 years ago

Ok, i just took another laptop and tried to reproduce without docker :

  1. Download https://github.com/azakordonets/test-axios-codegen
  2. cd swagger-axios-codegen
  3. yarn install
  4. cd ../test-axios-codegen
  5. yarn install
  6. cd swagger-axios-codegen
  7. yarn link
  8. cd ../test-axios-codegen
  9. yarn link "swagger-axios-codegen"
  10. node src/index.js
  11. cat cat service/service.index.ts As a result - broken generated client.

Can you please confirm that you can reproduce the same steps and outcome on your machine ?

fairking commented 4 years ago

Did you add swagger-axios-codegen package to your test-axios-codegen in package.json? I cannot see it. I never played with docker as well. What error exactly you get? I am not sure 100% but when I was linking the local package I wasn't removing it from packages.

azakordonets commented 4 years ago

@fairking because when you run yarn link "swagger-axios-codegen" then it creates a simlink in your node_modules to local package :

ls -la test-axios-codegen/node_modules
total 8
drwxr-xr-x  4 andrew  staff  128 Aug  5 10:11 .
drwxr-xr-x  8 andrew  staff  256 Aug  5 10:09 ..
-rw-r--r--  1 andrew  staff  237 Aug  5 10:08 .yarn-integrity
lrwxr-xr-x  1 andrew  staff   54 Aug  5 10:11 swagger-axios-codegen -> ../../../../../.config/yarn/link/swagger-axios-codegen
fairking commented 4 years ago

Yeah, but I assume the app needs to know threre is a package anyway. But I am using npm, so I am not sure.

azakordonets commented 4 years ago

@fairking package.json is to specify which packages to download to node_modules. Your code is using package code from node_modules

Manweill commented 4 years ago

can you redo without test-axios-codegen repo?

azakordonets commented 4 years ago

What do you mean @Manweill ? test-axios-codegen repo is the simpliest repo i created to reproduce the problem. I need some repo where i can add local version of swagger-axios-codegen to test my changes to your repo. The problem is that even with no changes into your repo i got this problem while using local version. Can you please provide some steps on how should i redo without test-axios-codegen ?

Manweill commented 4 years ago

There may be a problem with previous versions, you can try the latest swagger-axios-codegen source code.

azakordonets commented 4 years ago

@Manweill i was using master version - it's the latest one, right ? @Manweill , out of curiocity - can you reproduce the issue that i'm mentioning here ? Can you just follow the steps i wrote above and see if you will get the same result? If everything will work just fine on your machine, then it's clearly my setup issue. If you can reproduce this issue, then maybe you can also spot a problem that i'm missing ?

Manweill commented 4 years ago

no, it is not the latest.

azakordonets commented 4 years ago

@Manweill which branch is the latest then ?

azakordonets commented 4 years ago

Manweill i would love to try the latest version, but i don't know which one is it?

azakordonets commented 4 years ago

I just saw that you changed something in your library 10 days ago and gave a try with latest master again and the problem seem to disappear. Thanks for updating the library :)