Skip to content

Commit

Permalink
feat: use x-turso-organization header instead of path (#23)
Browse files Browse the repository at this point in the history
* feat: use x-turso-organization header instead of path

* docs: update readme

* chore: prelease tags

* chore: update workflow

* chore: cleanup
  • Loading branch information
notrab authored Jun 28, 2024
1 parent 9be4a82 commit e6acb5b
Show file tree
Hide file tree
Showing 13 changed files with 116 additions and 136 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ name: Release

on:
push:
branches:
- main
branches:
- main
- next

permissions:
contents: write
issues: write
pull-requests: write

jobs:
release:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
node_modules
dist
dist
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@
```ts
import { createClient } from "@tursodatabase/api";

// Personal account client
const turso = createClient({
org: "", // Your personal account or organization slug
token: "...",
});

// Organization account client
const turso = createClient({
org: "...", // Your organization slug
token: "...",
});
```
Expand Down Expand Up @@ -51,9 +57,9 @@ const token = await turso.groups.createToken("default", {
const token = await turso.groups.createToken("default", {
permissions: {
read_attach: {
databases: ["db1", "db2"]
}
}
databases: ["db1", "db2"],
},
},
});
const token = await turso.groups.rotateTokens("default");
```
Expand Down Expand Up @@ -112,9 +118,9 @@ const token = await turso.databases.createToken("my-db", {
const token = await turso.databases.createToken("my-db", {
permissions: {
read_attach: {
databases: ["db1", "db2"]
}
}
databases: ["db1", "db2"],
},
},
});
const token = await turso.databases.rotateTokens("my-db");

Expand Down
4 changes: 2 additions & 2 deletions release.config.cjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
branches: ["main"],
};
branches: ["main", { name: "next", prerelease: true }],
};
7 changes: 3 additions & 4 deletions src/api-token.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { TursoConfig } from "./config";
import { TursoClient } from "./client";
import { TursoClient, type TursoConfig } from "./client";

export interface ApiToken {
id: string;
Expand All @@ -24,7 +23,7 @@ export class ApiTokenClient {

async list(): Promise<ApiToken[]> {
const response = await TursoClient.request<{ tokens: ApiToken[] }>(
"auth/api-tokens",
`auth/api-tokens`,
this.config
);

Expand Down Expand Up @@ -60,7 +59,7 @@ export class ApiTokenClient {

async validate(token: string): Promise<ApiTokenValidation> {
const response = await TursoClient.request<{ exp: number }>(
"auth/api-tokens/validate",
`auth/api-tokens/validate`,
this.config,
{
headers: {
Expand Down
10 changes: 3 additions & 7 deletions src/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,20 @@ import { TursoClient, TursoClientError, createClient } from "./client";

describe("TursoClient", () => {
it("should throw an error if no API token is provided", () => {
const config = { org: "turso" };

// @ts-expect-error
expect(() => new TursoClient(config)).toThrow(
expect(() => new TursoClient({ org: "turso" })).toThrow(
"You must provide an API token"
);
});

it("should create an instance of TursoClient", () => {
const config = { org: "turso", token: "abc" };
const client = new TursoClient(config);
const client = new TursoClient({ org: "turso", token: "abc" });

expect(client).toBeInstanceOf(TursoClient);
});

it("should throw an error message that will match with API's error message", async () => {
const config = { org: "turso", token: "abc" };
const client = new TursoClient(config);
const client = new TursoClient({ org: "turso", token: "abc" });

const error = await client.databases
.get("databaseName")
Expand Down
39 changes: 37 additions & 2 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { TursoConfig } from "./config";
import { ApiTokenClient } from "./api-token";
import { OrganizationClient } from "./organization";
import { LocationClient } from "./location";
import { GroupClient } from "./group";
import { DatabaseClient } from "./database";

export const TURSO_API_URL = "https://api.turso.tech/v1/";

interface ApiErrorResponse {
error: string;
}
Expand All @@ -22,6 +23,26 @@ export class TursoClientError extends Error {
}
}

/**
* Configuration interface for Turso API client.
*/
export interface TursoConfig {
/**
* Organization identifier.
*/
org?: string;

/**
* API token for authentication.
*/
token: string;

/**
* Base URL for the API. Optional and defaults to "https://api.turso.tech/v1/".
*/
baseUrl?: string;
}

export class TursoClient {
private config: TursoConfig;
public apiTokens: ApiTokenClient;
Expand All @@ -36,7 +57,7 @@ export class TursoClient {
}

this.config = {
baseUrl: "https://api.turso.tech/v1/",
baseUrl: TURSO_API_URL,
...config,
};

Expand All @@ -47,6 +68,14 @@ export class TursoClient {
this.databases = new DatabaseClient(this.config);
}

/**
* Makes an API request.
* @param url - The endpoint URL.
* @param config - The Turso configuration.
* @param options - The request options.
* @returns The API response.
* @throws TursoClientError if the request fails.
*/
static async request<T>(
url: string,
config: TursoConfig,
Expand All @@ -58,6 +87,7 @@ export class TursoClient {
...options.headers,
Authorization: `Bearer ${config.token}`,
"User-Agent": "@tursodatabase/api",
...(config.org && { "x-turso-organization": config.org }),
},
});

Expand All @@ -75,6 +105,11 @@ export class TursoClient {
}
}

/**
* Creates a new TursoClient instance.
* @param config - The Turso configuration.
* @returns The TursoClient instance.
*/
export function createClient(config: TursoConfig): TursoClient {
return new TursoClient(config);
}
5 changes: 0 additions & 5 deletions src/config.ts

This file was deleted.

32 changes: 11 additions & 21 deletions src/database.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { TursoClient, type TursoConfig } from "./client";
import { LocationKeys } from "./location";
import { TursoConfig } from "./config";
import { TursoClient } from "./client";

export interface Database {
name: string;
Expand Down Expand Up @@ -101,15 +100,15 @@ export class DatabaseClient {
async list(): Promise<Database[]> {
const response = await TursoClient.request<{
databases: ApiDatabaseResponse[];
}>(`organizations/${this.config.org}/databases`, this.config);
}>(`databases`, this.config);

return (response.databases ?? []).map((db) => this.formatResponse(db));
}

async get(dbName: string): Promise<Database> {
const response = await TursoClient.request<{
database: ApiDatabaseResponse;
}>(`organizations/${this.config.org}/databases/${dbName}`, this.config);
}>(`databases/${dbName}`, this.config);

return this.formatResponse(response.database);
}
Expand Down Expand Up @@ -146,7 +145,7 @@ export class DatabaseClient {

const response = await TursoClient.request<{
database: ApiCreateDatabaseResponse;
}>(`organizations/${this.config.org}/databases`, this.config, {
}>(`databases`, this.config, {
method: "POST",
headers: {
"content-type": "application/json",
Expand All @@ -162,7 +161,7 @@ export class DatabaseClient {

async updateVersion(dbName: string): Promise<void> {
return await TursoClient.request(
`organizations/${this.config.org}/databases/${dbName}/update`,
`databases/${dbName}/update`,
this.config,
{
method: "POST",
Expand All @@ -172,7 +171,7 @@ export class DatabaseClient {

async delete(dbName: string) {
const response = await TursoClient.request<DeletedDatabase>(
`organizations/${this.config.org}/databases/${dbName}`,
`databases/${dbName}`,
this.config,
{
method: "DELETE",
Expand All @@ -185,10 +184,7 @@ export class DatabaseClient {
async listInstances(dbName: string): Promise<DatabaseInstance[]> {
const response = await TursoClient.request<{
instances: DatabaseInstance[];
}>(
`organizations/${this.config.org}/databases/${dbName}/instances`,
this.config
);
}>(`databases/${dbName}/instances`, this.config);

return response.instances ?? [];
}
Expand All @@ -199,10 +195,7 @@ export class DatabaseClient {
): Promise<DatabaseInstance> {
const response = await TursoClient.request<{
instance: DatabaseInstance;
}>(
`organizations/${this.config.org}/databases/${dbName}/instances/${instanceName}`,
this.config
);
}>(`databases/${dbName}/instances/${instanceName}`, this.config);

return response.instance ?? null;
}
Expand All @@ -228,7 +221,7 @@ export class DatabaseClient {
}

const response = await TursoClient.request<DatabaseToken>(
`organizations/${this.config.org}/databases/${dbName}/auth/tokens?${queryParams}`,
`databases/${dbName}/auth/tokens?${queryParams}`,
this.config,
{
method: "POST",
Expand All @@ -247,7 +240,7 @@ export class DatabaseClient {

async rotateTokens(dbName: string): Promise<void> {
return await TursoClient.request<void>(
`organizations/${this.config.org}/databases/${dbName}/auth/rotate`,
`databases/${dbName}/auth/rotate`,
this.config,
{
method: "POST",
Expand All @@ -273,10 +266,7 @@ export class DatabaseClient {
database: DatabaseUsage;
instances: InstanceUsages;
total: TotalUsage;
}>(
`organizations/${this.config.org}/databases/${dbName}/usage?${queryParams}`,
this.config
);
}>(`databases/${dbName}/usage?${queryParams}`, this.config);

return response.database;
}
Expand Down
Loading

0 comments on commit e6acb5b

Please sign in to comment.