feat(release): initial release
This commit is contained in:
30
.github/workflows/publish.yml
vendored
Normal file
30
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
name: Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Deno
|
||||||
|
uses: maximousblk/setup-deno@v2 # Installs latest version
|
||||||
|
|
||||||
|
- run: deno test
|
||||||
|
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Publish package
|
||||||
|
run: npx jsr publish
|
||||||
18
.github/workflows/test.yml
vendored
Normal file
18
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Deno
|
||||||
|
uses: maximousblk/setup-deno@v2 # Installs latest version
|
||||||
|
|
||||||
|
- run: deno test
|
||||||
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"deno.enable": true,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "denoland.vscode-deno",
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": "explicit"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
deno.json
Normal file
22
deno.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "@valkyr/inverse",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"exports": "./mod.ts",
|
||||||
|
"imports": {
|
||||||
|
"std/": "https://deno.land/std@0.224.0/"
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
".vscode"
|
||||||
|
],
|
||||||
|
"lint": {
|
||||||
|
"rules": {
|
||||||
|
"exclude": [
|
||||||
|
"no-explicit-any",
|
||||||
|
"require-await"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fmt": {
|
||||||
|
"lineWidth": 120
|
||||||
|
}
|
||||||
|
}
|
||||||
40
deno.lock
generated
Normal file
40
deno.lock
generated
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"version": "3",
|
||||||
|
"remote": {
|
||||||
|
"https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
|
||||||
|
"https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
|
||||||
|
"https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
|
||||||
|
"https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
|
||||||
|
"https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
|
||||||
|
"https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
|
||||||
|
"https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
|
||||||
|
"https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
|
||||||
|
"https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
|
||||||
|
"https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e",
|
||||||
|
"https://deno.land/std@0.224.0/testing/_test_suite.ts": "f10a8a6338b60c403f07a76f3f46bdc9f1e1a820c0a1decddeb2949f7a8a0546",
|
||||||
|
"https://deno.land/std@0.224.0/testing/bdd.ts": "3e4de4ff6d8f348b5574661cef9501b442046a59079e201b849d0e74120d476b"
|
||||||
|
}
|
||||||
|
}
|
||||||
115
libraries/container.ts
Normal file
115
libraries/container.ts
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import { MissingChildContainerError, MissingDependencyError } from "./errors.ts";
|
||||||
|
|
||||||
|
export class Container<T extends Tokens<T>, C extends JSON = JSON> {
|
||||||
|
readonly providers: Map<keyof T, T[keyof T]> = new Map();
|
||||||
|
readonly contexts: Map<C, Container<T, C>> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple dependency injection service using inversion of control principles
|
||||||
|
* allowing the developer to program against TypeScript types or interfaces
|
||||||
|
* with implementation details injected by service providers.
|
||||||
|
*
|
||||||
|
* @param id - Container identifier used for easier debugging.
|
||||||
|
*/
|
||||||
|
constructor(readonly id: string) {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
| Contexts
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| A container can have one or more contexts which represents a cloned version of
|
||||||
|
| the parent container. A context container is usually useful for when you want
|
||||||
|
| different types of the same provider to exist within the same dependency scope
|
||||||
|
| under a unique filterable context.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new container with the given context object. A context is an object
|
||||||
|
* we provide to the .where method used to query the container assigned to the
|
||||||
|
* given context object.
|
||||||
|
*
|
||||||
|
* @param context - Context object used to filter future .where requests.
|
||||||
|
*/
|
||||||
|
createContext(context: C): Container<T, C> {
|
||||||
|
return this.contexts.set(context, new Container<T, C>(this.id)).get(
|
||||||
|
context,
|
||||||
|
) as Container<T, C>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create or retrieve a container based on a specific context container.
|
||||||
|
*
|
||||||
|
* @param filter - Method which receives the container context object used to
|
||||||
|
* filter the specific container we want to operate on.
|
||||||
|
*/
|
||||||
|
where(filter: Filter<C>): Container<T, C> {
|
||||||
|
for (const context of Array.from(this.contexts.keys())) {
|
||||||
|
if (filter(context)) {
|
||||||
|
return this.contexts.get(context) as Container<T, C>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new MissingChildContainerError(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
| Utilities
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a token has been registered in the singleton or transient map
|
||||||
|
* of the container.
|
||||||
|
*
|
||||||
|
* @param token - Token to verify.
|
||||||
|
*/
|
||||||
|
has<K extends keyof T>(token: K): boolean {
|
||||||
|
return this.providers.has(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a transient or singleton provider against the provided token.
|
||||||
|
*
|
||||||
|
* @param token - Token to register.
|
||||||
|
* @param provider - Provider to register under the given token.
|
||||||
|
*/
|
||||||
|
set<K extends keyof T>(token: K, provider: T[K]): this {
|
||||||
|
this.providers.set(token, provider);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a transient or singleton provider instance.
|
||||||
|
*
|
||||||
|
* @param token - Token to retrieve dependency for.
|
||||||
|
* @param args - Arguments to pass to a transient provider.
|
||||||
|
*/
|
||||||
|
get<K extends keyof T>(token: K): T[K] {
|
||||||
|
const provider = this.providers.get(token);
|
||||||
|
if (!provider) {
|
||||||
|
throw new MissingDependencyError(this.id, token);
|
||||||
|
}
|
||||||
|
return provider as T[K];
|
||||||
|
}
|
||||||
|
|
||||||
|
new<K extends keyof T>(
|
||||||
|
token: K,
|
||||||
|
...args: ConstructorParameters<T[K]>
|
||||||
|
): InstanceType<T[K]> {
|
||||||
|
const provider = this.providers.get(token);
|
||||||
|
if (!provider) {
|
||||||
|
throw new MissingDependencyError(this.id, token);
|
||||||
|
}
|
||||||
|
return new (provider as any)(...(args as any));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Tokens<T> = {
|
||||||
|
[K in keyof T]: T[K];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Filter<T> = (context: T) => boolean;
|
||||||
|
|
||||||
|
export type JSON = Record<string, unknown>;
|
||||||
17
libraries/errors.ts
Normal file
17
libraries/errors.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export class MissingChildContainerError extends Error {
|
||||||
|
public readonly type = "MissingChildContainerError" as const;
|
||||||
|
|
||||||
|
constructor(id: string) {
|
||||||
|
super(`Dependency Violation: '${id}' container failed to resolve unregistered sub container`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MissingDependencyError extends Error {
|
||||||
|
public readonly type = "MissingDependencyError" as const;
|
||||||
|
|
||||||
|
constructor(id: string, token: string | number | symbol) {
|
||||||
|
super(
|
||||||
|
`Dependency Violation: '${id}' container failed to resolve unregistered dependency token: ${token.toString()}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
131
tests/container.test.ts
Normal file
131
tests/container.test.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import { assertEquals, assertExists, assertInstanceOf, assertObjectMatch, assertThrows } from "std/assert/mod.ts";
|
||||||
|
import { beforeAll, beforeEach, describe, it } from "std/testing/bdd.ts";
|
||||||
|
|
||||||
|
import { Container } from "../libraries/container.ts";
|
||||||
|
import { MissingChildContainerError, MissingDependencyError } from "../libraries/errors.ts";
|
||||||
|
import { Invoice2Go } from "./mocks/providers/invoice-2-go.ts";
|
||||||
|
import { PayPal } from "./mocks/providers/paypal.ts";
|
||||||
|
import { Stripe } from "./mocks/providers/stripe.ts";
|
||||||
|
import { Invoices } from "./mocks/services/invoices.ts";
|
||||||
|
import { Logger } from "./mocks/services/logger.ts";
|
||||||
|
import { Payments } from "./mocks/services/payments.ts";
|
||||||
|
|
||||||
|
type Context = {
|
||||||
|
provider: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Tokens = {
|
||||||
|
logger: typeof Logger;
|
||||||
|
invoices: typeof Invoices;
|
||||||
|
payments: Payments;
|
||||||
|
};
|
||||||
|
|
||||||
|
const CONTAINER_ID = "mock";
|
||||||
|
|
||||||
|
const isProvider = (provider: string) => (context: Context) => provider === context.provider;
|
||||||
|
|
||||||
|
describe("Inverse Module > Container", () => {
|
||||||
|
describe("when .where method is used", () => {
|
||||||
|
const paypal: Context = { provider: "paypal" };
|
||||||
|
const stripe: Context = { provider: "stripe" };
|
||||||
|
|
||||||
|
let container: Container<Tokens, Context>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
container = new Container<Tokens, Context>(CONTAINER_ID);
|
||||||
|
container.createContext(paypal);
|
||||||
|
container.createContext(stripe);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add a sub container when argument is a context object", () => {
|
||||||
|
assertExists(container.contexts.get(paypal));
|
||||||
|
assertExists(container.contexts.get(stripe));
|
||||||
|
assertEquals(container.contexts.get({ provider: "skrill" }), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set a sub container dependency when a filter method is provided", async () => {
|
||||||
|
container.where(isProvider("paypal")).set("payments", new PayPal());
|
||||||
|
container.where(isProvider("stripe")).set("payments", new Stripe());
|
||||||
|
|
||||||
|
assertObjectMatch(
|
||||||
|
await container.where(isProvider("paypal")).get("payments").create("xyz", "usd", 100),
|
||||||
|
{
|
||||||
|
customerId: "xyz",
|
||||||
|
provider: "paypal",
|
||||||
|
currency: "usd",
|
||||||
|
amount: 100,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assertObjectMatch(
|
||||||
|
await container.where(isProvider("stripe")).get("payments").create("xyz", "jpy", 15000),
|
||||||
|
{
|
||||||
|
customerId: "xyz",
|
||||||
|
provider: "stripe",
|
||||||
|
currency: "jpy",
|
||||||
|
amount: 15000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw error when sub container does not exist", () => {
|
||||||
|
assertThrows(() => container.where(isProvider("skrill")), MissingChildContainerError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw error when sub container does not have a registered dependency", () => {
|
||||||
|
assertThrows(() => container.where(isProvider("paypal")).get("payments"), MissingDependencyError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when .has() method is used", () => {
|
||||||
|
const container = new Container<Tokens>(CONTAINER_ID);
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
container.set("payments", new PayPal());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for registered dependencies", () => {
|
||||||
|
assertEquals(container.has("payments"), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for unregistered dependencies", () => {
|
||||||
|
assertEquals(container.has("invoices"), false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when .set() method is used", () => {
|
||||||
|
const container = new Container<Tokens>(CONTAINER_ID);
|
||||||
|
|
||||||
|
it("should set new dependency", () => {
|
||||||
|
assertEquals(container.set("payments", new PayPal()).has("payments"), true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when .get() method is used", () => {
|
||||||
|
const container = new Container<Tokens>(CONTAINER_ID);
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
container.set("logger", Logger);
|
||||||
|
container.set("payments", new Stripe());
|
||||||
|
container.set("invoices", Invoice2Go);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve correct instances", () => {
|
||||||
|
assertInstanceOf(container.new("logger"), Logger);
|
||||||
|
assertInstanceOf(container.get("payments"), Stripe);
|
||||||
|
assertInstanceOf(container.new("invoices", "xyz"), Invoice2Go);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve correct results", async () => {
|
||||||
|
assertObjectMatch(await container.get("payments").create("xyz", "usd", 100), {
|
||||||
|
provider: "stripe",
|
||||||
|
currency: "usd",
|
||||||
|
amount: 100,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve a transient provider with correct arguments", () => {
|
||||||
|
assertEquals(container.new("invoices", "xyz").provider, "Invoice2Go");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
5
tests/mocks/providers/invoice-2-go.ts
Normal file
5
tests/mocks/providers/invoice-2-go.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Invoices } from "../services/invoices.ts";
|
||||||
|
|
||||||
|
export class Invoice2Go extends Invoices {
|
||||||
|
public readonly provider = "Invoice2Go";
|
||||||
|
}
|
||||||
14
tests/mocks/providers/paypal.ts
Normal file
14
tests/mocks/providers/paypal.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { type Currency, type Payment, Payments } from "../services/payments.ts";
|
||||||
|
|
||||||
|
export class PayPal extends Payments {
|
||||||
|
public async create(customerId: string, currency: Currency, amount: number): Promise<Payment> {
|
||||||
|
return {
|
||||||
|
paymentId: "xyz",
|
||||||
|
customerId,
|
||||||
|
provider: "paypal",
|
||||||
|
status: "created",
|
||||||
|
currency,
|
||||||
|
amount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
14
tests/mocks/providers/stripe.ts
Normal file
14
tests/mocks/providers/stripe.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { type Currency, type Payment, Payments } from "../services/payments.ts";
|
||||||
|
|
||||||
|
export class Stripe extends Payments {
|
||||||
|
public async create(customerId: string, currency: Currency, amount: number): Promise<Payment> {
|
||||||
|
return {
|
||||||
|
paymentId: "xyz",
|
||||||
|
customerId,
|
||||||
|
provider: "stripe",
|
||||||
|
status: "created",
|
||||||
|
currency,
|
||||||
|
amount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
5
tests/mocks/services/invoices.ts
Normal file
5
tests/mocks/services/invoices.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export abstract class Invoices {
|
||||||
|
public abstract readonly provider: string;
|
||||||
|
|
||||||
|
constructor(public readonly paymentId: string) {}
|
||||||
|
}
|
||||||
1
tests/mocks/services/logger.ts
Normal file
1
tests/mocks/services/logger.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export class Logger {}
|
||||||
22
tests/mocks/services/payments.ts
Normal file
22
tests/mocks/services/payments.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
export abstract class Payments {
|
||||||
|
public abstract create(customerId: string, currency: Currency, amount: number): Promise<Payment>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
| Types
|
||||||
|
|--------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type Payment = {
|
||||||
|
paymentId: string;
|
||||||
|
customerId: string;
|
||||||
|
provider: string;
|
||||||
|
status: Status;
|
||||||
|
currency: Currency;
|
||||||
|
amount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Status = "created" | "processing" | "failed" | "processed";
|
||||||
|
|
||||||
|
export type Currency = "usd" | "eur" | "jpy";
|
||||||
Reference in New Issue
Block a user