Skip to content

Commit

Permalink
Update README.
Browse files Browse the repository at this point in the history
Add JSDoc comments.
Modfiy visibility of some properties.
Remove unused interfaces.
  • Loading branch information
envis10n committed May 26, 2021
1 parent 6e3cdd0 commit 86be933
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 12 deletions.
109 changes: 108 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,110 @@
[![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/darango/mod.ts)

# DArango

An ArangoDB driver for Deno.
An ArangoDB driver for Deno.

```ts
import { Arango } from "https://deno.land/x/darango/mod.ts";
```

## Features
- [x] Collection create, get, and truncate.
- [x] Document get, find, update, delete.
- [x] AQL query support, returning the result set as a specific generic type.
- [x] AQL query on a collection, returning results as Documents.

*More planned for future releases.*

## Usage
Here are some examples for getting a client instance.

### Authenticating
#### Basic Auth
```ts
import { Arango } from "https://deno.land/x/darango/mod.ts";

// Connect and obtain a JWT token from the server by providing basic auth details.
const arango = await Arango.basicAuth({ uri: "http://localhost:8529/_db/some_db", username: "arango", password: "arango" });
```
#### JWT Auth
```ts
import { Arango } from "https://deno.land/x/darango/mod.ts";

// Connect and test that the token works by checking for DBs the token can access.
const arango = await Arango.jwtAuth({ uri: "http://localhost:8529/_db/some_db", jwt: "JWT Token Here" });
```
### Getting a Collection
```ts
// Some dummy interface to use as the document structure.
interface TestType {
code: number;
text: string;
}

const collection = await arango.collection<TestType>("test");

// OR

const collection = await arango.createCollection<TestType>("test");
```
The generic type provided for `Collection<T>` will be used as the document type. `Document<T>` is a joined type including `_id`, `_key`, and `_rev` fields, along with your own interface's fields.
### Modifying a Document
```ts
// Some dummy interface to use as the document structure.
interface TestType {
code: number;
text: string;
}

const collection = await arango.collection<TestType>("test");

const doc = await collection.get("documentkey");

// Update document's fields.
doc.code = 200;
doc.text = "Hello, world!";

// Update document on the server. After this call, the _rev field will be updated on the object to match the new revision.
await doc.update();

// Delete the document. Do not use it after this, since the key will no longer exist on the server.
await doc.delete();
```
### Running an AQL query
#### Query with custom type definition
```ts
// Some dummy interface to use as the document structure.
interface TestType {
code: number;
text: string;
}

const cursor = await arango.query<TestType>("FOR d IN test RETURN d");

for await (const docs of cursor) {
// Each loop here will continue calling the cursor, until it is exhausted on the server side.
for (const doc of docs) {
// Document data available, but cannot be directly manipulated and updated on the server.
console.log(doc.code); // Log the code
}
}
```
#### Query from collection object
```ts
// Some dummy interface to use as the document structure.
interface TestType {
code: number;
text: string;
}

const collection = await arango.collection<TestType>("test");

// This is mainly a helper that is calling arango.query<TestType>, but also transforms the results set to contain actual Document<T> objects.
const results = await collection.query("FOR d IN test RETURN d");
for (const doc of results) {
// Document data available as if you had called collection.get("key")
doc.code = 200;
await doc.update();
}
```
48 changes: 41 additions & 7 deletions collection.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,41 @@
import { axiod } from "./deps.ts";
import { ArangoCursor, CursorOptions } from "./cursor.ts";

/**
* Define Document methods.
*/
export interface DocumentBase {
update(): Promise<void>;
delete(): Promise<boolean>;
}

/**
* Join DocumentData<T> and DocumentBase
*/
export type Document<T> =
& DocumentData<T>
& DocumentBase;

/**
* Document ID fields
*/
export type DocumentID = { _id: string; _key: string; _rev: string };
export type DocumentData<T> = T & DocumentID;

export type DocumentFilter<T> = {
[Property in keyof DocumentData<T>]: string | DocumentData<T>[Property];
};
/**
* Document Data and ID fields.
*/
export type DocumentData<T> = T & DocumentID;

/**
* An object representing a collection on the ArangoDB server.
*/
export class Collection<T> {
constructor(private ax: typeof axiod, public readonly name: string) {
//
}
constructor(private ax: typeof axiod, public readonly name: string) {}
/**
* Get a document directly by its key
* @param key The document key
* @returns The document specified by the key.
*/
public async get(key: string): Promise<Document<T>> {
const res = await this.ax.get(`/_api/document/${this.name}/${key}`);
if (res.status != 200) {
Expand Down Expand Up @@ -65,6 +80,11 @@ export class Collection<T> {
},
});
}
/**
* Create a new document in the collection.
* @param data The initial document data.
* @returns The document, as it exists on the server.
*/
public async create(data: T): Promise<Document<T>> {
const res = await this.ax.post(`/_api/document/${this.name}`, data);
switch (res.status) {
Expand All @@ -79,6 +99,11 @@ export class Collection<T> {
}
}
}
/**
* Find multiple documents matching the filter provided.
* @param filter Partial document data to filter results by.
* @returns Documents matching the filter provided.
*/
public async find(
filter: Partial<DocumentData<T>>,
): Promise<Document<T>[]> {
Expand All @@ -95,13 +120,22 @@ export class Collection<T> {
}\n\tRETURN doc`;
return await this.query(query);
}
/**
* Run a query, returning the results as Document objects.
* @param aql The query string to execute.
* @param options Cursor options.
* @returns The collected results of the query, converted to Document objects.
*/
public async query(
aql: string,
options?: CursorOptions,
): Promise<Document<T>[]> {
const res = await new ArangoCursor<T>(this.ax, aql, options).collect();
return res.map((d) => this.makeDocument(d));
}
/**
* Truncate the collection.
*/
public async truncate(): Promise<void> {
const res = await this.ax.put(`/_api/collection/${this.name}/truncate`);
if (res.data.error) {
Expand Down
17 changes: 15 additions & 2 deletions cursor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { axiod } from "./deps.ts";
import { DocumentData } from "./collection.ts";

/**
* A Cursor object from the server.
*/
export interface Cursor<T> {
result: DocumentData<T>[];
id: string;
Expand All @@ -10,15 +13,21 @@ export interface Cursor<T> {
count: number;
}

/**
* Options for a Cursor.
*/
export interface CursorOptions {
count?: boolean;
batchSize?: number;
}

/**
* A Cursor object capable of executing a query and returning the results sets.
*/
export class ArangoCursor<T> implements AsyncIterable<DocumentData<T>[]> {
id: string | null;
private id: string | null;
private options: CursorOptions;
hasMore: boolean;
private hasMore: boolean;
constructor(
private readonly ax: typeof axiod,
public readonly query: string,
Expand All @@ -32,6 +41,10 @@ export class ArangoCursor<T> implements AsyncIterable<DocumentData<T>[]> {
private getOptions(): { query: string } & CursorOptions {
return Object.assign({}, this.options, { query: this.query });
}
/**
* Collect all of the results sets and return them as a single array.
* @returns All of the results sets from this query flattened into a 1D array.
*/
public async collect(): Promise<DocumentData<T>[]> {
const res: DocumentData<T>[] = [];
for await (const docs of this) {
Expand Down
43 changes: 41 additions & 2 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,36 @@ import { axiod } from "./deps.ts";
import { Collection } from "./collection.ts";
import { ArangoCursor, CursorOptions } from "./cursor.ts";

/**
* Base options required.
*/
interface ArangoOptionsBase {
uri: string;
}

/**
* Basic Auth options.
*/
interface ArangoOptionsBasicAuth extends ArangoOptionsBase {
username: string;
password: string;
}

/**
* JWT Auth options.
*/
interface ArangoOptionsJWT extends ArangoOptionsBase {
jwt: string;
}

/**
* The ArangoDB client.
*/
export class Arango {
public ax: typeof axiod;
private ax: typeof axiod;
constructor(
private readonly uri: string,
public jwt: string,
private jwt: string,
) {
this.ax = axiod.create({
"baseURL": this.uri,
Expand All @@ -28,6 +40,11 @@ export class Arango {
},
});
}
/**
* Create a new Arango instance by first obtaining a valid JWT token.
* @param options Options required for Basic Auth.
* @returns A new Arango instance.
*/
public static async basicAuth(
options: ArangoOptionsBasicAuth,
): Promise<Arango> {
Expand All @@ -42,6 +59,11 @@ export class Arango {
}
return new Arango(options.uri, res.data.jwt);
}
/**
* Create a new Arango instance using a previously obtained JWT token, validating it first.
* @param options Options required for JWT Auth.
* @returns A new Arango instance.
*/
public static async jwtAuth(options: ArangoOptionsJWT): Promise<Arango> {
const res = await axiod({
"baseURL": options.uri,
Expand All @@ -56,14 +78,31 @@ export class Arango {
}
return new Arango(options.uri, options.jwt);
}
/**
* Create a new Cursor to execute a query. NOTE: Data returned by this cannot be turned into a Document object for direct modification.
* @param aql The query string to be executed.
* @param options Cursor options.
* @returns A new ArangoCursor that can be asynchronously iterated or collected.
*/
public query<T>(aql: string, options?: CursorOptions): ArangoCursor<T> {
return new ArangoCursor(this.ax, aql, options);
}
/**
* Get an existing collection by name.
* @param name The name of the collection to get.
* @returns A Collection instance.
*/
public async collection<T>(name: string): Promise<Collection<T>> {
const res = await this.ax.get(`/_api/collection/${name}`);
if (res.status != 200) throw new Error("Unable to find collection.");
return new Collection(this.ax, name);
}
/**
*
* @param name The name of the collection.
* @param edge Optional. Make this an edge collection.
* @returns The new collection as a Collection instance.
*/
public async createCollection<T>(
name: string,
edge?: boolean,
Expand Down

0 comments on commit 86be933

Please sign in to comment.