mirror of
https://github.com/Expand-sys/ccash-client-js
synced 2026-03-22 12:27:09 +11:00
feat: add CCashClient methods
This commit is contained in:
parent
145c9507b4
commit
9e3a08f307
18 changed files with 328 additions and 15 deletions
|
|
@ -15,9 +15,11 @@ npm install ccash-client-js
|
|||
```js
|
||||
import { CCashClient } from 'ccash-client-js';
|
||||
|
||||
process.env.CCASH_API_BASE_URL = 'https://your.ccash.api';
|
||||
|
||||
const client = new CCashClient();
|
||||
|
||||
client.balance('twix');
|
||||
console.log(await client.balance('blinkblinko'));
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
|
|
|||
1
examples/node/.env
Normal file
1
examples/node/.env
Normal file
|
|
@ -0,0 +1 @@
|
|||
CCASH_API_BASE_URL=https://wtfisthis.tech/BankF
|
||||
|
|
@ -1,10 +1,20 @@
|
|||
require('dotenv').config();
|
||||
const { CCashClient } = require('ccash-client-js');
|
||||
|
||||
const client = new CCashClient()
|
||||
const user = 'blinkblinko';
|
||||
const pass = 'TestPassword';
|
||||
|
||||
const client = new CCashClient();
|
||||
|
||||
async function main() {
|
||||
const balance = await client.balance('twix');
|
||||
console.log(`Balance: ${balance}`)
|
||||
const createdUser = await client.addUser(user, pass);
|
||||
console.log('User created', createdUser);
|
||||
|
||||
const balance = await client.balance(user);
|
||||
console.log(`Balance: ${balance}`);
|
||||
|
||||
const deletedUser = await client.deleteUser(user, pass);
|
||||
console.log('User deleated', deletedUser);
|
||||
}
|
||||
|
||||
main()
|
||||
main();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"start": "node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"ccash-client-js": "file:../.."
|
||||
"ccash-client-js": "file:../..",
|
||||
"dotenv": "^10.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ axios@^0.21.1:
|
|||
dependencies:
|
||||
axios "^0.21.1"
|
||||
|
||||
dotenv@^10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
|
||||
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
|
||||
|
||||
follow-redirects@^1.10.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
|
||||
|
|
|
|||
2
examples/web/.env
Normal file
2
examples/web/.env
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
SKIP_PREFLIGHT_CHECK=true
|
||||
CCASH_API_BASE_URL=https://wtfisthis.tech/BankF
|
||||
|
|
@ -2,14 +2,16 @@ import React, { useEffect, useState } from 'react';
|
|||
import { CCashClient } from 'ccash-client-js';
|
||||
import './App.css';
|
||||
|
||||
const client = new CCashClient();
|
||||
const client = new CCashClient(
|
||||
process.env.CCASH_API_BASE_URL || 'https://wtfisthis.tech/BankF'
|
||||
);
|
||||
|
||||
function App() {
|
||||
const [balance, setBalance] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
(async function getBalance() {
|
||||
setBalance(await client.balance('twix'));
|
||||
setBalance(await client.balance('blinkblinko'));
|
||||
})();
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
|
|
@ -3163,6 +3163,7 @@ case-sensitive-paths-webpack-plugin@2.3.0:
|
|||
version "0.0.0"
|
||||
dependencies:
|
||||
axios "^0.21.1"
|
||||
class-transformer "^0.4.0"
|
||||
|
||||
chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
|
|
@ -3266,6 +3267,11 @@ cjs-module-lexer@^0.6.0:
|
|||
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f"
|
||||
integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==
|
||||
|
||||
class-transformer@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.4.0.tgz#b52144117b423c516afb44cc1c76dbad31c2165b"
|
||||
integrity sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==
|
||||
|
||||
class-utils@^0.3.5:
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1"
|
||||
"axios": "^0.21.1",
|
||||
"class-transformer": "^0.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.5",
|
||||
|
|
|
|||
69
src/CCashClient.exceptions.ts
Normal file
69
src/CCashClient.exceptions.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { Exception } from './Exception';
|
||||
|
||||
export class BaseUrlMissingException extends Exception {
|
||||
constructor() {
|
||||
super('base url missing');
|
||||
}
|
||||
}
|
||||
|
||||
export class UserNotFoundException extends Exception {
|
||||
constructor() {
|
||||
super('user not found');
|
||||
}
|
||||
}
|
||||
|
||||
export class WrongPasswordException extends Exception {
|
||||
constructor() {
|
||||
super('wrong password');
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidRequestException extends Exception {
|
||||
constructor() {
|
||||
super('invalid request');
|
||||
}
|
||||
}
|
||||
|
||||
export class WrongAdminPasswordException extends Exception {
|
||||
constructor() {
|
||||
super('wrong admin password');
|
||||
}
|
||||
}
|
||||
|
||||
export class NameTooLongException extends Exception {
|
||||
constructor() {
|
||||
super('name too long');
|
||||
}
|
||||
}
|
||||
|
||||
export class UserAlreadyExistsException extends Exception {
|
||||
constructor() {
|
||||
super('user already exists');
|
||||
}
|
||||
}
|
||||
|
||||
export class InsufficientFundsException extends Exception {
|
||||
constructor() {
|
||||
super('insufficient funds');
|
||||
}
|
||||
}
|
||||
|
||||
export enum ErrorCodes {
|
||||
UserNotFound = -1,
|
||||
WrongPassword = -2,
|
||||
InvalidRequest = -3,
|
||||
WrongAdminPassword = -4,
|
||||
NameTooLong = -5,
|
||||
UserAlreadyExists = -6,
|
||||
InsufficientFunds = -7,
|
||||
}
|
||||
|
||||
export const ExceptionMap = {
|
||||
[ErrorCodes.UserNotFound]: UserNotFoundException,
|
||||
[ErrorCodes.WrongPassword]: WrongPasswordException,
|
||||
[ErrorCodes.InvalidRequest]: InvalidRequestException,
|
||||
[ErrorCodes.WrongAdminPassword]: WrongAdminPasswordException,
|
||||
[ErrorCodes.NameTooLong]: NameTooLongException,
|
||||
[ErrorCodes.UserAlreadyExists]: UserAlreadyExistsException,
|
||||
[ErrorCodes.InsufficientFunds]: InsufficientFundsException,
|
||||
};
|
||||
|
|
@ -1,5 +1,166 @@
|
|||
export class CCashClient {
|
||||
import axios, { AxiosInstance, AxiosResponse } from 'axios';
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import { ICCashClient, User } from './CCashClient.types';
|
||||
import {
|
||||
BaseUrlMissingException,
|
||||
ExceptionMap,
|
||||
ErrorCodes,
|
||||
} from './CCashClient.exceptions';
|
||||
|
||||
export class CCashClient implements Partial<ICCashClient> {
|
||||
/** TODO: not partial **/
|
||||
http: AxiosInstance;
|
||||
|
||||
constructor(baseURL: string | undefined = process.env.CCASH_API_BASE_URL) {
|
||||
if (!baseURL) {
|
||||
throw new BaseUrlMissingException();
|
||||
}
|
||||
|
||||
this.http = axios.create({
|
||||
baseURL,
|
||||
});
|
||||
}
|
||||
|
||||
balance(user: string): Promise<number> {
|
||||
return Promise.resolve(10);
|
||||
return this.http
|
||||
.get(`/${user}/bal`)
|
||||
.then((response) => this.handleError(response) || response.data.value);
|
||||
}
|
||||
|
||||
log(
|
||||
user: string,
|
||||
pass: string,
|
||||
transactionCount: number = 10
|
||||
): Promise<number[]> {
|
||||
return this.http
|
||||
.get(`/${user}/bal`, {
|
||||
headers: { Password: pass },
|
||||
params: { n: transactionCount },
|
||||
})
|
||||
.then((response) => this.handleError(response) || response.data.value);
|
||||
}
|
||||
|
||||
sendFunds(
|
||||
user: string,
|
||||
pass: string,
|
||||
to: string,
|
||||
amount: number
|
||||
): Promise<number> {
|
||||
return this.http
|
||||
.post(`/${user}/send/${to}`, {
|
||||
headers: { Password: pass },
|
||||
params: { amount },
|
||||
})
|
||||
.then((response) => this.handleError(response) || amount);
|
||||
}
|
||||
|
||||
verifyPassword(user: string, pass: string): Promise<boolean> {
|
||||
return this.http
|
||||
.get(`/${user}/pass/verify`, { headers: { Password: pass } })
|
||||
.then((response) => this.handleError(response) || response.data.value);
|
||||
}
|
||||
|
||||
changePassword(user: string, pass: string, newPass: string): Promise<User> {
|
||||
return this.http
|
||||
.patch(
|
||||
`/${user}/pass/change`,
|
||||
{ password: newPass },
|
||||
{ headers: { Password: pass } }
|
||||
)
|
||||
.then(
|
||||
(response) =>
|
||||
this.handleError(response) || this.serialize(User, { user })
|
||||
);
|
||||
}
|
||||
|
||||
setBal(user: string, pass: string, amount: number): Promise<number> {
|
||||
return this.http
|
||||
.patch(`/admin/${user}/bal`, undefined, {
|
||||
headers: { Password: pass },
|
||||
params: { amount },
|
||||
})
|
||||
.then((response) => this.handleError(response) || amount);
|
||||
}
|
||||
|
||||
help(): Promise<string> {
|
||||
return this.http.get('/help').then((response) => response.data);
|
||||
}
|
||||
|
||||
close(pass: string): Promise<boolean> {
|
||||
return this.http
|
||||
.post('/close', undefined, { headers: { Password: pass } })
|
||||
.then((response) => this.handleError(response) || true);
|
||||
}
|
||||
|
||||
contains(user: string): Promise<boolean> {
|
||||
return this.http
|
||||
.get(`/contains/${user}`)
|
||||
.then(
|
||||
(response) => this.handleError(response) || response.data.value || false
|
||||
);
|
||||
}
|
||||
|
||||
adminVerifyPass(pass: string): Promise<boolean> {
|
||||
return this.http
|
||||
.get('/admin/verify')
|
||||
.then(
|
||||
(response) => this.handleError(response) || response.data.value || false
|
||||
);
|
||||
}
|
||||
|
||||
addUser(user: string, pass: string): Promise<User> {
|
||||
return this.http
|
||||
.post(`/user/${user}`, undefined, { headers: { Password: pass } })
|
||||
.then(
|
||||
(response) =>
|
||||
this.handleError(response) || this.serialize(User, { user })
|
||||
);
|
||||
}
|
||||
|
||||
adminAddUser(
|
||||
user: string,
|
||||
pass: string,
|
||||
initialBalance: number
|
||||
): Promise<User> {
|
||||
return this.http
|
||||
.post(`/user/${user}`, undefined, {
|
||||
headers: { Password: pass },
|
||||
params: { init_bal: initialBalance },
|
||||
})
|
||||
.then(
|
||||
(response) =>
|
||||
this.handleError(response) || this.serialize(User, { user })
|
||||
);
|
||||
}
|
||||
|
||||
deleteUser(user: string, pass: string): Promise<User> {
|
||||
return this.http
|
||||
.delete(`/user/${user}`, { headers: { Password: pass } })
|
||||
.then(
|
||||
(response) =>
|
||||
this.handleError(response) || this.serialize(User, { user })
|
||||
);
|
||||
}
|
||||
|
||||
adminDeleteUser(user: string, pass: string): Promise<User> {
|
||||
return this.http
|
||||
.delete(`/user/${user}`, { headers: { Password: pass } })
|
||||
.then(
|
||||
(response) =>
|
||||
this.handleError(response) || this.serialize(User, { user })
|
||||
);
|
||||
}
|
||||
|
||||
private handleError(response: AxiosResponse<any>): false {
|
||||
if (
|
||||
response.data.value &&
|
||||
Object.values(ErrorCodes).includes(response.data.value)
|
||||
) {
|
||||
throw new ExceptionMap[response.data.value as ErrorCodes]();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private serialize = plainToClass;
|
||||
}
|
||||
|
|
|
|||
36
src/CCashClient.types.ts
Normal file
36
src/CCashClient.types.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { User } from './User';
|
||||
|
||||
export { User };
|
||||
|
||||
export interface ICCashClient {
|
||||
// Usage
|
||||
balance(user: string): Promise<number>;
|
||||
log(user: string, pass: string, transactionCount?: number): Promise<number[]>;
|
||||
sendFunds(
|
||||
user: string,
|
||||
pass: string,
|
||||
to: string,
|
||||
amount: number
|
||||
): Promise<number>;
|
||||
verifyPassword(user: string, pass: string): Promise<boolean>;
|
||||
|
||||
// Meta usage
|
||||
changePassword(user: string, pass: string, newPass: string): Promise<User>;
|
||||
setBal(user: string, pass: string, amount: number): Promise<number>;
|
||||
|
||||
// System usage
|
||||
help(): Promise<string>;
|
||||
close(pass: string): Promise<boolean>;
|
||||
contains(user: string): Promise<boolean>;
|
||||
adminVerifyPass(pass: string): Promise<boolean>;
|
||||
|
||||
// User management
|
||||
addUser(user: string, pass: string): Promise<User>;
|
||||
adminAddUser(
|
||||
user: string,
|
||||
pass: string,
|
||||
initialBalance: number
|
||||
): Promise<User>;
|
||||
deleteUser(user: string, pass: string): Promise<User>;
|
||||
adminDeleteUser(user: string, pass: string): Promise<User>;
|
||||
}
|
||||
1
src/Exception.ts
Normal file
1
src/Exception.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export class Exception extends Error {}
|
||||
7
src/User.ts
Normal file
7
src/User.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { Exclude, Expose } from 'class-transformer';
|
||||
|
||||
@Exclude()
|
||||
export class User {
|
||||
@Expose()
|
||||
user!: string;
|
||||
}
|
||||
|
|
@ -1 +1,3 @@
|
|||
export { CCashClient } from './CCashClient';
|
||||
export * from './CCashClient';
|
||||
export * from './CCashClient.types';
|
||||
export * from './CCashClient.exceptions';
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ describe('CCashClient', () => {
|
|||
|
||||
describe('balance', () => {
|
||||
it('returns an integer', async () => {
|
||||
expect(await client.balance('twix')).toEqual(10);
|
||||
expect(await client.balance('blinkblinko')).toEqual(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@
|
|||
"outDir": "./dist/cjs",
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"paths": {
|
||||
|
|
|
|||
|
|
@ -1546,6 +1546,11 @@ cjs-module-lexer@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.1.tgz#2fd46d9906a126965aa541345c499aaa18e8cd73"
|
||||
integrity sha512-jVamGdJPDeuQilKhvVn1h3knuMOZzr8QDnpk+M9aMlCaMkTDd6fBWPhiDqFvFZ07pL0liqabAiuy8SY4jGHeaw==
|
||||
|
||||
class-transformer@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.4.0.tgz#b52144117b423c516afb44cc1c76dbad31c2165b"
|
||||
integrity sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==
|
||||
|
||||
cliui@^7.0.2:
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
|
||||
|
|
|
|||
Loading…
Reference in a new issue