Skip to content

Commit

Permalink
Showing 13 changed files with 1,190 additions and 538 deletions.
496 changes: 304 additions & 192 deletions src/acts/acts.ts

Large diffs are not rendered by default.

168 changes: 150 additions & 18 deletions src/acts/types.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,167 @@
import { Struct } from "../deps.ts";
import { Body } from "../utils/mod.ts";

/**
* type of ActFn
* @param body - input of function is type of body
* type of Body is equal to
* { service?: ServiceKeys; - nameOfService
* contents: "dynamic" | "static"; - type of contest
* wants: {
* model: string; -model that client want to execute action (name of schema)
* act: string;
* };
* details: Details;
* }
*/
export type ActFn = (body: Body) => any;

/**
* interface of Act is include of tow features
* validator of function and fn
* @interface
*/
export interface Act {
validator: Struct<any>;
fn: ActFn;
validator: Struct<any>;
fn: ActFn;
}

/**
* Acts include tow features : dynamic and static
* dynamic for dynamic request and static for static request for example get static file
* @example
*
* dynamic: {
* user: {
* create: {
* validator: (input: any) => {
* return true;
* },
* fn: (input: any) => {
* return input;
* },
* },
* update: {
* validator: (input: any) => {
* return true;
* },
* fn: (input: any) => {
* return input;
* },
* },
* },
* },
* static: {
* "blogFirstPage": {
* "get": {
* "validator": (input: any) => {
* return true;
* },
* "fn": (input: any) => {
* return input;
* },
* },
* "set": {
* "validator": (input: any) => {
* return true;
* },
* "fn": (input: any) => {
* return input;
* },
* },
* },
* },
*/
export interface Acts {
dynamic: {
[key: string]: {
[key: string]: Act;
dynamic: {
[key: string]: {
[key: string]: Act;
};
};
};
static: {
[key: string]: {
[key: string]: Act;
static: {
[key: string]: {
[key: string]: Act;
};
};
};
}

/**
* service inteface is include main service and functions
* and also maybe include other services
* @example
* {
* main:{
* dynamic: {
* user: {
* create: {
* validator: (input: any) => {
* return true;
* },
* fn: (input: any) => {
* return input;
* },
* },
* static: {
* "blogFirstPage": {
* "get": {
* "validator": (input: any) => {
* return true;
* },
* "fn": (input: any) => {
* return input;
* },
* },
* }
* },
* },
* "ecommerce":"https://localhost:5050/lesan",
* "blog":{
* dynamic: {
* user: {
* create: {
* validator: (input: any) => {
* return true;
* },
* fn: (input: any) => {
* return input;
* },
* },
* }
* },
* main services is type of Acts , other services maybe type of string or Act:
* if type of string we get answer of req with http Request , but if type of it equal to Acts with anwer to req directly
*/
export interface Services {
main: Acts;
[key: string]: Acts | string | undefined;
main: Acts;
[key: string]: Acts | string | undefined;
}

/**
* ActInp is type of action in lesan
* for set action function
* @interface
*/
export interface ActInp {
type: "static" | "dynamic";
schema: string;
actName: string;
validator: Struct<any>;
fn: ActFn;
/**
* type of action static or dynamic
* when equal to static for get static file (isdb)
* else for dynamic request(request to db ideed)
*/
type: "static" | "dynamic";
/**
* name of schema that set action for it
*/
schema: string;
/**
* name of action
*/
actName: string;
/**
* validator function for example for validion input date
*/
validator: Struct<any>;

/**
* function
*/
fn: ActFn;
}
67 changes: 53 additions & 14 deletions src/models/context.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,62 @@
/// context type ----
//
/**
* Context Holds values and carries them in functions.
* @example
* {
* Request: "values of Req",
* user: {
* "_id":1,
* "name":"ali",
* "lastName":"Alavi"
* "role":"Admin"
* } *
*/
export interface Context {
[key: string]: any;
[key: string]: any;
}

/**
* this function is create for define all things in local scope
* and also all functions of context define in this function
* @returns - return objects of all functions that define in this function
*/
export const contextFns = () => {
let context: Context = {};
/**
* variable of context
* @defaultValue
* the value of context is '{}'
*/
let context: Context = {};

const getContextModel = () => context;
/**
* @returns The contextObj
*/
const getContextModel = () => context;
/**
* asigne all of value that we want to carry
* @param con - objects of key , value
* @returns nothing
*/
const addContexts = (con: Context) => context = con;

const addContexts = (con: Context) => context = con;
/**
* add values to previous values that we want to carry
* @param con - objects of key , value
* @returns nothing
*/
const addContext = (con: Context) => context = { ...context, con };
/**
* add Request to Context because the requeste may be required in later functions
* @param con - request of user
* @returns nothing
*/
const addReqToContext = (con: Request) => context["Request"] = con;

const addContext = (con: Context) => context = { ...context, con };

const addReqToContext = (con: Request) => context["Request"] = con;

return {
getContextModel,
addContexts,
addContext,
addReqToContext,
};
return {
getContextModel,
addContexts,
addContext,
addReqToContext,
};
};
118 changes: 79 additions & 39 deletions src/models/inrelation.ts
Original file line number Diff line number Diff line change
@@ -2,47 +2,87 @@ import { ISchema } from "./mod.ts";
import { schemaFns } from "./schema.ts";
import { InRelation } from "./types.ts";

/**
* this function is create for define all things in local scope
* and also all functions of inrelation define in this function
* @function
* @returns - return objects of all functions that define in this function
*/
export const inrelationFns = (schemasObj: ISchema) => {
const addInrelations = (
{ schemaName, inrelation }: {
schemaName: string;
inrelation: Record<string, InRelation>;
},
) => {
const schemas = schemaFns(schemasObj).getSchemas();
const schema = schemas[schemaName];
if (!schema) {
throw new Error(`Schema ${schemaName} does not exist`);
}
schema.inrelation = inrelation;
};

const addOneInRelation = (
{ schemaName, inrelationName, type }: {
schemaName: string;
inrelationName: string;
type: "one" | "many";
},
) => {
const schemas = schemaFns(schemasObj).getSchemas();
const schema = schemas[schemaName];
if (!schema) {
throw new Error(`Schema ${schemaName} does not exist`);
}
schema.inrelation = {
...schema.inrelation,
[inrelationName]: { schemaName, type },
/**
* asign inrelation of schema that schema has relation with it example of relation of SQL that we keep foriegn key.
* @param schemaName - Name of schema that we want add inerRealation for it
* @param inerRealation - record of key value of inRelation Type
* @example
* example of input parameter is
* {
* schemaName:"city",
* inerRealation:{
"provice":{
"schemaName":"provice",
"type":"one"
}
* }
* }
*/
const addInrelations = (
{ schemaName, inrelation }: {
schemaName: string;
inrelation: Record<string, InRelation>;
},
) => {
const schemas = schemaFns(schemasObj).getSchemas();
const schema = schemas[schemaName];
if (!schema) {
throw new Error(`Schema ${schemaName} does not exist`);
}
schema.inrelation = inrelation;
};
/**
* add one innerRelation to previous inrelation of schema that schema has relation with it example of relation of SQL that we keep foriegn key.
* @param schemaName - Name of schema that we want add inerRealation for it
* @param inerRealationName - name of inerRealtion fields
* @param schemaInRelation - Name of schema that this schema has inerRealation with it
* @example
* example of input parameter is
* {
* schemaName:"city",
* "inrelationName":"provice"
* "schemaInRelation":"provice"
* "type":"one"
* }
*/
const addOneInRelation = (
{ schemaName, inrelationName, schemaInRelation, type }: {
schemaName: string;
inrelationName: string;
schemaInRelation: string;
type: "one" | "many";
},
) => {
const schemas = schemaFns(schemasObj).getSchemas();
const schema = schemas[schemaName];
if (!schema) {
throw new Error(`Schema ${schemaName} does not exist`);
}
schema.inrelation = {
...schema.inrelation,
[inrelationName]: { schemaName: schemaInRelation, type },
};
};
};

const getInrelations = (schemaName: string) => {
const schemas = schemaFns(schemasObj).getSchemas();
schemas[schemaName].inrelation;
};
/**
* get all of inerRealation of one schema
* @param schemaName - name of schema that we want inerRealation of it
*/
const getInrelations = (schemaName: string) => {
const schemas = schemaFns(schemasObj).getSchemas();
schemas[schemaName].inrelation;
};

return {
addInrelations,
addOneInRelation,
getInrelations,
};
return {
addInrelations,
addOneInRelation,
getInrelations,
};
};
135 changes: 92 additions & 43 deletions src/models/outrelation.ts
Original file line number Diff line number Diff line change
@@ -2,50 +2,99 @@ import { ISchema } from "./mod.ts";
import { schemaFns } from "./schema.ts";
import { OutRelation } from "./types.ts";

/**
* this function is create for define all things in local scope
* and also all functions of outrelation define in this function
* @function
* @returns - return objects of all functions that define in this function
*/
export const outrelationFns = (schemasObj: ISchema) => {
const addOneOutRelation = (
{ schemaName, outrelationName, number, sort }: {
schemaName: string;
outrelationName: string;
number: number;
sort: {
field: string;
order: "asc" | "desc";
};
},
) => {
const schemas = schemaFns(schemasObj).getSchemas();
const schema = schemas[schemaName];
if (!schema) {
throw new Error(`Schema ${schemaName} does not exist`);
}
schema.outrelation = {
...schema.outrelation,
[outrelationName]: { schemaName, number, sort },
/**
* add addOneOutRelation to previous outrelation of schema.
* outerRelation is Relationships that we do not hold the external key in SQL and usually have more than 50
* @param schemaName - Name of schema that we want add outrelation for it
* @param outrelationName - name of outerRelation fields
* @param schemaOuterRelation - Name of schema that this schema has outrelation with it
* @param number - number of value that we want to keep
* @param sort : {field , order} - field of sort , and order of sort
* @example
* example of input parameter is
* {
* schemaName:"country",
* "outrelationName":"states"
* "schemaOuterRelation":"state"
* "sort":{
* "field":"_id",
* "order":"desc"
* }
* }
*/
const addOneOutRelation = (
{ schemaName, outrelationName, schemaOuterRelation, number, sort }: {
schemaName: string;
outrelationName: string;
schemaOuterRelation: string;
number: number;
sort: {
field: string;
order: "asc" | "desc";
};
},
) => {
const schemas = schemaFns(schemasObj).getSchemas();
const schema = schemas[schemaName];
if (!schema) {
throw new Error(`Schema ${schemaName} does not exist`);
}
schema.outrelation = {
...schema.outrelation,
[outrelationName]: { schemaName: schemaOuterRelation, number, sort },
};
};
/**
* assign addOneOutRelation of schema.
* outerRelation is Relationships that we do not hold the external key in SQL and usually have more than 50
* @param schemaName - Name of schema that we want add outrelation for it
* @param outrelationName - name of outerRelation fields
* @param schemaOuterRelation - Name of schema that this schema has outrelation with it
* @param number - number of value that we want to keep
* @param sort : {field , order} - field of sort , and order of sort
* @example
* example of input parameter is
* {
* schemaName:"country",
* "outrelationName":"states"
* "schemaOuterRelation":"state"
* "sort":{
* "field":"_id",
* "order":"desc"
* }
* }
*/
const addOutRelations = (
{ schemaName, outrelation }: {
schemaName: string;
outrelation: Record<string, OutRelation>;
},
) => {
const schema = schemaFns(schemasObj).getSchemas()[schemaName];
if (!schema) {
throw new Error(`Schema ${schemaName} does not exist`);
}
schema.outrelation = outrelation;
};
/**
* getOutRelations of one schema
* @param schemaName - name of schema that we want outerRelations
*/
const getOutRelations = (schemaName: string) => {
const schemas = schemaFns(schemasObj).getSchemas();
return schemas[schemaName].outrelation;
};
};

const addOutRelations = (
{ schemaName, outrelation }: {
schemaName: string;
outrelation: Record<string, OutRelation>;
},
) => {
const schema = schemaFns(schemasObj).getSchemas()[schemaName];
if (!schema) {
throw new Error(`Schema ${schemaName} does not exist`);
}
schema.outrelation = outrelation;
};

const getOutRelations = (schemaName: string) => {
const schemas = schemaFns(schemasObj).getSchemas();
return schemas[schemaName].outrelation;
};

return {
addOneOutRelation,
addOutRelations,
getOutRelations,
};
return {
addOneOutRelation,
addOutRelations,
getOutRelations,
};
};
74 changes: 49 additions & 25 deletions src/models/pure.ts
Original file line number Diff line number Diff line change
@@ -2,32 +2,56 @@ import { ISchema } from "./mod.ts";
import { schemaFns } from "./schema.ts";
import { PureModel } from "./types.ts";

/**
* this function is create for define all things in local scope
* and also all functions of pure define in this function
* @function
* @returns - return objects of all functions that define in this function
*/
export const pureFns = (schemasObj: ISchema) => {
const addPureModel = (name: string, pureModel: PureModel) => {
const schemas = schemaFns(schemasObj).getSchemas();
return schemas[name] = {
pure: pureModel,
inrelation: {},
outrelation: {},
};

// schemas[name].pure = pureModel;
};
/**
* add pure feature of model to schema
* @param name - name of schema that we want to add pure features
* @param pureModel - key and type of model to add schema
*
* @example
* name:"city"
* pureModel: {
* "name":string()
* }
*/
const addPureModel = (name: string, pureModel: PureModel) => {
const schemas = schemaFns(schemasObj).getSchemas();
return schemas[name] = {
pure: pureModel,
inrelation: {},
outrelation: {},
};

const getPureModel = (name: string) => {
const schemas = schemaFns(schemasObj).getSchemas();
return schemas[name]?.pure;
};

const getPureModelByNameAndKey = (name: string, key: string) => {
const schemas = schemaFns(schemasObj).getSchemas();
const pureModel = schemas[name].pure[key];
return pureModel;
};
// schemas[name].pure = pureModel;
};
/**
* get pure features of one schema
* @param name - name of schema that we want to get pure feature
*/
const getPureModel = (name: string) => {
const schemas = schemaFns(schemasObj).getSchemas();
return schemas[name]?.pure;
};
/**
* get pure one feature of one schema by name of schema and key of feature
* @param name - name of schema that we want to get one pure feature
* @param key - key of feature of schema that we want to get one pure feature
*/
const getPureModelByNameAndKey = (name: string, key: string) => {
const schemas = schemaFns(schemasObj).getSchemas();
const pureModel = schemas[name].pure[key];
return pureModel;
};

return {
addPureModel,
getPureModel,
getPureModelByNameAndKey,
};
return {
addPureModel,
getPureModel,
getPureModelByNameAndKey,
};
};
32 changes: 21 additions & 11 deletions src/models/relation.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { ISchema } from "./mod.ts";
import { schemaFns } from "./schema.ts";

/**
* this function is create for define all things in local scope
* and also all functions of relationFns define in this function
* @param {@link ISchema} schemasObjs - input is all record of schemas
* @returns - return objects of all functions that define in this function
*/
export const relationFns = (schemasObjs: ISchema) => {
const getRelation = (
name: string,
relationType: "inrelation" | "outrelation",
) => {
const schemas = schemaFns(schemasObjs).getSchemas();
return schemas[name][relationType];
};
/**
* get inerRelatrion or outerRealtion of one schema
* @param name - name of schema
* @param relationType - type of relation that we want (inerRelatrion or outrelation)
*/
const getRelation = (
name: string,
relationType: "inrelation" | "outrelation",
) => {
const schemas = schemaFns(schemasObjs).getSchemas();
return schemas[name][relationType];
};

return {
getRelation,
};
return {
getRelation,
};
};
133 changes: 125 additions & 8 deletions src/models/schema.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,42 @@
import { array, assign, object } from "../deps.ts";
import { Model } from "./types.ts";

/**
* this function is create for define all things in local scope
* and also all functions of relationFns define in this function
* @param {@link ISchema} schemasObjs - input is all record of schemas
* @returns - return objects of all functions that define in this function
*/
export const schemaFns = (schemas: Record<string, Model>) => {
// const schema1 = {}

/**
* get object of schema
* @returns all schemas
*/
const getSchemas = () => schemas;

/**
* get one feature of schema by schemaName
* @param schemaName - name of schema that we want feature
* @returns
* return one schema feature for example:
* pure: {
* "id": string(),
* "name": string(),
* "age": number(),
* },
* inrelation: {
* "posts": { schemaName: "post", type: "many" },
* },
* outrelation: {
* "comments": {
* schemaName: "comment",
* number: 50,
* sort: { filed: "id", order: "desc" },
* },
* },
*/
const getSchema = (schemaName: string) => {
const schema = schemas[schemaName];

@@ -14,6 +46,16 @@ export const schemaFns = (schemas: Record<string, Model>) => {
return schema;
};

/**
* get pure feature of one schema
* @param schemaName - name of schema that we want pure feature
* @returns return pure feature of schema for example:
* {
* "id": string(),
* "name": string(),
* "age": number(),
* },
*/
const getPureSchema = (schemaName: string) => {
const schema = schemas[schemaName];

@@ -23,23 +65,57 @@ export const schemaFns = (schemas: Record<string, Model>) => {
return schema.pure;
};

/**
* extract pure feature of inrelations schema
* @param schemaName - name of schema
* @returns return pure fetaures of schema that we have inrelation with it
* for example if: inerRelation of schema is equal to '{
*
* "posts": { schemaName: "post", type: "many" },
* }'
* output of this function is equal to :{
* "posts": array({
* "id": string(),
* "title": string(),
* "content": string(),
* }),}
*/
const getPureFromInRel = (schemaName: string) => {
const schema = getSchema(schemaName);
let pureSchemas = {};
for (const property in schema.inrelation) {
// console.log(`${property}: ${object[property]}`);
pureSchemas = {
...pureSchemas,
[property]: schema.inrelation[property].type === "one"
? object(schemas[schema.inrelation[property].schemaName]?.pure)
: array(
object(schemas[schema.inrelation[property].schemaName]?.pure),
),
[property]:
schema.inrelation[property].type === "one"
? object(schemas[schema.inrelation[property].schemaName]?.pure)
: array(
object(schemas[schema.inrelation[property].schemaName]?.pure)
),
};
}
return pureSchemas;
};

/**
* extract pure feature of outrelation of schema
* @param schemaName - name of schema
* @returns return pure fetaures of schema that we have outrelation with it
* for example if: outrelation of schema is equal to '{
*
* "comments": {
* schemaName: "comment",
* number: 50,
* sort: { filed: "id", order: "desc" },
* },
* }'
* output of this function is equal to :{
* "comments": array({
* "id": string(),
* "content": string(),
* }),
* }
*/
const getPureFromOutRel = (schemaName: string) => {
const schema = getSchema(schemaName);
let pureSchemas = {};
@@ -48,20 +124,61 @@ export const schemaFns = (schemas: Record<string, Model>) => {
pureSchemas = {
...pureSchemas,
[property]: array(
object(schemas[schema.outrelation[property].schemaName]?.pure),
object(schemas[schema.outrelation[property].schemaName]?.pure)
),
};
}
return pureSchemas;
};

/**
* create embed features, embed feature is equal to all of pure features of inerRelations and outerRelations
* @param schemaName - name of schema
* @returns return embedd feature of schema
* for example
* {
* "posts": array({
* "id": string(),
* "title": string(),
* "content": string(),
* }),
* "comments": array({
* "id": string(),
* "content": string(),
* }),
* }
*/
const createEmbedded = (schemaName: string) => {
return {
...getPureFromInRel(schemaName),
...getPureFromOutRel(schemaName),
};
};

/**
* create struct features, struct feature is used for create client of db.
* struct feature is include pure feature and embed features
* @param schemaName - name of schema that we want struct feature
* @returns return struct feature
* for example :
* assign(
* object({
* "id": string(),
* "content": string(),
* }),
* object({
* "user": object({
* "id": string(),
* "name": string(),
* "age": number(),
* }),
* "post": object({
* "id": string(),
* "title": string(),
* "content": string(),
* }),
* }),
* ),
*/
const createStruct = (schemaName: string) => {
const schema = getSchema(schemaName);

57 changes: 45 additions & 12 deletions src/models/types.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,55 @@
import { Struct } from "../deps.ts";

/**
* PureModel is interface of pure feature,
* pure feature is an intrinsic feature of an schema. Which are embedded in other schemas
* @public
*/
export interface PureModel {
[key: string]: Struct<any>;
[key: string]: Struct<any>;
}

/**
* if schema has relation with other schema and in SQL that we keep foreign key.
* store in InRelation feature
* @public
*/
export interface InRelation {
schemaName: string;
type: "one" | "many";
/**
* name of schema that this schema has relation with
*/
schemaName: string;
/**
* type of relation if equal to one: this schema record one object from other schema else
* this schema record array of object from other schema
*/
type: "one" | "many";
}

/**
* if schema has relation with other schema and in SQL that we dont keep foriegn key.
* store in OutRelation feature
* and usually the number of it greater thant of 50
* @public
*/
export interface OutRelation {
schemaName: string;
number: number;
sort: { field: string; order: "asc" | "desc" };
/**
* name of schema that this schema has relation with
*/
schemaName: string;
/**
* number of value that we want to keep
*/
number: number;
/**
* sort : {field , order} - field of sort , and order of sort
*/
sort: { field: string; order: "asc" | "desc" };
}

/**
* this model includes :pure feature , inrelation feature and outrelation feacture
* @public
*/
export interface Model {
pure: PureModel;
inrelation: Record<string, InRelation>;
outrelation: Record<string, OutRelation>;
pure: PureModel;
inrelation: Record<string, InRelation>;
outrelation: Record<string, OutRelation>;
}
38 changes: 22 additions & 16 deletions src/server/mod.ts
Original file line number Diff line number Diff line change
@@ -7,18 +7,25 @@ import { generateSchemTypes } from "../types/mod.ts";
import { lesanFns } from "../utils/mod.ts";
import { runPlayground } from "./playground/mod.ts";

export const lesanServer = (
schemasObj: ISchema,
actsObj: Services,
) => {
const runServer = async (
{ port, playground, typeGeneration }: {
port: number;
playground: boolean;
typeGeneration: boolean;
},
) => {
typeGeneration && await generateSchemTypes(schemasObj);
/**
* this function is for run Server and get request of client and send response of request for client
* @param port - port of listen
* @param playground - use playground or not?
* @param db - connection of DB
* @param typeGeneration -
*/

export const lesanServer = (schemasObj: ISchema, actsObj: Services) => {
const runServer = async ({
port,
playground,
typeGeneration,
}: {
port: number;
playground: boolean;
typeGeneration: boolean;
}) => {
typeGeneration && (await generateSchemTypes(schemasObj));
const handler = async (request: Request): Promise<Response> => {
try {
// return request.method === "GET"
@@ -28,16 +35,15 @@ export const lesanServer = (
} catch (e) {
return new Response(
`Somthing has wrong =>> :: ${
e.message ||
"we do not know anything !!! sorry"
e.message || "we do not know anything !!! sorry"
}`,
{ status: 501 },
{ status: 501 }
);
}
};

console.log(
`HTTP webserver running. Access it at: http://localhost:${port}/`,
`HTTP webserver running. Access it at: http://localhost:${port}/`
);
await serve(handler, { port });
playground && runPlayground();
164 changes: 96 additions & 68 deletions src/utils/checkWants.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,115 @@
import { throwError } from "./mod.ts";

/**
* details of input is include set , get
* @public
*/
export interface Details {
set: Record<string, any>;
get: Record<string, any>;
/**
* set of query
*/
set: Record<string, any>;
/**
* get pf query
* What the client wants to return
*/
get: Record<string, any>;
}

/**
* interface is type of input of Actions
* @public
*/
export interface Body {
service?: string;
contents: "dynamic" | "static";
wants: {
model: string;
act: string;
};
details: Details;
/**
* name of service
* "main" | "blog" | "ecommerce"
*/
service?: string;
/**
* type of contents : dynamic | static
*/
contents: "dynamic" | "static";
/**
* model : schema name that client wants
* act : name of Actions
*/
wants: {
model: string;
act: string;
};
/**
* details of request set and get
*/
details: Details;
}

const decodeBody = async (req: Request): Promise<Body> => {
/**
* @function
* decode body of request when content type is application/json
*/
const decodeJsonBody = async () =>
req.body
? JSON.parse(await req.text()) as Body
: throwError("Your request body is incorrect");
/**
* @function
* decode body of request when content type is application/json
*/
const decodeJsonBody = async () =>
req.body
? JSON.parse(await req.text()) as Body
: throwError("Your request body is incorrect");

/**
* @function
* decode all files and funql body when type of content type is multipart/form-data
* @remarks it puts all of files in set of details of request body
* @return parsed body with uploaded files
* @example we recommend to use postman or other client lib fot handling boundary field and other fields
*/
const decodeMultiPartBody = async () => {
// finds boundary of form data
// const boundary = contentType.match(/boundary=([^\s]+)/)?.[1];
/**
* @function
* decode all files and funql body when type of content type is multipart/form-data
* @remarks it puts all of files in set of details of request body
* @return parsed body with uploaded files
* @example we recommend to use postman or other client lib fot handling boundary field and other fields
*/
const decodeMultiPartBody = async () => {
// finds boundary of form data
// const boundary = contentType.match(/boundary=([^\s]+)/)?.[1];

const getFileFormData: () => Promise<Body> = async () => {
const fd = await req.formData();
// for (const f of fd.entries()) {
// if (!(f[1] instanceof File)) {
// continue;
// }
// const fileData = new Uint8Array(await f[1].arrayBuffer());
// await Deno.writeFile("./files/" + f[1].name, fileData);
// }
const returnBody: (body: string) => Body = (body) => {
const parsedBody = JSON.parse(body) as Body;
parsedBody &&
parsedBody.details &&
parsedBody.details.set &&
(parsedBody.details.set = {
...parsedBody.details.set,
formData: fd,
});
return parsedBody;
};
const getFileFormData: () => Promise<Body> = async () => {
const fd = await req.formData();
// for (const f of fd.entries()) {
// if (!(f[1] instanceof File)) {
// continue;
// }
// const fileData = new Uint8Array(await f[1].arrayBuffer());
// await Deno.writeFile("./files/" + f[1].name, fileData);
// }
const returnBody: (body: string) => Body = (body) => {
const parsedBody = JSON.parse(body) as Body;
parsedBody
&& parsedBody.details
&& parsedBody.details.set
&& (parsedBody.details.set = {
...parsedBody.details.set,
formData: fd,
});
return parsedBody;
};

const body = fd.get("lesan-body") ? fd.get("lesan-body") as string : "{}";
const body = fd.get("lesan-body") ? fd.get("lesan-body") as string : "{}";

return body
? returnBody(body)
: throwError("somthing wrong with your file");
};
return body
? returnBody(body)
: throwError("somthing wrong with your file");
};

return req.body
? await getFileFormData()
: throwError("Your body is incorrect");
};
return req.body
? await getFileFormData()
: throwError("Your body is incorrect");
};

const contentType = req.headers.get("content-type") || "";
return contentType.includes("application/json")
? await decodeJsonBody()
: contentType.includes("multipart/form-data")
? await decodeMultiPartBody()
: throwError("content type is not correct");
const contentType = req.headers.get("content-type") || "";
return contentType.includes("application/json")
? await decodeJsonBody()
: contentType.includes("multipart/form-data")
? await decodeMultiPartBody()
: throwError("content type is not correct");
};

export const parsBody = async (req: Request, port: number) => {
const parsedBody = await decodeBody(req);
const url = req.url.split(`${port}`)[1];
const parsedBody = await decodeBody(req);
const url = req.url.split(`${port}`)[1];

return req.method === "POST" && url === "/lesan"
? parsedBody
: throwError("you most send a post request to /lesan url");
return req.method === "POST" && url === "/lesan"
? parsedBody
: throwError("you most send a post request to /lesan url");
};
239 changes: 148 additions & 91 deletions src/utils/serveLesan.ts
Original file line number Diff line number Diff line change
@@ -2,108 +2,165 @@ import { acts, Services } from "../acts/mod.ts";
import { assert, enums } from "../deps.ts";
import { Body, parsBody } from "./mod.ts";

/**
* function of lesan
* @param actsObj -all services
* @returns - {
* runAct, : runc action that client wants to run
checkActs,
checkModels,
checkContetType,
serveLesan,}
*/
export const lesanFns = (actsObj: Services) => {
const runAct = async (body: Body) => {
const bodyService = body.service || "main";
const act = acts(actsObj).getAct(
bodyService,
body.contents,
body.wants.model,
body.wants.act,
);
assert(body.details, act.validator);

return await act.fn(body);
};
/**
* runc action that client wants to run
* @param {Body} body - input of act
* @returns answer of function that executed
*/
const runAct = async (body: Body) => {
const bodyService = body.service || "main";
const act = acts(actsObj).getAct(
bodyService,
body.contents,
body.wants.model,
body.wants.act,
);
assert(body.details, act.validator);

const checkActs = async (body: Body) => {
const bodyService = body.service || "main";
const actKeys = acts(actsObj).getActsKeys(
bodyService,
body.contents,
body.wants.model,
);
const getedActs = enums(actKeys);
assert(body.wants.act, getedActs);
return await act.fn(body);
};

return await runAct(body);
};
/**
* check action that client wants is true and if request is true, this function will run runAct's function
* @param {@link Body} body - is type of Body
* @returns return output of runAct's function
*/
const checkActs = async (body: Body) => {
const bodyService = body.service || "main";
const actKeys = acts(actsObj).getActsKeys(
bodyService,
body.contents,
body.wants.model,
);
const getedActs = enums(actKeys);
assert(body.wants.act, getedActs);

const checkModels = async (body: Body) => {
const bodyService = body.service || "main";
const models = body.contents === "static"
? enums(acts(actsObj).getStaticKeys(bodyService))
: enums(acts(actsObj).getDynamicKeys(bodyService));
assert(body.wants.model, models);
return await runAct(body);
};

return await checkActs(body);
};
/**
* check model that client wants is true?
* @param {@link Body} body - is type of Body
* @return if it is exist in dynamickey or static key, it will run CkeckActs
* else throw Error
*/
const checkModels = async (body: Body) => {
const bodyService = body.service || "main";
const models = body.contents === "static"
? enums(acts(actsObj).getStaticKeys(bodyService))
: enums(acts(actsObj).getDynamicKeys(bodyService));
assert(body.wants.model, models);

const checkContetType = async (body: Body) => {
const content = enums(["dynamic", "static"]);
assert(body.contents, content);
return await checkActs(body);
};

return await checkModels(body);
};
/**
* check Content-Type that client sends is true?
* @param {@link Body} body - is type of Body
* @returns if ContentType is true, it will run checkModels , else throwError
*/
const checkContetType = async (body: Body) => {
const content = enums(["dynamic", "static"]);
assert(body.contents, content);

async function postData(url = "", body: Body, headers = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
mode: "cors", // no-cors, *cors, same-origin
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "same-origin", // include, *same-origin, omit
headers: {
"Content-Type": "application/json",
...headers,
},
redirect: "follow", // manual, *follow, error
referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(body), // body data type must match "Content-Type" header
});
return response.json(); // parses JSON response into native JavaScript objects
}
return await checkModels(body);
};

// TODO: do not implement fetch completly it shoud be fetch data with the same req but change the req.body.service to main
const fetchService = async (
headers: {},
body: Body,
serviceValue: string,
) => {
const result = await postData(
serviceValue,
{ ...body, service: "main" },
headers,
);
return result;
};
/**
* sed request to another service
* @param url - that we want to sed request for it
* @param Body of request has been sent by client
* @param header request has been sent by client
* @returns return response of request
*/
async function postData(url = "", body: Body, headers = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
mode: "cors", // no-cors, *cors, same-origin
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "same-origin", // include, *same-origin, omit
headers: {
"Content-Type": "application/json",
...headers,
},
redirect: "follow", // manual, *follow, error
referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(body), // body data type must match "Content-Type" header
});
return response.json(); // parses JSON response into native JavaScript objects
}

const checkServices = async (req: Request, port: number) => {
const body = (await parsBody(req, port)) as Body;
const serviceKeys = acts(actsObj).getServiceKeys();
const servic = enums(serviceKeys);
const bodyService = body.service || "main";
assert(bodyService, servic);
const serviceValue = acts(actsObj).getService(bodyService);
return typeof serviceValue === "string"
? await fetchService(req.headers, body, serviceValue)
: await checkContetType(body);
};
// TODO: do not implement fetch completly it shoud be fetch data with the same req but change the req.body.service to main
/**
* if we have url of service we send request to specif service with this function
* and this function change service name becuse we want to request to another url of service
* @param Body of request has been sent by client
* @param header request has been sent by client
* @param serviceValue - this url of service that we want to send request to it
*/
const fetchService = async (
headers: {},
body: Body,
serviceValue: string,
) => {
const result = await postData(
serviceValue,
{ ...body, service: "main" },
headers,
);
return result;
};

const serveLesan = async (req: Request, port: number) => {
const response = async () => {
return await checkServices(req, port);
/**
* checkServices: if request for mainService or in the core Service exists other Act of service this function runs checkContetType's function
* else run fetchService's function and send request to service directrly
* @returns response of request
* @param req - request of client
* @param port - port of request
*/
const checkServices = async (req: Request, port: number) => {
const body = (await parsBody(req, port)) as Body;
const serviceKeys = acts(actsObj).getServiceKeys();
const servic = enums(serviceKeys);
const bodyService = body.service || "main";
assert(bodyService, servic);
const serviceValue = acts(actsObj).getService(bodyService);
return typeof serviceValue === "string"
? await fetchService(req.headers, body, serviceValue)
: await checkContetType(body);
};
/**
* this is main function that call checkServices
* @param req - request of client
* @param port - port of request
* @returns return response of request
*/
const serveLesan = async (req: Request, port: number) => {
const response = async () => {
return await checkServices(req, port);
};

return new Response(
JSON.stringify({ body: await response(), success: true }),
);
};
return {
runAct,
checkActs,
checkModels,
checkContetType,
serveLesan,
};
return new Response(
JSON.stringify({ body: await response(), success: true }),
);
};
return {
runAct,
checkActs,
checkModels,
checkContetType,
serveLesan,
};
};
7 changes: 6 additions & 1 deletion src/utils/throwError.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* get message and throw error
* @param msg - message of error
* @returns throwError with input message
*/
export const throwError = (msg?: string) => {
throw new Error(msg);
throw new Error(msg);
};

0 comments on commit 6037633

Please sign in to comment.