@proofgeist/fmdapi
Version:
FileMaker Data API client
96 lines (86 loc) • 3.05 kB
text/typescript
import { FileMakerError } from "../index.js";
import memoryStore from "../tokenStore/memory.js";
import type { TokenStoreDefinitions } from "../tokenStore/types.js";
import type {
BaseFetchAdapterOptions,
GetTokenArguments,
} from "./fetch-base-types.js";
import { BaseFetchAdapter } from "./fetch-base.js";
export interface FetchAdapterOptions extends BaseFetchAdapterOptions {
auth: {
username: string;
password: string;
};
tokenStore?: TokenStoreDefinitions;
}
export class FetchAdapter extends BaseFetchAdapter {
private username: string;
private password: string;
private tokenStore: Omit<TokenStoreDefinitions, "getKey">;
private getTokenKey: Required<TokenStoreDefinitions>["getKey"];
constructor(args: FetchAdapterOptions) {
super({ ...args, refreshToken: true });
this.username = args.auth.username;
this.password = args.auth.password;
this.tokenStore = args.tokenStore ?? memoryStore();
this.getTokenKey =
args.tokenStore?.getKey ?? (() => `${args.server}/${args.db}`);
if (this.username === "") throw new Error("Username is required");
if (this.password === "") throw new Error("Password is required");
}
/**
* Gets a FileMaker Data API token for authentication.
*
* This token is **NOT** guaranteed to be valid, since it expires 15 minutes after the last use. Pass `refresh=true` to forcibly get a fresh token
*
* @param args.refresh - If true, forces getting a new token instead of using cached token
* @internal This method is intended for internal use, you should not need to use it in most cases.
*/
public override getToken = async (
args?: GetTokenArguments,
): Promise<string> => {
const { refresh = false } = args ?? {};
let token: string | null = null;
if (!refresh) {
token = await this.tokenStore.getToken(this.getTokenKey());
}
if (!token) {
const res = await fetch(`${this.baseUrl}/sessions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${Buffer.from(
`${this.username}:${this.password}`,
).toString("base64")}`,
},
});
if (!res.ok) {
const data = await res.json();
throw new FileMakerError(
data.messages[0].code,
data.messages[0].message,
);
}
token = res.headers.get("X-FM-Data-Access-Token");
if (!token) throw new Error("Could not get token");
this.tokenStore.setToken(this.getTokenKey(), token);
}
return token;
};
public disconnect = async (): Promise<void> => {
const token = await this.tokenStore.getToken(this.getTokenKey());
if (token) {
await this.request({
url: `/sessions/${token}`,
method: "DELETE",
fetchOptions: {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
},
});
this.tokenStore.clearToken(this.getTokenKey());
}
};
}