mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-25 10:20:03 -06:00
feat: adds vercel style guide in API package (#2861)
This commit is contained in:
@@ -2,11 +2,11 @@ import { NextApiResponse } from "next";
|
||||
|
||||
export type ApiResponse = ApiSuccessResponse | ApiErrorResponse;
|
||||
|
||||
export interface ApiSuccessResponse<T = { [key: string]: any }> {
|
||||
interface ApiSuccessResponse<T = { [key: string]: unknown }> {
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface ApiErrorResponse {
|
||||
interface ApiErrorResponse {
|
||||
code:
|
||||
| "not_found"
|
||||
| "gone"
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ["@formbricks/eslint-config/legacy-library.js"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
extends: ["@formbricks/eslint-config/library.js"],
|
||||
parserOptions: {
|
||||
project: "tsconfig.json",
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TActionInput } from "@formbricks/types/actions";
|
||||
import { Result } from "@formbricks/types/errorHandlers";
|
||||
import { NetworkError } from "@formbricks/types/errors";
|
||||
import { makeRequest } from "../../utils/makeRequest";
|
||||
import { type TActionInput } from "@formbricks/types/actions";
|
||||
import { type Result } from "@formbricks/types/errorHandlers";
|
||||
import { type NetworkError } from "@formbricks/types/errors";
|
||||
import { makeRequest } from "../../utils/make-request";
|
||||
|
||||
export class ActionAPI {
|
||||
private apiHost: string;
|
||||
@@ -12,7 +12,9 @@ export class ActionAPI {
|
||||
this.environmentId = environmentId;
|
||||
}
|
||||
|
||||
async create(actionInput: Omit<TActionInput, "environmentId">): Promise<Result<{}, NetworkError | Error>> {
|
||||
async create(
|
||||
actionInput: Omit<TActionInput, "environmentId">
|
||||
): Promise<Result<object, NetworkError | Error>> {
|
||||
return makeRequest(this.apiHost, `/api/v1/client/${this.environmentId}/actions`, "POST", actionInput);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TAttributeUpdateInput } from "@formbricks/types/attributes";
|
||||
import { Result } from "@formbricks/types/errorHandlers";
|
||||
import { NetworkError } from "@formbricks/types/errors";
|
||||
import { makeRequest } from "../../utils/makeRequest";
|
||||
import { type TAttributeUpdateInput } from "@formbricks/types/attributes";
|
||||
import { type Result } from "@formbricks/types/errorHandlers";
|
||||
import { type NetworkError } from "@formbricks/types/errors";
|
||||
import { makeRequest } from "../../utils/make-request";
|
||||
|
||||
export class AttributeAPI {
|
||||
private apiHost: string;
|
||||
@@ -16,7 +16,7 @@ export class AttributeAPI {
|
||||
attributeUpdateInput: Omit<TAttributeUpdateInput, "environmentId">
|
||||
): Promise<Result<{ changed: boolean; message: string }, NetworkError | Error>> {
|
||||
// transform all attributes to string if attributes are present into a new attributes copy
|
||||
const attributes: { [key: string]: string } = {};
|
||||
const attributes: Record<string, string> = {};
|
||||
for (const key in attributeUpdateInput.attributes) {
|
||||
attributes[key] = String(attributeUpdateInput.attributes[key]);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TDisplayCreateInput, TDisplayUpdateInput } from "@formbricks/types/displays";
|
||||
import { Result } from "@formbricks/types/errorHandlers";
|
||||
import { NetworkError } from "@formbricks/types/errors";
|
||||
import { makeRequest } from "../../utils/makeRequest";
|
||||
import { type TDisplayCreateInput, type TDisplayUpdateInput } from "@formbricks/types/displays";
|
||||
import { type Result } from "@formbricks/types/errorHandlers";
|
||||
import { type NetworkError } from "@formbricks/types/errors";
|
||||
import { makeRequest } from "../../utils/make-request";
|
||||
|
||||
export class DisplayAPI {
|
||||
private apiHost: string;
|
||||
@@ -21,7 +21,7 @@ export class DisplayAPI {
|
||||
async update(
|
||||
displayId: string,
|
||||
displayInput: Omit<TDisplayUpdateInput, "environmentId">
|
||||
): Promise<Result<{}, NetworkError | Error>> {
|
||||
): Promise<Result<object, NetworkError | Error>> {
|
||||
return makeRequest(
|
||||
this.apiHost,
|
||||
`/api/v1/client/${this.environmentId}/displays/${displayId}`,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApiConfig } from "../../types";
|
||||
import { type ApiConfig } from "../../types";
|
||||
import { ActionAPI } from "./action";
|
||||
import { AttributeAPI } from "./attribute";
|
||||
import { DisplayAPI } from "./display";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Result } from "@formbricks/types/errorHandlers";
|
||||
import { NetworkError } from "@formbricks/types/errors";
|
||||
import { makeRequest } from "../../utils/makeRequest";
|
||||
import { type Result } from "@formbricks/types/errorHandlers";
|
||||
import { type NetworkError } from "@formbricks/types/errors";
|
||||
import { makeRequest } from "../../utils/make-request";
|
||||
|
||||
export class PeopleAPI {
|
||||
private apiHost: string;
|
||||
@@ -11,7 +11,7 @@ export class PeopleAPI {
|
||||
this.environmentId = environmentId;
|
||||
}
|
||||
|
||||
async create(userId: string): Promise<Result<{}, NetworkError | Error>> {
|
||||
async create(userId: string): Promise<Result<{ userId: string }, NetworkError | Error>> {
|
||||
return makeRequest(this.apiHost, `/api/v1/client/${this.environmentId}/people`, "POST", {
|
||||
environmentId: this.environmentId,
|
||||
userId,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Result } from "@formbricks/types/errorHandlers";
|
||||
import { NetworkError } from "@formbricks/types/errors";
|
||||
import { TResponseInput, TResponseUpdateInput } from "@formbricks/types/responses";
|
||||
import { makeRequest } from "../../utils/makeRequest";
|
||||
import { type Result } from "@formbricks/types/errorHandlers";
|
||||
import { type NetworkError } from "@formbricks/types/errors";
|
||||
import { type TResponseInput, type TResponseUpdateInput } from "@formbricks/types/responses";
|
||||
import { makeRequest } from "../../utils/make-request";
|
||||
|
||||
type TResponseUpdateInputWithResponseId = TResponseUpdateInput & { responseId: string };
|
||||
|
||||
@@ -26,7 +26,7 @@ export class ResponseAPI {
|
||||
data,
|
||||
ttc,
|
||||
language,
|
||||
}: TResponseUpdateInputWithResponseId): Promise<Result<{}, NetworkError | Error>> {
|
||||
}: TResponseUpdateInputWithResponseId): Promise<Result<object, NetworkError | Error>> {
|
||||
return makeRequest(this.apiHost, `/api/v1/client/${this.environmentId}/responses/${responseId}`, "PUT", {
|
||||
finished,
|
||||
data,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TUploadFileConfig } from "@formbricks/types/storage";
|
||||
import type { TUploadFileConfig, TUploadFileResponse } from "@formbricks/types/storage";
|
||||
|
||||
export class StorageAPI {
|
||||
private apiHost: string;
|
||||
@@ -33,12 +33,13 @@ export class StorageAPI {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Upload failed with status: ${response.status}`);
|
||||
throw new Error(`Upload failed with status: ${String(response.status)}`);
|
||||
}
|
||||
|
||||
const json = await response.json();
|
||||
const json = (await response.json()) as TUploadFileResponse;
|
||||
|
||||
const { data } = json;
|
||||
|
||||
const { signedUrl, fileUrl, signingData, presignedFields, updatedFileName } = data;
|
||||
|
||||
let requestHeaders: Record<string, string> = {};
|
||||
@@ -76,7 +77,7 @@ export class StorageAPI {
|
||||
if (!uploadResponse.ok) {
|
||||
// if local storage is used, we'll use the json response:
|
||||
if (signingData) {
|
||||
const uploadJson = await uploadResponse.json();
|
||||
const uploadJson = (await uploadResponse.json()) as { message: string };
|
||||
const error = new Error(uploadJson.message);
|
||||
error.name = "FileTooLargeError";
|
||||
throw error;
|
||||
@@ -84,13 +85,13 @@ export class StorageAPI {
|
||||
|
||||
// if s3 is used, we'll use the text response:
|
||||
const errorText = await uploadResponse.text();
|
||||
if (presignedFields && errorText && errorText.includes("EntityTooLarge")) {
|
||||
if (presignedFields && errorText.includes("EntityTooLarge")) {
|
||||
const error = new Error("File size exceeds the size limit for your plan");
|
||||
error.name = "FileTooLargeError";
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new Error(`Upload failed with status: ${uploadResponse.status}`);
|
||||
throw new Error(`Upload failed with status: ${String(uploadResponse.status)}`);
|
||||
}
|
||||
|
||||
return fileUrl;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Client } from "./api/client";
|
||||
import { ApiConfig } from "./types/index";
|
||||
import { type ApiConfig } from "./types/index";
|
||||
|
||||
export class FormbricksAPI {
|
||||
client: Client;
|
||||
|
||||
@@ -3,6 +3,22 @@ export interface ApiConfig {
|
||||
apiHost: string;
|
||||
}
|
||||
|
||||
export type ApiResponse<T> = {
|
||||
export type ApiResponse = ApiSuccessResponse | ApiErrorResponse;
|
||||
|
||||
export interface ApiSuccessResponse<T = Record<string, unknown>> {
|
||||
data: T;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ApiErrorResponse {
|
||||
code:
|
||||
| "not_found"
|
||||
| "gone"
|
||||
| "bad_request"
|
||||
| "internal_server_error"
|
||||
| "unauthorized"
|
||||
| "method_not_allowed"
|
||||
| "not_authenticated"
|
||||
| "forbidden";
|
||||
message: string;
|
||||
details: Record<string, string | string[] | number | number[] | boolean | boolean[]>;
|
||||
}
|
||||
|
||||
40
packages/api/src/utils/make-request.ts
Normal file
40
packages/api/src/utils/make-request.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { type Result, err, ok, wrapThrowsAsync } from "@formbricks/types/errorHandlers";
|
||||
import { type NetworkError } from "@formbricks/types/errors";
|
||||
import type { ApiErrorResponse, ApiResponse, ApiSuccessResponse } from "../types";
|
||||
|
||||
export const makeRequest = async <T>(
|
||||
apiHost: string,
|
||||
endpoint: string,
|
||||
method: "GET" | "POST" | "PUT" | "DELETE",
|
||||
data?: unknown
|
||||
): Promise<Result<T, NetworkError | Error>> => {
|
||||
const url = new URL(apiHost + endpoint);
|
||||
const body = data ? JSON.stringify(data) : undefined;
|
||||
|
||||
const res = await wrapThrowsAsync(fetch)(url.toString(), {
|
||||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
if (!res.ok) return err(res.error);
|
||||
|
||||
const response = res.data;
|
||||
const json = (await response.json()) as ApiResponse;
|
||||
|
||||
if (!response.ok) {
|
||||
const errorResponse = json as ApiErrorResponse;
|
||||
return err({
|
||||
code: "network_error",
|
||||
status: response.status,
|
||||
message: errorResponse.message || "Something went wrong",
|
||||
url,
|
||||
...(Object.keys(errorResponse.details).length > 0 && { details: errorResponse.details }),
|
||||
});
|
||||
}
|
||||
|
||||
const successResponse = json as ApiSuccessResponse<T>;
|
||||
return ok(successResponse.data);
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Result, err, ok, wrapThrows } from "@formbricks/types/errorHandlers";
|
||||
import { NetworkError } from "@formbricks/types/errors";
|
||||
|
||||
export const makeRequest = async <T>(
|
||||
apiHost: string,
|
||||
endpoint: string,
|
||||
method: "GET" | "POST" | "PUT" | "DELETE",
|
||||
data?: any
|
||||
): Promise<Result<T, NetworkError | Error>> => {
|
||||
const url = new URL(apiHost + endpoint);
|
||||
const body = JSON.stringify(data);
|
||||
|
||||
const res = wrapThrows(fetch)(url.toString(), {
|
||||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body,
|
||||
});
|
||||
if (res.ok === false) return err(res.error);
|
||||
|
||||
const response = await res.data;
|
||||
const json = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
return err({
|
||||
code: "network_error",
|
||||
status: response.status,
|
||||
message: json.message || "Something went wrong",
|
||||
url,
|
||||
...(json.details && { details: json.details }),
|
||||
});
|
||||
}
|
||||
|
||||
return ok(json.data as T);
|
||||
};
|
||||
@@ -30,5 +30,5 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
},
|
||||
ignorePatterns: ["node_modules/", "dist/"],
|
||||
ignorePatterns: ["node_modules/", "dist/", "*.config.js"],
|
||||
};
|
||||
|
||||
7
packages/types/.eslintrc.cjs
Normal file
7
packages/types/.eslintrc.cjs
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ["@formbricks/eslint-config/library.js"],
|
||||
parserOptions: {
|
||||
project: "tsconfig.json",
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
};
|
||||
@@ -15,3 +15,21 @@ export const ZUploadFileConfig = z.object({
|
||||
});
|
||||
|
||||
export type TUploadFileConfig = z.infer<typeof ZUploadFileConfig>;
|
||||
|
||||
const ZUploadFileResponse = z.object({
|
||||
data: z.object({
|
||||
signedUrl: z.string(),
|
||||
fileUrl: z.string(),
|
||||
signingData: z
|
||||
.object({
|
||||
signature: z.string(),
|
||||
timestamp: z.number(),
|
||||
uuid: z.string(),
|
||||
})
|
||||
.nullable(),
|
||||
presignedFields: z.record(z.string()).optional(),
|
||||
updatedFileName: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type TUploadFileResponse = z.infer<typeof ZUploadFileResponse>;
|
||||
|
||||
Reference in New Issue
Block a user