show collections
This commit is contained in:
@ -10,6 +10,9 @@
|
|||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
<div>
|
||||||
|
<collections-list aw-endpoint="https://aw.sa.vin/v1" aw-project="62f5ccd6af025a76edb0"></collections-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="/src/index.tsx" type="module"></script>
|
<script src="/src/index.tsx" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -4,7 +4,10 @@
|
|||||||
"description": "UI that generates itself based on the shape of a given Appwrite project.",
|
"description": "UI that generates itself based on the shape of a given Appwrite project.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"start": "vite",
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"serve": "vite preview"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -24,7 +27,8 @@
|
|||||||
"vite-plugin-solid": "^2.3.0"
|
"vite-plugin-solid": "^2.3.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-appwrite": "^7.0.2",
|
"appwrite": "^9.0.1",
|
||||||
|
"rxjs": "^7.5.6",
|
||||||
"solid-element": "^1.4.8",
|
"solid-element": "^1.4.8",
|
||||||
"solid-js": "^1.4.7"
|
"solid-js": "^1.4.7"
|
||||||
}
|
}
|
||||||
|
94
pnpm-lock.yaml
generated
94
pnpm-lock.yaml
generated
@ -1,7 +1,8 @@
|
|||||||
lockfileVersion: 5.4
|
lockfileVersion: 5.4
|
||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
node-appwrite: ^7.0.2
|
appwrite: ^9.0.1
|
||||||
|
rxjs: ^7.5.6
|
||||||
solid-element: ^1.4.8
|
solid-element: ^1.4.8
|
||||||
solid-js: ^1.4.7
|
solid-js: ^1.4.7
|
||||||
typescript: ^4.7.4
|
typescript: ^4.7.4
|
||||||
@ -9,7 +10,8 @@ specifiers:
|
|||||||
vite-plugin-solid: ^2.3.0
|
vite-plugin-solid: ^2.3.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
node-appwrite: 7.0.2
|
appwrite: 9.0.1
|
||||||
|
rxjs: 7.5.6
|
||||||
solid-element: 1.4.8_solid-js@1.4.8
|
solid-element: 1.4.8_solid-js@1.4.8
|
||||||
solid-js: 1.4.8
|
solid-js: 1.4.8
|
||||||
|
|
||||||
@ -387,17 +389,17 @@ packages:
|
|||||||
color-convert: 1.9.3
|
color-convert: 1.9.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/asynckit/0.4.0:
|
/appwrite/9.0.1:
|
||||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
resolution: {integrity: sha512-yLxb5H2fqlK0l4q6eEzrb5HGs3xA2894wcLIseOJ2v/iqUmjuIjXfLUpWG+DC94CQmEdZCxwvIFUVO/AG/t+cw==}
|
||||||
|
dependencies:
|
||||||
|
cross-fetch: 3.1.5
|
||||||
|
isomorphic-form-data: 2.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/axios/0.27.2:
|
/asynckit/0.4.0:
|
||||||
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
|
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||||
dependencies:
|
|
||||||
follow-redirects: 1.15.1
|
|
||||||
form-data: 4.0.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- debug
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/babel-plugin-jsx-dom-expressions/0.33.14_@babel+core@7.18.10:
|
/babel-plugin-jsx-dom-expressions/0.33.14_@babel+core@7.18.10:
|
||||||
@ -472,6 +474,14 @@ packages:
|
|||||||
safe-buffer: 5.1.2
|
safe-buffer: 5.1.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/cross-fetch/3.1.5:
|
||||||
|
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
|
||||||
|
dependencies:
|
||||||
|
node-fetch: 2.6.7
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
dev: false
|
||||||
|
|
||||||
/debug/4.3.4:
|
/debug/4.3.4:
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
@ -712,19 +722,9 @@ packages:
|
|||||||
engines: {node: '>=0.8.0'}
|
engines: {node: '>=0.8.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/follow-redirects/1.15.1:
|
/form-data/2.5.1:
|
||||||
resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
|
resolution: {integrity: sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==}
|
||||||
engines: {node: '>=4.0'}
|
engines: {node: '>= 0.12'}
|
||||||
peerDependencies:
|
|
||||||
debug: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
debug:
|
|
||||||
optional: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/form-data/4.0.0:
|
|
||||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
|
||||||
engines: {node: '>= 6'}
|
|
||||||
dependencies:
|
dependencies:
|
||||||
asynckit: 0.4.0
|
asynckit: 0.4.0
|
||||||
combined-stream: 1.0.8
|
combined-stream: 1.0.8
|
||||||
@ -780,6 +780,12 @@ packages:
|
|||||||
engines: {node: '>=12.13'}
|
engines: {node: '>=12.13'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/isomorphic-form-data/2.0.0:
|
||||||
|
resolution: {integrity: sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==}
|
||||||
|
dependencies:
|
||||||
|
form-data: 2.5.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/js-tokens/4.0.0:
|
/js-tokens/4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -826,13 +832,16 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/node-appwrite/7.0.2:
|
/node-fetch/2.6.7:
|
||||||
resolution: {integrity: sha512-qFahgNKk0qfzoL8/a2v1p/9+EsEuKNutxZqZSlweYi5q4jK3jJDcQZyoPI+B76zmLbYpk7q0zO5LrbfVVkI/kg==}
|
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
|
||||||
|
engines: {node: 4.x || >=6.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
encoding: ^0.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
encoding:
|
||||||
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
axios: 0.27.2
|
whatwg-url: 5.0.0
|
||||||
form-data: 4.0.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- debug
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/node-releases/2.0.6:
|
/node-releases/2.0.6:
|
||||||
@ -873,6 +882,12 @@ packages:
|
|||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/rxjs/7.5.6:
|
||||||
|
resolution: {integrity: sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==}
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/safe-buffer/5.1.2:
|
/safe-buffer/5.1.2:
|
||||||
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
|
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -927,10 +942,18 @@ packages:
|
|||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tr46/0.0.3:
|
||||||
|
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/ts-toolbelt/9.6.0:
|
/ts-toolbelt/9.6.0:
|
||||||
resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==}
|
resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tslib/2.4.0:
|
||||||
|
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/typescript/4.7.4:
|
/typescript/4.7.4:
|
||||||
resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==}
|
resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==}
|
||||||
engines: {node: '>=4.2.0'}
|
engines: {node: '>=4.2.0'}
|
||||||
@ -991,3 +1014,14 @@ packages:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/webidl-conversions/3.0.1:
|
||||||
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/whatwg-url/5.0.0:
|
||||||
|
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||||
|
dependencies:
|
||||||
|
tr46: 0.0.3
|
||||||
|
webidl-conversions: 3.0.1
|
||||||
|
dev: false
|
||||||
|
1
src/components.tsx
Normal file
1
src/components.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
import './components/collections';
|
51
src/components/collections.tsx
Normal file
51
src/components/collections.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import {createSignal, onMount} from "solid-js";
|
||||||
|
import { customElement } from "solid-element";
|
||||||
|
import {awService} from "../services/aw-service";
|
||||||
|
import {Attribute, Collection, CollectionsList} from "./types";
|
||||||
|
|
||||||
|
const style = ``;
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
awEndpoint: "localhost:80/v1",
|
||||||
|
awProject: "",
|
||||||
|
styles: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
customElement("collections-list", defaultProps, (props) => {
|
||||||
|
const [collections, setCollections] = createSignal<CollectionsList>();
|
||||||
|
awService.init(props.awEndpoint, props.awProject);
|
||||||
|
|
||||||
|
let customStyles;
|
||||||
|
try {
|
||||||
|
customStyles = props.styles;
|
||||||
|
} catch(e) {
|
||||||
|
customStyles = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
awService.getCollections().subscribe((collection: CollectionsList) => {
|
||||||
|
setCollections(collection);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={'collections-frame'}>
|
||||||
|
<style>{style}</style>
|
||||||
|
<style>{customStyles}</style>
|
||||||
|
{/*<span>{JSON.stringify(collections(), null, " ")}</span>*/}
|
||||||
|
{collections()?.collections.map((col: Collection) => {
|
||||||
|
return <div>
|
||||||
|
<h3>{col.name}</h3>
|
||||||
|
<span>{col.$id}</span><br></br>
|
||||||
|
{
|
||||||
|
col.attributes.map((attr: Attribute) => {
|
||||||
|
return <div>
|
||||||
|
<span>{attr.key + ", " + attr.type}</span>
|
||||||
|
</div>;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
28
src/components/types.ts
Normal file
28
src/components/types.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export interface CollectionsList {
|
||||||
|
collections: Collection[]
|
||||||
|
total: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Collection {
|
||||||
|
$id: string
|
||||||
|
$createdAt: number
|
||||||
|
$updatedAt: number
|
||||||
|
$read: string[]
|
||||||
|
$write: string[]
|
||||||
|
databaseId: string
|
||||||
|
name: string
|
||||||
|
enabled: boolean
|
||||||
|
permission: string
|
||||||
|
attributes: Attribute[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Attribute {
|
||||||
|
key: string
|
||||||
|
type: string
|
||||||
|
status: string
|
||||||
|
required: boolean
|
||||||
|
array: boolean
|
||||||
|
size: number
|
||||||
|
default: any
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
|||||||
/* @refresh reload */
|
/* @refresh reload */
|
||||||
import { render } from 'solid-js/web';
|
// import { render } from 'solid-js/web';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App';
|
// import App from './App';
|
||||||
|
|
||||||
render(() => <App />, document.getElementById('root') as HTMLElement);
|
import './components';
|
||||||
|
|
||||||
|
// render(() => <App />, document.getElementById('root') as HTMLElement);
|
||||||
|
82
src/services/aw-service.ts
Normal file
82
src/services/aw-service.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import {ObserveCache} from "./observeCache";
|
||||||
|
import {Account, Client, Functions, Models} from 'appwrite';
|
||||||
|
import {CollectionsList} from "../components/types";
|
||||||
|
|
||||||
|
class AWService {
|
||||||
|
private awEndpoint: string = "";
|
||||||
|
private awProject: string = "";
|
||||||
|
private readonly awClient: Client;
|
||||||
|
private collectionsObserveCache: ObserveCache<CollectionsList> = new ObserveCache<CollectionsList>();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.awClient = new Client();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCollections(): Observable<CollectionsList> {
|
||||||
|
let collectionRequest: () => Promise<CollectionsList> = () => {
|
||||||
|
let account = new Account(this.awClient);
|
||||||
|
let functions = new Functions(this.awClient);
|
||||||
|
|
||||||
|
return account.get().then(() => {
|
||||||
|
return functions.createExecution("getCollections")
|
||||||
|
}).then((executionInfo: Models.Execution) => {
|
||||||
|
return waitForCompletion();
|
||||||
|
|
||||||
|
async function waitForCompletion(): Promise<Models.Execution> {
|
||||||
|
let complete = false;
|
||||||
|
let exec: Models.Execution = await getExec();
|
||||||
|
if (exec.status !== "processing") {
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
while (!complete) {
|
||||||
|
exec = await delay();
|
||||||
|
if (exec.status !== "processing") {
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function delay(): Promise<Models.Execution> {
|
||||||
|
return new Promise<Models.Execution>((resolve, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
getExec().then((exec: Models.Execution) => {
|
||||||
|
resolve(exec);
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExec(): Promise<Models.Execution> {
|
||||||
|
return functions.getExecution("getCollections", executionInfo.$id);
|
||||||
|
}
|
||||||
|
}).then((ex: Models.Execution) => {
|
||||||
|
return JSON.parse(ex.response) as CollectionsList;
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
return {collections: [], total: 0};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.collectionsObserveCache.get('collections', collectionRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(endpoint: string, project: string) {
|
||||||
|
if (this.awEndpoint === "") {
|
||||||
|
this.awEndpoint = endpoint;
|
||||||
|
this.awClient.setEndpoint(endpoint);
|
||||||
|
}
|
||||||
|
if (this.awProject === "") {
|
||||||
|
this.awProject = project;
|
||||||
|
this.awClient.setProject(project);
|
||||||
|
}
|
||||||
|
// otherwise we have already been initialized, and we don't need to do anything
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const awService = new AWService();
|
||||||
|
|
||||||
|
export {
|
||||||
|
awService
|
||||||
|
// AWService
|
||||||
|
}
|
100
src/services/fetch.ts
Normal file
100
src/services/fetch.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
declare var window: any;
|
||||||
|
const defaultBasePath = '';
|
||||||
|
|
||||||
|
export function fetchAsync(method: 'GET' | 'POST' | 'DELETE' | 'PUT', url: string, body?: any) {
|
||||||
|
const headers = { 'Content-Type': 'application/json; charset=utf-8' };
|
||||||
|
// if (access_token) headers['Authorization'] = `Token ${access_token}`;
|
||||||
|
return window.fetch(`${defaultBasePath}${url}`, {
|
||||||
|
method,
|
||||||
|
headers,
|
||||||
|
body: body && JSON.stringify(body)
|
||||||
|
}).then((response: any) => {
|
||||||
|
// if (response.status === 401) {
|
||||||
|
// window.location.href = '/auth/login';
|
||||||
|
// // throw new Error('401');
|
||||||
|
// }
|
||||||
|
// if (response.status === 403 && url === '/api/v1/organization/') {
|
||||||
|
// // this indicates a user that is not supposed to be in bx-console
|
||||||
|
// window.location.href = '/auth/logout';
|
||||||
|
// }
|
||||||
|
const result = response.json();
|
||||||
|
if (!response.ok) throw result;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get<T>(url: string): Promise<T> {
|
||||||
|
return fetchAsync('GET', url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function post<T>(url: string, body?: any): Promise<T> {
|
||||||
|
return fetchAsync('POST', url, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postWithFile<T>(url: string, body?: any): Promise<T> {
|
||||||
|
return fetchWithFile('POST', url, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function putWithFile<T>(url: string, body?: any): Promise<T> {
|
||||||
|
return fetchWithFile('PUT', url, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchWithFile<T>(method: string, url: string, body?: any): Promise<T> {
|
||||||
|
return window.fetch(`${defaultBasePath}${url}`, {
|
||||||
|
method: method,
|
||||||
|
body: body
|
||||||
|
}).then((response: any) => {
|
||||||
|
if (response.status === 401) {
|
||||||
|
window.location.href = '/auth/login';
|
||||||
|
// throw new Error('401');
|
||||||
|
}
|
||||||
|
const result = response.json();
|
||||||
|
if (!response.ok) throw result;
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function del(url: string) {
|
||||||
|
return fetchAsync('DELETE', url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function put(url: string, body?: any) {
|
||||||
|
return fetchAsync('PUT', url, body);
|
||||||
|
}
|
||||||
|
export function toQueryString(obj: any) {
|
||||||
|
const parts = [];
|
||||||
|
for (var i in obj) {
|
||||||
|
if (obj.hasOwnProperty(i)) {
|
||||||
|
parts.push(encodeURIComponent(i) + "=" + encodeURIComponent(obj[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parts.join("&");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializeObject<T>(form: any) {
|
||||||
|
let obj: {[key: string]: any} = {};
|
||||||
|
if (typeof form == 'object' && form.nodeName == "FORM") {
|
||||||
|
for (let i = 0; i < form.elements.length; i++) {
|
||||||
|
const field = form.elements[i];
|
||||||
|
if (field.name
|
||||||
|
&& field.type != 'file'
|
||||||
|
&& field.type != 'reset'
|
||||||
|
&& field.type != 'submit'
|
||||||
|
&& field.type != 'button') {
|
||||||
|
if (field.type == 'select-multiple') {
|
||||||
|
obj[field.name] = '';
|
||||||
|
let tempvalue = '';
|
||||||
|
for (let j = 0; j < form.elements[i].options.length; j++) {
|
||||||
|
if (field.options[j].selected)
|
||||||
|
tempvalue += field.options[j].value + ';';
|
||||||
|
}
|
||||||
|
if (tempvalue.charAt(tempvalue.length - 1) === ';') obj[field.name] = tempvalue.substring(0, tempvalue.length - 1);
|
||||||
|
|
||||||
|
} else if ((field.type != 'checkbox' && field.type != 'radio') || field.checked) {
|
||||||
|
obj[field.name] = field.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj as T;
|
||||||
|
}
|
43
src/services/observeCache.ts
Normal file
43
src/services/observeCache.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { Observable, from } from 'rxjs';
|
||||||
|
import { get } from './fetch';
|
||||||
|
import { ObserveService } from './observeService';
|
||||||
|
|
||||||
|
interface storedItem<T> {
|
||||||
|
observeService: ObserveService<T>
|
||||||
|
bust: boolean
|
||||||
|
updated: number
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObserveCache<T> {
|
||||||
|
private cacheStore: { [key: string]: storedItem<T> } = {};
|
||||||
|
private expiration: number = 5 * 60 * 1000; // 5 minutes
|
||||||
|
|
||||||
|
public get(key: string, endpoint: string|(()=>Promise<T>)) {
|
||||||
|
let now = new Date().getTime();
|
||||||
|
if (!this.cacheStore[key]) {
|
||||||
|
function getRequest() {
|
||||||
|
if (typeof endpoint === 'string') {
|
||||||
|
return from<Promise<T>>(get<T>(endpoint));
|
||||||
|
}
|
||||||
|
return from<Promise<T>>(endpoint());
|
||||||
|
}
|
||||||
|
this.cacheStore[key] = {
|
||||||
|
observeService: new ObserveService<T>(getRequest),
|
||||||
|
bust: false,
|
||||||
|
updated: now,
|
||||||
|
};
|
||||||
|
} else if (this.cacheStore[key].bust || now - this.cacheStore[key].updated > this.expiration) {
|
||||||
|
this.cacheStore[key].observeService.next();
|
||||||
|
this.cacheStore[key].updated = now;
|
||||||
|
}
|
||||||
|
return this.cacheStore[key].observeService.value$;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bust(key: string) {
|
||||||
|
this.cacheStore[key].bust = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
ObserveCache
|
||||||
|
}
|
19
src/services/observeService.ts
Normal file
19
src/services/observeService.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import {merge, Observable, Subject} from 'rxjs';
|
||||||
|
import {debounceTime, shareReplay, switchMap} from 'rxjs/operators';
|
||||||
|
|
||||||
|
export class ObserveService<T> {
|
||||||
|
private updateStream: Subject<void>;
|
||||||
|
public value$: Observable<T>;
|
||||||
|
// TODO: add timestamp for last updated so the cache can be smart about when to check for new values
|
||||||
|
|
||||||
|
constructor(request: ()=>Observable<T>) {
|
||||||
|
this.updateStream = new Subject<void>();
|
||||||
|
const initialCurrentResponse$ = request();
|
||||||
|
const reload$ = this.updateStream.pipe(debounceTime(1000), switchMap(() => request()));
|
||||||
|
this.value$ = merge(initialCurrentResponse$, reload$).pipe(shareReplay(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public next() {
|
||||||
|
this.updateStream.next();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user