1.0.0 • Published 2 years ago

cloud-coffee-ts v1.0.0

Weekly downloads
-
License
ISC
Repository
-
Last release
2 years ago

CloudCoffee - Data Framework

cloud-coffee is a implementation agnostic database / client framework that intends to be a single source of truth when it comes to application state and how to access data.

the framework seeks to achieve the following objectives

  • Create an extensible modular database framework that can support any given database implementation given the implementation follows the layed out specification
  • Create a simple and easy to use forward facing API that is readable & powerful
  • Any applications written with cloud-coffee should be compatible with any database technology that supports the features defined in the specification. The specification seeks to be general enough to support this

Implementation Free

the cloud-coffee is mostly implementation free meaning it consists mostly of interface and type definitions in order to ensure the core part of API does not depend on any existing implementation. The functions that are implemented in the base package require little or no dependencies

Modular framework

At the time of writing cloud-coffee consists of 8 core modules that need to be implemented for the full functionality of the framework. These are

export interface Cloud {
    auth    : CloudAuth,
    batcher : CloudBatcher  
    db      : CloudDb
    helper  : CloudHelper
    library : CloudLibrary
    local   : CloudLocal
    runtime : CloudRuntime
    server  : CloudServer
}

CloudAuth contains authentication functions

CloudBatcher functions that support atomic batching of database calls

CloudDb the core database API to read, write, delete data objects

CloudHelper a list of helper functions other modules can call into

CloudLibrary a companion to CloudDb to store file based resources like images, and other files

CloudLocal functions that can run on the local machine if not a web based system

CloudRuntime Factory pattern that handles the creation of runtime resources

CloudServer Server specific functions that depend of what server environment you are using

CloudCoffee.js

This file in the main entry point and forward facing API that contain a list of functions that can be called based on the Database implementation

Setup

The default export from 'index.js / CloudCoffee.js' is a setup function that is used to configure the runtime environment

Setup the implementation

import Setup from "cloud-coffee";

let myCloud : Cloud = {/* Provide a cloud implementation */}

await Setup(myCloud);

Optional config

Setup() can take in an optional 2nd parameter with config options that can change the runtime behavior of the framework

Setup definition

export type CloudSetup = {
    runtimeConfig : RuntimeConfig
    runtime : CloudRuntime
}

Custom Setup

import Setup from "cloud-coffee";

let myCloud : Cloud = {/* Provide a cloud implementation */}
let mySetup : CloudSetup = {/* Provide s setup config*/}

await Setup(myCloud, mySetup);

once you await the setup you can immediately start using the database api and client functions

Account

The the time of writing cloud-coffee only supports a single email/password based auth provider (This may change when the interface / API is fleshed our further)

To create an account use the Account function

import { Account } from "cloud-coffee"
async function main(){
    const uid = await Account("username@gmail", "password");
    console.log(`New Account in as ${uid}`);
    //TODO:ask for name and save to DB
}
main()

Login

To Login use the Login function

import { Login } from "cloud-coffee"
async function main(){
    const uid = await Login("username@gmail", "password");
    console.log(`Logged in as ${uid}`)
}
main()

User

use the User() string | null to retrieve the user id or null of the currently logged in user

import { User } from "cloud-coffee"
const uid = User();
if(uid)
    console.log(`User is available :${uid}`);
else
    console.log("User is null")

Signout

use the Signout() : Promise<void> to log the user out

import { Signout } from "cloud-coffee"

await Signout()

UserReady

use the UserReady() : Dispose to attach a listener to detect when the user as logged in or out. If the callback can return null if user is not present. If user is logged out you must at least callback once with null

import { UserReady } from "cloud-coffee"

const dispose = UserReady(uid => {
    if(uid){
        //Database is ready to be used
    }
})

dispose() //will detach the listener

Email

set the users email by calling Email(currentPassword, newEmail)

Password

set the users password by calling Password(currentPassword, newPassword)

Verify

await Verify() will send a verification email

Present

if user is available ie User() != null

CloudDB

The database module was inspired by the google firebase API and the current implementation I have written is a firebase one, as such the specification borrows some ideas of how to structure data

Concepts

The functional language of CloudCoffee borrows words inspired by literature and library jargon as a way to conceptualist the actions you are preforming on the database

Page

a page is a path to a object / data resource. To access a page you provide the database scoped Url/Path to that resource. Exactly like firebase pages are denoted using an odd number of path segments for example '/users/837' is a user page

a volume is a collection of pages or objects at a specific Url/Path that can be accessed.

Example '/users/927/projects' is a path to the user bucket projects

Note the first segment of a path is always a volume

Page

to access a page with a callback use the 'Page' function. This will return a dispose function to detach the callback. You can type age page to return exactly what you want

import { CreateCaller, Page } from "cloud-coffee"

type Project = {
    projectName : string
}

const local = CreateCaller("my-local-caller");
Page<Project>(local, "/users/983/projects/project-01", project => {
    //TODO:do something with return
})

Read

Page is a callback based, cloud-coffeesupports inline await for Paging data with Read which is basically the same signature as Page

import { CreateCaller, Read } from "cloud-coffee"

const local = CreateCaller("my-local-caller");
const data = await Read<Project>(local, "/users/983/projects/project-01");
if(data != null) {
    //You data is available
}

Volume

To read a list of data use the Volume function

import { CreateCaller, Volume } from "cloud-coffee"

const local = CreateCaller("my-local-caller");
Volume<Project>(local, "/users/983/projects", projects => {
    //print projects in console //TODO
});

List

List is the promise based version of Volume to await the data inline

import { CreateCaller, List } from "cloud-coffee"

const local = CreateCaller("my-local-caller");
const array = await List<Project>(local, "/users/75/projects");

Publish

you can Publish (add) data with the Publish function. Just input which volume / path you want the data to be published too

import { CreateCaller, Publish } from "cloud-coffee"

const newProject : Project = {
    projectName = "New API Imp"
}

const local = CreateCaller("my-local-caller");

const object = await Publish<Project>(local, "/users/983/projects", newProject);

Publish returns a CloudObject<T> which is a virtual pointer to an object in the database

Amend

You can update / amend with the Amend function. This will update the object in the database only if the resource still exists. Specify which page to update

import { CreateCaller, Amend } from "cloud-coffee"

const local = CreateCaller("my-local-caller");

await Amend<Project>(local, "/users/983/projects/project-34", {
    projectName : "Override new Project Name"
});

Delist / Remove

You can delist a page / remove / delete by called the 'Delist(.., page)' function

import { CreateCaller, Delist } from "cloud-coffee"
const local = CreateCaller("my-local-caller");

await Delist<Project>(local, "/users/983/projects/project-deleteME");

Write

You can set a database value even if it currently doesn't exist using Write function. However depending on the formatof an object the write call can behave different to Amend since amend only handles changes, Write can handle the Create state of an object which needs special consideration. This is due to the fact that cloud-coffee has a special data format called modular which is defined in the specification.

import { CreateCaller, Write } from "cloud-coffee"
const local = CreateCaller("my-local-caller");

const data = await Write<Project>(local, "/users/983/projects/write-empty", object, {
    format : "regular"
});

Write has a 4th optional param that defines a object format regular means the format is a 1:1 representation so the data is exactly the same as what was passed.

Note: All Db calls have an optional params / options type end parameter that contains is passed to the function