Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package-lock.json
pnpm-lock.yaml
node_modules
dist
test
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "marcsync",
"version": "1.1.4-dev",
"version": "1.2.0",
"description": "A NodeJS MarcSync Client to communicate with MarcSync's API",
"main": "dist/marcsync.js",
"types": "dist/index.d.ts",
Expand Down
18 changes: 9 additions & 9 deletions src/Collection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Entry, EntryData, EntryNotFound } from "./Entry";
import { Unauthorized } from "./marcsync";

export class Collection {
export class Collection<T extends EntryData> {

private _accessToken: string;
private _collectionName: string;
Expand Down Expand Up @@ -149,7 +149,7 @@ export class Collection {
* });
*
*/
async createEntry(data: EntryData): Promise<Entry> {
async createEntry(data: T): Promise<Entry<T>> {
try {
const result = await fetch(`https://api.marcsync.dev/v0/entries/${this._collectionName}`, {
method: "POST",
Expand Down Expand Up @@ -185,7 +185,7 @@ export class Collection {
*
* const entry = await collection.getEntryById("my-entry-id");
*/
async getEntryById(id: string): Promise<Entry> {
async getEntryById(id: string): Promise<Entry<T>> {
try {
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}?methodOverwrite=GET`, {
method: "PATCH",
Expand All @@ -203,7 +203,7 @@ export class Collection {
const json = await result.json();
if (!json.success) throw new Error();
if(json.entries.length === 0) throw new EntryNotFound();
return new Entry(this._accessToken, this._collectionName, json.entries[0]);
return new Entry<T>(this._accessToken, this._collectionName, json.entries[0]);
} catch (e) {
if(e instanceof Unauthorized) throw new Unauthorized();
if(e instanceof EntryNotFound) throw new EntryNotFound();
Expand Down Expand Up @@ -236,7 +236,7 @@ export class Collection {
* @see {@link EntryData} for more information about entry data.
*
*/
async getEntries(filter: EntryData={}): Promise<Entry[]> {
async getEntries(filter?: Partial<{ [K in keyof T]: T[K] }>): Promise<Entry<T>[]> {
try {
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}?methodOverwrite=GET`, {
method: "PATCH",
Expand All @@ -245,7 +245,7 @@ export class Collection {
"content-type": "application/json"
},
body: JSON.stringify({
filters: filter
filters: filter || {}
})
})
if (result.status === 401) throw new Unauthorized();
Expand Down Expand Up @@ -297,7 +297,7 @@ export class Collection {
* **__warning: Will delete the entries from the collection. This action cannot be undone.__**
*
*/
async deleteEntries(filter: EntryData): Promise<number> {
async deleteEntries(filter?: Partial<{ [K in keyof T]: T[K] }>): Promise<number> {
try {
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}`, {
method: "DELETE",
Expand All @@ -324,7 +324,7 @@ export class Collection {
* @returns The Id of the updated entry
*
*/
async updateEntryById(id: string, data: EntryData): Promise<string> {
async updateEntryById(id: string, data: Partial<{ [K in keyof T]: T[K] }>): Promise<string> {
try {
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}`, {
method: "PUT",
Expand Down Expand Up @@ -355,7 +355,7 @@ export class Collection {
* @returns The amount of updated entries
*
*/
async updateEntries(filter: EntryData, data: EntryData): Promise<number> {
async updateEntries(filter: Partial<{ [K in keyof T]: T[K] }>, data: Partial<{ [K in keyof T]: T[K] }>): Promise<number> {
try {
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}`, {
method: "PUT",
Expand Down
61 changes: 19 additions & 42 deletions src/Entry.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
export class BaseEntry {
type WithDefaultId<T extends EntryData> = T['_id'] extends string
? T
: T & { _id: string };

private _data: EntryData;
export class BaseEntry<T extends EntryData> {

private _data: T;
private _collectionName: string;

constructor(private data: EntryData, private collectionName: string) {
constructor(private data: T, private collectionName: string) {
this._data = data;
this._collectionName = collectionName;
}
Expand Down Expand Up @@ -31,36 +35,8 @@ export class BaseEntry {
* @see {@link EntryData} for more information about entry data.
*
*/
getValues(): EntryData {
return this._data;
}

/**
*
* @param key - The key of the value to get
* @returns The value of the specified key
*
* @example
*
* import { Client } from "marcsync";
*
* const client = new Client("<my access token>");
* const collection = client.getCollection("my-collection");
*
* const entry = await collection.getEntryById("my-entry-id");
*
* const name = entry.getValueAs<string>("name");
*
* console.log(name);
*
* @remarks
* This method is useful if you want to get the value of a specific key as a specific type.
*
* @see {@link EntryData} for more information about entry data.
*
*/
getValueAs<T>(key: string): T {
return this._data[key];
getValues(): WithDefaultId<T> {
return this._data as WithDefaultId<T>;
}

/**
Expand All @@ -87,8 +63,8 @@ export class BaseEntry {
* @see {@link EntryData} for more information about entry data.
*
*/
getValue(key: string): any {
return this._data[key];
getValue<K extends keyof WithDefaultId<T>>(key: K): WithDefaultId<T>[K] {
return (this._data as WithDefaultId<T>)[key];
}

/**
Expand All @@ -100,20 +76,20 @@ export class BaseEntry {
return this._collectionName;
}

protected _setData(data: EntryData) {
protected _setData(data: T) {
this._data = data;
}
}

export class Entry extends BaseEntry {
export class Entry<T extends EntryData> extends BaseEntry<T> {

private _accessToken: string;
private _entryId: string;

constructor(accessToken: string, collectionName: string, data: EntryData) {
constructor(accessToken: string, collectionName: string, data: T) {
super(data, collectionName);
this._accessToken = accessToken;
this._entryId = data._id;
this._entryId = data._id!;
}

/**
Expand All @@ -139,7 +115,7 @@ export class Entry extends BaseEntry {
* This method is useful if you want to update the value of a specific key.
*
*/
async updateValue(key: string, value: any): Promise<EntryData> {
async updateValue<K extends keyof WithDefaultId<T>>(key: K, value: WithDefaultId<T>[K]): Promise<WithDefaultId<T>> {
try {
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this.getCollectionName()}`, {
method: "PUT",
Expand Down Expand Up @@ -193,7 +169,7 @@ export class Entry extends BaseEntry {
* @see {@link updateValue} for more information about updating a single value.
*
*/
async updateValues(values: EntryData): Promise<EntryData> {
async updateValues(values: Partial<{ [K in keyof WithDefaultId<T>]: WithDefaultId<T>[K] }>): Promise<WithDefaultId<T>> {
try {
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this.getCollectionName()}`, {
method: "PUT",
Expand All @@ -215,7 +191,7 @@ export class Entry extends BaseEntry {
}
const data = this.getValues();
for (const key in values) {
data[key] = values[key];
data[key] = values[key]!;
}
this._setData(data);
return data;
Expand Down Expand Up @@ -249,6 +225,7 @@ export class Entry extends BaseEntry {
}

export interface EntryData {
_id?: string
[key: string]: any;
}

Expand Down
18 changes: 9 additions & 9 deletions src/marcsync.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Collection, CollectionAlreadyExists, CollectionNotFound } from "./Collection";
import { BaseEntry, Entry } from "./Entry";
import { BaseEntry, Entry, EntryData } from "./Entry";
import { SubscriptionManager } from "./SubscriptionManager";

export class Client {
Expand Down Expand Up @@ -31,8 +31,8 @@ export class Client {
* const collection = client.getCollection("my-collection");
*
*/
getCollection(collectionName: string) {
return new Collection(this._accessToken, collectionName);
getCollection<T extends EntryData = EntryData>(collectionName: string): Collection<T> {
return new Collection<T>(this._accessToken, collectionName);
}

/**
Expand All @@ -51,7 +51,7 @@ export class Client {
* This method is useful if you want to fetch the collection from the server to check if it exists before using it.
*
*/
async fetchCollection(collectionName: string): Promise<Collection> {
async fetchCollection<T extends EntryData = EntryData>(collectionName: string): Promise<Collection<T>> {
try {
const result = await fetch(`https://api.marcsync.dev/v0/collection/${collectionName}`, {
method: "GET",
Expand All @@ -66,7 +66,7 @@ export class Client {
if (e instanceof Unauthorized) throw new Unauthorized();
throw new CollectionNotFound();
}
return new Collection(this._accessToken, collectionName);
return new Collection<T>(this._accessToken, collectionName);
}

/**
Expand All @@ -83,7 +83,7 @@ export class Client {
*
* @remarks
*/
async createCollection(collectionName: string): Promise<Collection> {
async createCollection<T extends EntryData = EntryData>(collectionName: string): Promise<Collection<T>> {
try {
const result = await fetch(`https://api.marcsync.dev/v0/collection/${collectionName}`, {
method: "POST",
Expand Down Expand Up @@ -121,7 +121,7 @@ export class Unauthorized extends Error {
}

export interface ClientEvents {
entryCreated: [entry: Entry, databaseId: string, timestamp: number];
entryUpdated: [oldEntry: BaseEntry, newEntry: Entry, databaseId: string, timestamp: number];
entryDeleted: [entry: BaseEntry, databaseId: string, timestamp: number];
entryCreated: [entry: Entry<EntryData>, databaseId: string, timestamp: number];
entryUpdated: [oldEntry: BaseEntry<EntryData>, newEntry: Entry<EntryData>, databaseId: string, timestamp: number];
entryDeleted: [entry: BaseEntry<EntryData>, databaseId: string, timestamp: number];
}