diff --git a/README.md b/README.md index f131db8..ea53bef 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > **atlas_sdk** is a TypeSafe > [MongoDB Atlas Data API](https://www.mongodb.com/docs/atlas/api/data-api/#introduction) -> SDK for Deno & Deno Deploy +> SDK for Deno & Deno Deploy & Web [![Discord server](https://img.shields.io/discord/768918486575480863?color=blue&label=Ask%20for%20help%20here&logo=discord&style=flat-square)](https://discord.gg/HEdTCvZUSf) @@ -10,6 +10,10 @@ - [Docs](https://doc.deno.land/https/deno.land/x/atlas_sdk/mod.ts) +## Permissions + +This module needs `net` permission. use `deno run --allow-net` command + ### Import Replace `LATEST_VERSION` with @@ -22,20 +26,54 @@ import { } from "https://deno.land/x/atlas_sdk@LATEST_VERSION/mod.ts"; ``` -### Connect +### Constructor + +#### Authenticate via email and password + +[Documentation](https://www.mongodb.com/docs/atlas/app-services/authentication/email-password/#std-label-email-password-authentication) + +```ts +const client = new MongoClient({ + endpoint: "https://data.mongodb-api.com/app/YOUR_APP_ID/endpoint/data/v1", + dataSource: "YOUR_CLUSTER_NAME", // e.g. "Cluster0" + auth: { + email: "YOUR_EMAIL", + password: "YOUR_PASSWORD", + }, +}); +``` + +#### Authenticate via api-key + +[Documentation](https://www.mongodb.com/docs/atlas/app-services/authentication/api-key/#std-label-api-key-authentication) + +```ts +const client = new MongoClient({ + endpoint: "https://data.mongodb-api.com/app/YOUR_APP_ID/endpoint/data/v1", + dataSource: "YOUR_CLUSTER_NAME", // e.g. "Cluster0" + auth: { + apiKey: "YOUR_API_KEY", + }, +}); +``` + +#### Authenticate via custom JWT + +[Documentation](https://www.mongodb.com/docs/atlas/app-services/authentication/custom-jwt/#std-label-custom-jwt-authentication) ```ts const client = new MongoClient({ - appId: "YOUR_APP_ID", // e.g. "data-omosm" + endpoint: "https://data.mongodb-api.com/app/YOUR_APP_ID/endpoint/data/v1", dataSource: "YOUR_CLUSTER_NAME", // e.g. "Cluster0" - apiKey: "YOUR_API_KEY", + auth: { + jwtTokenString: "YOUR_JWT", + }, }); ``` -### Access Collection +### Define Schema Type ```ts -// Defining schema interface interface UserSchema { _id: ObjectId; username: string; @@ -48,45 +86,57 @@ const users = db.collection("users"); ### Insert +#### insertOne + ```ts const insertId = await users.insertOne({ username: "user1", password: "pass1", }); - -const insertIds = await users.insertMany([ - { - username: "user1", - password: "pass1", - }, - { - username: "user2", - password: "pass2", - }, -]); ``` -### Find +#### insertMany ```ts -const user1 = await users.findOne({ _id: insertId }); +const insertIds = await users.insertMany([{ + username: "user1", + password: "pass1", +}, { + username: "user2", + password: "pass2", +}]); +``` + +### Find -const all_users = await users.find({ username: { $ne: null } }); +#### findOne -// find by ObjectId +```ts const user1_id = await users.findOne({ _id: new ObjectId("SOME OBJECTID STRING"), }); ``` +#### find + +```ts +const allActiveUsers = await users.find({ active: true }); +``` + ### Count +#### countDocuments + ```ts -const count = await users.countDocuments({ username: { $ne: null } }); +// count of all active users +const count = await users.countDocuments({ active: true }); +``` -const estimatedCount = await users.estimatedDocumentCount({ - username: { $ne: null }, -}); +#### estimatedDocumentCount + +```ts +// estimated count of all active users +const estimatedCount = await users.estimatedDocumentCount({ active: true }); ``` ### Aggregation @@ -100,12 +150,18 @@ const docs = await users.aggregate([ ### Update +#### updateOne + ```ts const { matchedCount, modifiedCount, upsertedId } = await users.updateOne( { username: { $ne: null } }, { $set: { username: "USERNAME" } }, ); +``` + +#### updateMany +```ts const { matchedCount, modifiedCount, upsertedId } = await users.updateMany( { username: { $ne: null } }, { $set: { username: "USERNAME" } }, @@ -126,8 +182,14 @@ const { matchedCount, modifiedCount, upsertedId } = await users.replaceOne( ### Delete +#### deleteOne + ```ts const deleteCount = await users.deleteOne({ _id: insertId }); +``` + +#### deleteMany -const deleteCount2 = await users.deleteMany({ username: "test" }); +```ts +const deleteCount = await users.deleteMany({ username: "test" }); ``` diff --git a/auth_types.d.ts b/auth_types.d.ts new file mode 100644 index 0000000..decf47a --- /dev/null +++ b/auth_types.d.ts @@ -0,0 +1,17 @@ +export interface EmailPasswordAuthOptions { + email: string; + password: string; +} + +export interface ApiKeyAuthOptions { + apiKey: string; +} + +export interface CustomJwtAuthOptions { + jwtTokenString: string; +} + +export type AuthOptions = + | EmailPasswordAuthOptions + | ApiKeyAuthOptions + | CustomJwtAuthOptions; diff --git a/client.ts b/client.ts index 0c26d94..a420e73 100644 --- a/client.ts +++ b/client.ts @@ -1,34 +1,49 @@ +import { AuthOptions } from "./auth_types.d.ts"; import { Document, EJSON } from "./deps.ts"; export interface MongoClientConstructorOptions { - appId: string; dataSource: string; - apiKey: string; - endpoint?: string; + auth: AuthOptions; + endpoint: string; fetch?: typeof fetch; } export class MongoClient { - appId: string; dataSource: string; - apiKey: string; - endpoint = "https://data.mongodb-api.com"; + endpoint: string; fetch = fetch; + headers = new Headers(); constructor( - { appId, dataSource, apiKey, endpoint, fetch }: - MongoClientConstructorOptions, + { dataSource, auth, endpoint, fetch }: MongoClientConstructorOptions, ) { - this.appId = appId; this.dataSource = dataSource; - this.apiKey = apiKey; - if (endpoint) { - this.endpoint = endpoint; - } + this.endpoint = endpoint; if (fetch) { this.fetch = fetch; } + + this.headers.set("Content-Type", "application/ejson"); + this.headers.set("Accept", "application/ejson"); + + if ("apiKey" in auth) { + this.headers.set("api-key", auth.apiKey); + return; + } + + if ("jwtTokenString" in auth) { + this.headers.set("jwtTokenString", auth.jwtTokenString); + return; + } + + if ("email" in auth && "password" in auth) { + this.headers.set("email", auth.email); + this.headers.set("password", auth.password); + return; + } + + throw new Error("Invalid auth options"); } database(name: string) { @@ -191,16 +206,12 @@ class Collection { // deno-lint-ignore no-explicit-any async callApi(method: string, extra: Document): Promise { - const { endpoint, appId, apiKey, dataSource } = this.client; - const url = `${endpoint}/app/${appId}/endpoint/data/v1/action/${method}`; + const { endpoint, dataSource, headers } = this.client; + const url = `${endpoint}/action/${method}`; const response = await this.client.fetch(url, { method: "POST", - headers: { - "Content-Type": "application/ejson", - "Accept": "application/ejson", - "api-key": apiKey, - }, + headers, body: EJSON.stringify({ collection: this.name, database: this.database.name, diff --git a/deps.ts b/deps.ts index 83137a6..e8a6a85 100644 --- a/deps.ts +++ b/deps.ts @@ -1 +1 @@ -export * from "https://deno.land/x/web_bson@v0.2.2/mod.ts"; +export * from "https://deno.land/x/web_bson@v0.2.3/mod.ts"; diff --git a/test.ts b/test.ts index aaf4bc6..3487103 100644 --- a/test.ts +++ b/test.ts @@ -1,15 +1,17 @@ // deno-lint-ignore-file require-await import { MongoClient, ObjectId } from "./mod.ts"; -import { deferred } from "https://deno.land/std@0.140.0/async/deferred.ts"; -import { assertEquals } from "https://deno.land/std@0.140.0/testing/asserts.ts"; +import { deferred } from "https://deno.land/std@0.146.0/async/deferred.ts"; +import { assertEquals } from "https://deno.land/std@0.146.0/testing/asserts.ts"; Deno.test("Sample Test", async () => { const fetchMock = deferred<{ url: string; init: RequestInit }>(); const client = new MongoClient({ - appId: "appId", + endpoint: "https://data.mongodb-api.com/app/data-abc/endpoint/data/v1", dataSource: "dataSource", - apiKey: "API_KEY", + auth: { + apiKey: "API_KEY", + }, fetch: (async (url: string, init: RequestInit) => { fetchMock.resolve({ url, init }); return { @@ -28,7 +30,7 @@ Deno.test("Sample Test", async () => { const { url, init } = await fetchMock; assertEquals( url, - "https://data.mongodb-api.com/app/appId/endpoint/data/beta/action/insertOne", + "https://data.mongodb-api.com/app/data-abc/endpoint/data/v1/action/insertOne", ); assertEquals(init.method, "POST"); assertEquals(