Giter Club home page Giter Club logo

sp2's Introduction

Phenyl - State Synchronization over Environments CircleCI

WORK IN PROGRESS

Phenyl is a series of JavaScript libraries for both client-side and server-side, which focuses on State Synchronization over Environments(SSoE).

ServerSide Phenyl provides an API server like MBaaS(Mobile Backend as a server). ClientSide Phenyl provides Redux module system. Within these two environments, States are Synchronized in various ways.

State Synchronization over Environments (SSoE)

State Synchronization over Environments is the main concept of modern applications.

For example, if you use RESTful API, you can synchronize server and client state through following HTTP methods:

  • Acquire server state of user by GET /user and provide user profile view by acquired user entity.
  • User updates user name at profile view, app update user entity in the client side and app POST or PATCH /user.
  • User delete account, app clear the client state and app execute DELETE /user

It's popular method of SSoE. So, many people create RDB, write API server to transform Data to Entity and communicate with client, write fetch code in client, client transform Entity to data for view, client update entity and notify to server, server save updated entity to DB and so on. Additionally error handlings are neccessary in practice.

Phenyl provides simpler solution to handle SSoE based on the following 4 concepts:

  • State as one big JSON
  • OAD: Operations as Data
  • MongoDB-like operations
  • Git-like synchronization

State as one big JSON

JSON is very useful format for JavaScript. It has some basic types like string or boolean and it's easy to serialize and deserialize. We represent the entire app state as one big JSON.

OAD: Operations As Data

Phenyl handles all CRUD operations on data, such as creating an entity, reading the data in an entity, updating a property in an entity, and deleting an entity as data. The history of all operations is also saved as in the state. This concept allows us to easily reproduce and debug any situations we want.

MongoDB-like operations

Phenyl provides MongoDB-like operations.

In a mongoDB shell, CRUD operations are performed as following:

> db.testUser.insertOne({_id: "test1", name: "Test1"})
{ "acknowledged" : true, "insertedId" : "test1" }

> db.testUser.findOne({_id: "test1"})
{ "_id" : "test1", "name" : "Test1" }

> db.testUser.updateOne({name: "Test1"}, {$set: {favoriteFood: "banana"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.testUser.findOne({_id: "test1"})
{ "_id" : "test1", "name" : "Test1" , "favoriteFood": "banana" }

> db.testUser.deleteOne({"_id": "test1"})
{ "acknowledged" : true, "deletedCount" : 1 }

> db.testUser.findOne({_id: "test1"})
null

In Phenyl client, the operations are performed as following. (This code is simplified.)

// omission prepare httpClient and preSession
const inserted = await phenylClient.insertAndGet(
    {
        entityName: "testUser",
        value: {
            id: "test1",
            name: "Test1",
        }
    },
    preSession.id
);

console.log(inserted.entity)
// -> { id: "test1", name: "Test1" }

const updated = await phenylClient.updateAndGet(
    {
        entityName: "testUser",
        id: "test1",
        operation: {
            $set: { favoriteFood: "banana" }
        }
    },
    preSession.id
);

console.log(updated.entity)
// -> { id: "test1", name: "Test1" , faboriteFood: "banana" }

await phenylClient.delete({ entityName: "testUser", id: "test1" })

If you have used mongodb, you will soon get to be friendly with the API of Phenyl😎.

Git-like synchronization

Phenyl synchronizes between server and client by using git-like command. You can acquire server side entity by pull and update by push. This allows us to handle offline oepration easily.

Phenyl Family

ServerSide Libraries

ClientSide Libraries

Common Libraries

sp2

Phenyl is powered by sp2, a set of JavaScript modules used for state operations.

Usage

Phenyl needs you to implement 2 features, one is GeneralTypeMap and the other is functionalGroup. GeneralTypeMap is type definition that describes shape of request and response of each entity and auth information. functionalGroup is implementation to notify Phenyl about the domain that we want to use. If you want to know more details, see example.

License

Apache License 2.0

sp2's People

Contributors

1natsu172 avatar dependabot-preview[bot] avatar imoans avatar naturalclar avatar renovate-bot avatar renovate[bot] avatar sasurau4 avatar shinout avatar yamatatsu avatar yuuyu00 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

sp2's Issues

[$path] I got an error when I put a nullable property in the argument of $path method

import { $bind } from "@sp2/updater";

type MapKeys = "key1";
interface Map { map: { [key in MapKeys]?: any } };

const { $set, $path } = $bind<Map>();
const operation = $set($path("map", "key1"));
error TS2345: Argument of type 'BoundDocumentPath2<Map, "map", "key1">' is not assignable to parameter of type '[string, any]'.
  Type 'String & { keys: ["map", "key1"]; object: Map; }' is missing the following properties from type '[string, any]': 0, 1, pop, push, and 13 more.

const operation = $set($path("map", "key1"));
                              ~~~~~~~~~~~~~~~~~~~~

Review request of static analysis results

I forked sp2 into my git and executed static analysis by using two types of tools: SonarCloud and DeepScan. The results between them are different because the analysis policies and the rules used by each other are different.

I'm sure your review will be helpful to others.
Click the badge below to see results.

  1. results analyzed by DeepScan: DeepScan grade

  2. results analyzed by SonarCloud: Quality Gate Status

FYI
SonarCloud ( https://sonarcloud.io/ )

  • analysis on 20+ different languages (including Javascript)
  • possible to analyze source from various perspectives:
    Reliability,
    Security (Hotspot, OWASPs Top 10, SNAS Top 25, CWE),
    Maintainability,
    Coverage,
    Duplications,
    Complexity
  • A total of 226 rules are applied to Javascript and 172 to TypeScript

DeepScan ( https://deepscan.io/ )

  • specialized in Javascript ( JavaScript, TypeScript, React and Vue.js )
  • semantic analysis (enables finding issues that syntax-based linters can't)
  • analyzed by using ESLint and 201 rules (As of June 5, 2020)

Add $append operator

$append operator is syntax sugar for $set operator.

{ $set: { 'obj.foo': 123, 'obj.bar': 'abc', 'obj.baz': true } }

can be written as

{ $append: { obj: { foo: 123, bar: 'abc', baz: true } }

Type error happend by using union type string literal value for $path from $bind.

example code:

import { $bind } from "@sp2/updater";

type Hoge = {
  fuga: {
    key1: string;
    key2: string;
  };
};

const { $push, $path } = $bind<Hoge>();

$push($path("fuga", "key1"), "hoge"); // valid

const key = Math.random() > 0.5 ? "key1" : "key2";

$push($path("fuga", key), "hoge"); // $path is type error

I see here.
https://github.com/phenyl-js/sp2/blob/5c14b6d31cf228b8403c2f184787eb02cbe41c80/modules/format/src/common/bound-document-path.ts#L21-L28
https://github.com/phenyl-js/sp2/blob/5c14b6d31cf228b8403c2f184787eb02cbe41c80/modules/format/src/common/bound-document-path.ts#L151-L154

The $path method is generating some depth path type and this using like following codes.
K1 extends keyof T, K2 extends keyof T[K1] ...
However if type K[i] had union type value, looks like TS compiler is checking 'left side value can extends all of expanded union type value?'.
And i think this issue can be solved by using like following codes.
K1 extends Extract<K1, keyof T>, K2 extends Extract<K2, T[K1]> ...

But if resolved this issue, #37 Error is still showing...

Add an adaptor for mongodb

sp2 provides slightly different formats from MongoDB's formats.
To fill the gap, sp2 should provide converters of operations.

function toMongoUpdateOperation(operation: GeneralUpdateOperation): GeneralUpdateOperation
function toMongoFindOperation(operation: FindOperation): FindOperation

$path: Support optional property

import { $bind } from 'sp2'

type SomeType = {
    exact: {
        deepNestedExact: {
            count: number
        }
    },
    optional?: {
        deepNestedOptional?: {
            count: number
        }
    }
}

const { $path } = $bind<SomeType>()

$path('exact', 'deepNestedExact', 'count') // OK
$path('optional', 'deepNestedOptional') // Error: Argument of type '"deepNestedOptional"' is not assignable to parameter of type 'never'.

Hi.
I found an error according to the optional property.

How can I traverse optional property?

Add benchmark tests

Add benchmark tests on benchmark module.

  • create module

update()

  • non-nested $set operation
  • nested $set operation
  • $push operation
  • mixed operation
  • comparison with immer
  • comparison with Immutable.js
  • comparison with other state-updating libraries

retrieve()

  • simple find operation
  • mixed operation

Not work when setting a value whose type is type variable

type TargetObject<T> = { foo: T };

function bar<T>(obj: TargetObject<T>, value: T) {
  const { $set, $docPath } = $bind<TargetObject<T>>();
  const path = $docPath("foo");
  const operation = $set(path, value); // this occurs an error
}

Maybe type inference at 2nd argument of $set is not working.

  const operation = $set(path, value as NestedValue<TargetObject<T>, "foo", "","","","","","","","",""); 

This can suppress the error, but I want easier way.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.