Skip to content

Commit

Permalink
added data and service components.
Browse files Browse the repository at this point in the history
  • Loading branch information
eser committed Sep 26, 2022
1 parent cf7e49c commit 5000746
Show file tree
Hide file tree
Showing 15 changed files with 407 additions and 25 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ cloud-function runtimes and web apis.
| [Directives](src/directives/) | Rules | |
| [Standards](src/standards/) | Abstraction | |
| [FP](src/fp/) | Functions Library | Tools for functional programming |
| [Data](src/data/) | Objects Library | Data Objects and Patterns |
| [Environment](src/environment/) | Objects Library | Environment adapters |
| [Formatters](src/formatters/) | Objects Library | Object serializers/deserializers |
| [Options](src/options/) | Manager | Configuration library |
| [DI](src/di/) | Manager | Dependency injection library |
| [I18N](src/i18n/) | Manager | Internationalization library |
| [Functions](src/functions/) | Manager | Functions runtime |
| [Service](src/service/) | Framework | A micro http framework |
| [Web](src/web/) | Framework | A web framework implementation |


Expand Down
1 change: 1 addition & 0 deletions src/data/deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as mongo from "https://deno.land/x/[email protected]/mod.ts";
2 changes: 2 additions & 0 deletions src/data/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./mongo.ts";
export * from "./repository.ts";
50 changes: 50 additions & 0 deletions src/data/mongo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { mongo } from "./deps.ts";
import { type Repository } from "./repository.ts";

class MongoRepository<T = mongo.Bson.Document> implements Repository<T, "_id"> {
collection: mongo.Collection<T>;

constructor(collection: mongo.Collection<T>) {
this.collection = collection;
}

get(id: string): Promise<T | undefined> {
return this.collection.findOne({
_id: new mongo.ObjectId(id),
});
}

getAll(): Promise<T[]> {
return this.collection.find().toArray();
}

async add(data: Omit<T, "_id">): Promise<string> {
const id = await this.collection.insertOne(data);

return String(id);
}

async update(id: string, data: Partial<T>): Promise<void> {
await this.collection.updateOne(
{ _id: new mongo.ObjectId(id) },
// @ts-ignore a bug in type definition
{ $set: data },
);
}

async replace(id: string, data: Omit<T, "_id">): Promise<void> {
await this.collection.replaceOne(
{ _id: new mongo.ObjectId(id) },
// @ts-ignore a bug in type definition
data,
);
}

async remove(id: string): Promise<void> {
await this.collection.deleteOne(
{ _id: new mongo.ObjectId(id) },
);
}
}

export { MongoRepository };
14 changes: 14 additions & 0 deletions src/data/repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// deno-lint-ignore no-explicit-any
interface Repository<T = unknown, K extends keyof any = "id"> {
get(id: string): Promise<T | undefined>;
getAll(): Promise<T[]>;

add(data: Omit<T, K>): Promise<string>;
update(id: string, data: Partial<T>): Promise<void>;
replace(id: string, data: Omit<T, K>): Promise<void>;
remove(id: string): Promise<void>;

// TODO getCursor, bulkInsert, upsert, count, aggregate, etc.
}

export { type Repository };
1 change: 1 addition & 0 deletions src/mod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * as data from "./data/mod.ts";
export * as di from "./di/mod.ts";
export * as environment from "./environment/mod.ts";
export * as formatters from "./formatters/mod.ts";
Expand Down
59 changes: 37 additions & 22 deletions src/options/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,60 +8,75 @@ interface BaseOptions {
type Options<T> = BaseOptions & Partial<T>;

interface WrappedEnv {
readString: (key: string, defaultValue?: string) => string | undefined;
readEnum: (
readString: <T extends string>(
key: string,
values: string[],
defaultValue?: string,
) => string | undefined;
readInt: (key: string, defaultValue?: number) => number | undefined;
readBool: (key: string, defaultValue?: boolean) => boolean | undefined;
defaultValue?: T,
) => T | undefined;
readEnum: <T extends string>(
key: string,
values: T[],
defaultValue?: T,
) => T | undefined;
readInt: <T extends number>(key: string, defaultValue?: T) => T | undefined;
readBool: <T extends boolean>(key: string, defaultValue?: T) => T | undefined;
}

// public functions
const wrapEnv = (env: LoadEnvResult): WrappedEnv => {
return {
readString: (key: string, defaultValue?: string): string | undefined => {
return env[key] ?? defaultValue;
readString: <T extends string>(
key: string,
defaultValue?: T,
): T | undefined => {
return env[key] as T ?? defaultValue;
},
readEnum: (
readEnum: <T extends string>(
key: string,
values: string[],
defaultValue?: string,
): string | undefined => {
values: T[],
defaultValue?: T,
): T | undefined => {
if (env[key] === undefined) {
return defaultValue;
}

if (values.includes(env[key])) {
return env[key];
if (values.includes(env[key] as T)) {
return env[key] as T;
}

return defaultValue;
},
readInt: (key: string, defaultValue?: number): number | undefined => {
readInt: <T extends number>(
key: string,
defaultValue?: T,
): T | undefined => {
if (env[key] === undefined) {
return defaultValue;
}

return parseInt(env[key], 10);
return parseInt(env[key], 10) as T;
},
readBool: (key: string, defaultValue?: boolean): boolean | undefined => {
readBool: <T extends boolean>(
key: string,
defaultValue?: T,
): T | undefined => {
if (env[key] === undefined) {
return defaultValue;
}

if (["1", "true", true].includes(env[key].trim().toLowerCase())) {
return true;
return true as T;
}

return false;
return false as T;
},
};
};

const loadOptions = async <T>(
loader: (wrappedEnv: WrappedEnv, options: Options<T>) => Options<T>,
loader: (
wrappedEnv: WrappedEnv,
options: Options<T>,
) => Promise<Options<T> | void> | Options<T> | void,
options?: LoadEnvOptions,
): Promise<Options<T>> => {
const env = await loadEnv(options);
Expand All @@ -72,7 +87,7 @@ const loadOptions = async <T>(
envName: env.name,
};

const result = loader(wrappedEnv, newOptions);
const result = await loader(wrappedEnv, newOptions);

return result ?? newOptions;
};
Expand Down
5 changes: 5 additions & 0 deletions src/service/deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * as asserts from "https://deno.land/[email protected]/testing/asserts.ts";
export * as djwt from "https://deno.land/x/[email protected]/mod.ts";
export * as log from "https://deno.land/[email protected]/log/mod.ts";
export * as logLevels from "https://deno.land/[email protected]/log/levels.ts";
export * as oak from "https://deno.land/x/[email protected]/mod.ts";
51 changes: 51 additions & 0 deletions src/service/http-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { oak } from "./deps.ts";

type Application = oak.Application;
type Middleware = oak.Middleware;
type Router = oak.Router;
type State = oak.State;
type Context = oak.Context & {
params: Record<string, string>;
};
type RouteParams<Route extends string> = oak.RouteParams<Route>;
type Route<
R extends string,
P extends RouteParams<R> = RouteParams<R>,
// deno-lint-ignore no-explicit-any
S extends State = Record<string, any>,
> = oak.Route<R, P, S>;
type RouterMiddleware<
R extends string,
P extends RouteParams<R> = RouteParams<R>,
// deno-lint-ignore no-explicit-any
S extends State = Record<string, any>,
> = oak.RouterMiddleware<R, P, S>;
type RouterContext<
R extends string,
P extends RouteParams<R> = RouteParams<R>,
// deno-lint-ignore no-explicit-any
S extends State = Record<string, any>,
> = oak.RouterContext<R, P, S>;

type HttpMethods =
| "all"
| "get"
| "post"
| "patch"
| "put"
| "delete"
| "head"
| "options";

export {
type Application,
type Context,
type HttpMethods,
type Middleware,
type Route,
type RouteParams,
type Router,
type RouterContext,
type RouterMiddleware,
type State,
};
15 changes: 15 additions & 0 deletions src/service/middlewares/timer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { log } from "../deps.ts";

// deno-lint-ignore no-explicit-any
const timerMiddleware = async (ctx: any, next: any) => {
const start = Date.now();

await next();

const ms = Date.now() - start;

// ctx.response.headers.set("X-Response-Time", `${ms}ms`);
log.info(`${ctx.request.method} ${ctx.request.url} - ${ms}ms`);
};

export { timerMiddleware, timerMiddleware as default };
3 changes: 3 additions & 0 deletions src/service/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./http-types.ts";
export * from "./options.ts";
export * from "./service.ts";
30 changes: 30 additions & 0 deletions src/service/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { logLevels } from "./deps.ts";
import * as options from "../options/mod.ts";

// interface definitions
interface ServiceOptionsValues {
port: number;
logs: logLevels.LevelName;
}

type ServiceOptions = options.Options<ServiceOptionsValues>;

// public functions
const loadServiceOptions = async (): Promise<ServiceOptions> => {
const serviceOptions = await options.loadOptions<ServiceOptionsValues>(
(env, opts) => {
opts.port = env.readInt("PORT", 3000);
opts.logs = env.readEnum<logLevels.LevelName>("LOGS", [
"DEBUG",
"INFO",
"WARNING",
"ERROR",
"CRITICAL",
], "INFO");
},
);

return serviceOptions;
};

export { loadServiceOptions, type ServiceOptions };
Loading

0 comments on commit 5000746

Please sign in to comment.