diff --git a/README.org b/README.org index 87bd0c0..bb53cf7 100644 --- a/README.org +++ b/README.org @@ -804,3 +804,31 @@ Please note that =.get(..)= and =.$get(..)= have same prototype and usage than other function and do not require you to build a query string as it'll encode in the URL as a querystring the data you've provided. + +* Contributing to Lokapi + +This package is using ~npm~ to track dependendies, so you can install them +with: + + #+begin_src sh + npm install + #+end_src + +As this package is written in =typescript=. You can transpile to +=javascript= and transpile on file change with: + + #+begin_src sh + ## Compile and watch + npx tspc -w + #+end_src + +Tests are managed through =vitest= + + + #+begin_src sh + ## Run test once + npm run test + #+end_src + +Note that you can also use ~npx vitest~ command to launch tests in +watch mode. diff --git a/package.json b/package.json index f214787..9d08cb9 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,34 @@ { - "name": "@lokavaluto/lokapi", - "version": "%%version%%", - "description": "Lokavaluto API", - "main": "build/index.js", - "types": "build/index.d.ts", - "scripts": { - "prepare": "tsc", - "lint": "eslint --ignore-path .gitignore --ext .js,.ts .", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "Valentin LAB", - "license": "MIT", - "repository": { - "type": "git", - "url": "git@github.com:Lokavaluto/lokapi.git" - }, - "devDependencies": { - "@0k/prettier": "^0.2.1", - "@types/node": "^15.12.2", - "@typescript-eslint/eslint-plugin": "^4.29.0", - "@typescript-eslint/parser": "^4.29.0", - "eslint": "^7.32.0", - "eslint-config-standard": "^16.0.3", - "typedoc": "^0.22.8", - "typescript": "^4.4.4" - }, - "dependencies": { - "@0k/types-request": "^1.0.0", - "qs": "^6.10.1" - } + "name": "@lokavaluto/lokapi", + "version": "0.1.1-alpha.202505101504", + "description": "Lokavaluto API", + "main": "build/index.js", + "types": "build/index.d.ts", + "scripts": { + "prepare": "tsc", + "lint": "eslint --ignore-path .gitignore --ext .js,.ts .", + "test": "vitest run" + }, + "author": "Valentin LAB", + "license": "MIT", + "repository": { + "type": "git", + "url": "git@github.com:Lokavaluto/lokapi.git" + }, + "devDependencies": { + "@0k/prettier": "^0.2.1", + "@types/node": "^22.15.3", + "@typescript-eslint/eslint-plugin": "^4.29.0", + "@typescript-eslint/parser": "^4.29.0", + "eslint": "^7.32.0", + "eslint-config-standard": "^16.0.3", + "jsdom": "^26.1.0", + "typedoc": "^0.22.8", + "typescript": "^4.7.4", + "vitest": "^3.1.2" + }, + "dependencies": { + "@0k/types-request": "^1.0.0", + "qs": "^6.10.1" + } } diff --git a/src/backend/odoo/account.ts b/src/backend/odoo/account.ts index c140280..87ba7f8 100644 --- a/src/backend/odoo/account.ts +++ b/src/backend/odoo/account.ts @@ -1,7 +1,9 @@ +import * as t from '../../type' + import { BridgeObject } from '..' -export default class Account extends BridgeObject { +export default abstract class Account extends BridgeObject { public async getPendingTopUp() { let requests = await this.backends.odoo.$get( @@ -12,4 +14,5 @@ export default class Account extends BridgeObject { requests.map((e: any) => this.parent.makeCreditRequest(e)) ) } + } diff --git a/src/backend/odoo/recipient.ts b/src/backend/odoo/recipient.ts new file mode 100644 index 0000000..ae762f3 --- /dev/null +++ b/src/backend/odoo/recipient.ts @@ -0,0 +1,31 @@ +import * as t from '../../type' + +import { Contact } from './contact' +import UserAccount from './userAccount' + + +export default abstract class Recipient extends Contact { + + abstract fromUserAccount: UserAccount + abstract walletInternalId: string + + /** + * Request if administrative backend allows current account to + * transfer to given recipient account. + * + * @throws {RequestFailed, APIRequestFailed, InvalidCredentials, InvalidJson} + * + * @param recipient The recipient to which we check if transfer is allowed + * + * @returns boolean + */ + public async isTransferAllowedByAdministrativeBackend (): Promise { + debugger + const backendType = this.backendId.split(":")[0] + return await this.backends.odoo.$get('/partner/is_transaction_allowed', { + sender_wallet_ident: this.fromUserAccount.internalId, + recipient_wallet_ident: this.walletInternalId + }) + } + +} diff --git a/src/backend/odoo/userAccount.ts b/src/backend/odoo/userAccount.ts index a9714c5..6621032 100644 --- a/src/backend/odoo/userAccount.ts +++ b/src/backend/odoo/userAccount.ts @@ -2,8 +2,12 @@ import { BridgeObject } from '..' import { t } from '../..' -export default class UserAccount extends BridgeObject { +export default abstract class UserAccount extends BridgeObject { + + abstract internalId: string + get isTopUpAllowed() { return this.jsonData?.is_topup_allowed !== false } + } diff --git a/src/exception.ts b/src/exception.ts index 90b47be..1f31fe3 100644 --- a/src/exception.ts +++ b/src/exception.ts @@ -97,3 +97,46 @@ export class CanceledOperation extends Error { this.name = 'CanceledOperation' } } + +// Payment exceptions + +export class PrepareTransferError extends Error { + constructor (message) { + super(message) + this.name = 'PrepareTransferError' + } +} + +export class PrepareTransferException extends PrepareTransferError { + + origException: Error + + constructor (message, origException?: Error) { + super(message) + this.name = 'PrepareTransferException' + this.origException = origException + } +} + +export class PrepareTransferAmountError extends PrepareTransferError { + constructor (message) { + super(message) + this.name = 'PrepareTransferAmountError' + } +} + +export class PrepareTransferInsufficientBalance extends PrepareTransferError { + constructor (message) { + super(message) + this.name = 'PrepareTransferInsufficientBalance' + } +} + +export class PrepareTransferUnsafeBalance extends PrepareTransferError { + realBal: number + constructor (message, realBal) { + super(message) + this.name = 'PrepareTransferUnsafeBalance' + this.realBal + } +} diff --git a/src/type.ts b/src/type.ts index 6f8dad7..38da65e 100644 --- a/src/type.ts +++ b/src/type.ts @@ -93,9 +93,10 @@ export interface IContact extends IBridge { export interface IRecipient extends IContact { + walletInternalId: string transfer(amount: number, senderMemo: string, - recipientMemo: string): Promise + recipientMemo: string): Promise } @@ -107,7 +108,7 @@ export interface IAccount extends IBridge { transfer(recipient: IRecipient, amount: number, senderMemo: string, - recipientMemo: string): Promise + recipientMemo: string): Promise } diff --git a/tests/environment.ts b/tests/environment.ts new file mode 100644 index 0000000..8e14371 --- /dev/null +++ b/tests/environment.ts @@ -0,0 +1,27 @@ +import type { Environment } from 'vitest' + +export default { + name: 'custom', + transformMode: 'ssr', + // optional - only if you support "experimental-vm" pool + async setupVM() { + const vm = await import('node:vm') + const context = vm.createContext() + return { + getVmContext() { + return context + }, + teardown() { + // called after all tests with this env have been run + } + } + }, + setup() { + // custom setup + return { + teardown() { + // called after all tests with this env have been run + } + } + } +} diff --git a/tests/setup.ts b/tests/setup.ts new file mode 100644 index 0000000..4d5f914 --- /dev/null +++ b/tests/setup.ts @@ -0,0 +1,11 @@ +import { Buffer } from 'buffer' +import { afterAll, beforeAll } from 'vitest'; + +beforeAll(() => { + // global.Buffer = Buffer +}); + +afterAll(() => { + // delete global.Buffer +}); + diff --git a/tsconfig.json b/tsconfig.json index 57b7767..14adfeb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,7 +24,13 @@ "outDir": "build", "paths": { "*": ["src/types/*"] - } + }, + "types": [ + "vitest/importMeta" + ], + "plugins": [ + { "transform": "./skip-prod-transpilation.ts" } + ] }, "typedocOptions": { "name": "LokAPI", diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..bfb496e --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,14 @@ +/// +import { defineConfig } from 'vite' + +const cfg = defineConfig({ + test: { + include: ['src/**/*.{js,ts}'], + setupFiles: ['./tests/setup.ts'], + environment: 'jsdom', + passWithNoTests: true, + bail: 1, + }, +}) + +export default cfg \ No newline at end of file