Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why bro? TypeScript weirdness and OpenAPI generation #369

Open
Arlen22 opened this issue Sep 30, 2024 · 2 comments
Open

Why bro? TypeScript weirdness and OpenAPI generation #369

Arlen22 opened this issue Sep 30, 2024 · 2 comments

Comments

@Arlen22
Copy link

Arlen22 commented Sep 30, 2024

My history of working with DocuSign APIs in Typescript goes like this:

Yay! They have a node SDK. Why doesn't my DocuSign library work with webpack? Oh well, my project is only partly webpack, so just install it as an external dependency and it works fine. Now let me figure out how to get a JWT user token and make a client request.

Wait, why do they rename the response property? Now I have to override the response object. And I also have to inspect the options manually because the definitely typed library doesn't include the request and response types for random parameters.

import * as docusign from 'docusign-esign';
/** DOCUSIGN!!! WHY??? */
interface DocusignResponseHORROR<T> extends AxiosResponse<T> {
  data: never;
  body: AxiosResponse<T>["data"];
}
const client = new docusign.ApiClient();
const token: DocusignResponseHORROR<TokenInfo> = await client.requestJWTUserToken(...);
client.setBasePath(this.env.DOCUSIGN_BASEPATH);
client.addDefaultHeader('Authorization', 'Bearer ' + token.body.access_token);
const result = await new docusign.EnvelopesApi(client).createEnvelope(accountID, {
  // inspect EnvelopesAPI source code to determine parameters
});

Seriously? Why do I have to constantly inspect source code? Especially when I have to navigate to the source code manually because I'm using @types/docusign-esign and can't just ctrl-click? Wait a minute, what's this about it being manually generated? Oh, it's based on OpenAPI... and... I can generate it myself? Let me do that! Surely then none of this weirdness will happen.

Oh nice, I found @hey-api/openapi-ts.

const res = await Envelopes_PostEnvelopes({
  baseUrl: this.env.DOCUSIGN_BASEPATH,
  headers: { 'Authorization': 'Bearer ' + await this.getDocusignToken() },
  path: { accountId: this.env.DOCUSIGN_JWT_AccountId },
  query: { },
  body: envelope,
});

Wait, why am I getting type errors? After more digging through source code that I had to manually navigate, I figured out that the builder was trying to declare types verbatim without checking their type names and thus trying to declare primitive types. So I make a custom generator file which renames all the schema definitions.

import { createClient } from '@hey-api/openapi-ts';
import { resolve } from 'path';
import { existsSync,readFileSync } from "fs";
const file = "/root/docusign/OpenAPI-Specifications/esignature.rest.swagger-v2.1.json";
if (!existsSync(file)) {
  throw new Error(`File not found: ${file}`);
}


// DOCUSIGN!!! SERIOUSLY!!!
// because the definitions are directly built as types and because you can't override primitive types (`export type number = ...`), I have to rename the definitions.
const input = JSON.parse(readFileSync(file, 'utf8'), (key, value) => {
  if (key === "$ref" && value.startsWith("#/definitions/")) {
    return value.replace("#/definitions/", "#/definitions/def_");
  } else {
    return value;
  }
});
Object.entries(input.definitions).forEach(([key, value]) => {
  input.definitions[`def_${key}`] = value;
  delete input.definitions[key];
});

createClient({
  input,
  output: process.argv[2] || "docusign-client",
  client: '@hey-api/client-fetch',
  services: {
    methodNameBuilder(operation) {
      console.log(operation["x-ds-methodname"] || operation.id);
      return operation["x-ds-methodname"] || operation.id;
    },
  },
});

Great! Now I have a properly working third party library to access the DocuSign API.

Does everyone do this?

@sonawane-sanket
Copy link
Contributor

@Arlen22
Thank you for using the DocuSign SDK, and we regret the issues you're facing and would like to address them:

  1. Response Structure: While we use Axios internally, our response structure differs from the standard Axios response. This was done to avoid disruption from our previous HTTP client, SuperAgent (Due to unresolved security vulnerabilities, we switched to Axios). By defining our own structure, we ensure backward compatibility and avoid impacting existing integrations.
  2. @types/docusign-esign: The @types/docusign-esign package is not managed by DocuSign, but by a third party. Our SDKs are JavaScript-based, not TypeScript, so we understand the difficulty in finding definitions. We're actively working on improving SDKs and may offer more type-safe versions in the future.
  3. Using Our SDK: We recommend using our SDK over creating your own via OpenAPI, as it offers a more streamlined integration experience.

Thanks again for highlighting these issues, which helps us improve our SDKs.

@Arlen22
Copy link
Author

Arlen22 commented Jan 10, 2025

Using Our SDK: We recommend using our SDK over creating your own via OpenAPI, as it offers a more streamlined integration experience.

It was not streamlined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants