@outlawdesigns/loe-rest-client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +198 -0
- package/index.js +3 -0
- package/package.json +16 -0
- package/src/core.js +54 -0
- package/src/models/anime.js +65 -0
- package/src/models/doc.js +65 -0
- package/src/models/episode.js +65 -0
- package/src/models/holdingBay.js +22 -0
- package/src/models/movie.js +65 -0
- package/src/models/song.js +65 -0
- package/src/singleton.js +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# LOE REST Client (JavaScript)
|
|
2
|
+
|
|
3
|
+
A lightweight JavaScript/Node.js client for interacting with the **Library of Everything (LOE)** REST API using OAuth2 authentication.
|
|
4
|
+
This package provides modular access to LOE models—anime, docs, episodes, movies, songs, and holding-bay items—along with robust token handling and a shared singleton interface.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 🚀 Features
|
|
9
|
+
|
|
10
|
+
* 🔐 **OAuth2 client credentials authentication**
|
|
11
|
+
* 🔄 **Automatic token refresh**
|
|
12
|
+
* 📦 Organized model access:
|
|
13
|
+
|
|
14
|
+
* `anime`
|
|
15
|
+
* `doc`
|
|
16
|
+
* `episode`
|
|
17
|
+
* `movie`
|
|
18
|
+
* `song`
|
|
19
|
+
* `holdingbay` (special pre-ingest items)
|
|
20
|
+
* 🧩 **Singleton mode** for shared instances
|
|
21
|
+
* ⚙️ Clean, consistent `axios`-based API
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 📦 Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @outlawdesigns/loe-rest-client
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 🧠 Basic Usage
|
|
34
|
+
|
|
35
|
+
### Initialize the API Client
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
import loeClient from '@outlawdesigns/loe-rest-client';
|
|
39
|
+
|
|
40
|
+
// Create the singleton instance
|
|
41
|
+
loeClient.init('https://loe-service.outlawdesigns.io', 'openid profile email');
|
|
42
|
+
|
|
43
|
+
// Initialize OAuth provider
|
|
44
|
+
await loeClient.get().auth.init(
|
|
45
|
+
'https://auth.outlawdesigns.io/oauth2/token',
|
|
46
|
+
'your-client-id',
|
|
47
|
+
'your-client-secret'
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Perform OAuth2 client credentials flow
|
|
51
|
+
await loeClient.get().auth.clientCredentialFlow(
|
|
52
|
+
'openid profile email',
|
|
53
|
+
[ 'https://loe-service.outlawdesigns.io' ]
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Example call: fetch 3 most recent songs
|
|
57
|
+
const recent = await loeClient.get().songs.getRecent(3);
|
|
58
|
+
console.log(recent);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Token Verification Example
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
const token = loeClient.get().auth.getAccessToken();
|
|
65
|
+
|
|
66
|
+
const tokenResp = await loeClient.get().auth.verifyAccessToken(
|
|
67
|
+
token,
|
|
68
|
+
['https://loe-service.outlawdesigns.io']
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
console.log(tokenResp);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Accessing the Singleton Anywhere
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
import loeClient from '@outlawdesigns/loe-rest-client';
|
|
78
|
+
|
|
79
|
+
const song = await loeClient.get().song.get(42);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 🔐 Authentication Flow
|
|
85
|
+
|
|
86
|
+
The LOE REST Client uses **`@outlawdesigns/authenticationclient`** to handle:
|
|
87
|
+
|
|
88
|
+
* Token acquisition
|
|
89
|
+
* Token refresh
|
|
90
|
+
* Attaching `Authorization: Bearer <token>` headers
|
|
91
|
+
* Token verification
|
|
92
|
+
|
|
93
|
+
### Typical Flow
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
// 1. init() creates the API + axios wrapper
|
|
97
|
+
loeClient.init(apiUrl, oauthScope);
|
|
98
|
+
|
|
99
|
+
// 2. Provide OAuth endpoint + credentials
|
|
100
|
+
await loeClient.get().auth.init(authUrl, clientId, clientSecret);
|
|
101
|
+
|
|
102
|
+
// 3. Client credentials flow to obtain tokens
|
|
103
|
+
await loeClient.get().auth.clientCredentialFlow(oauthScope, [apiUrl]);
|
|
104
|
+
|
|
105
|
+
// 4. Make API calls
|
|
106
|
+
const movies = await loeClient.get().movie.getAll();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 🧩 Available Model Modules
|
|
112
|
+
|
|
113
|
+
### 🎵 `api.song`
|
|
114
|
+
|
|
115
|
+
Supports LOE’s rich music feature set.
|
|
116
|
+
|
|
117
|
+
| Method | Description |
|
|
118
|
+
| --------------------------------- | -------------------------------- |
|
|
119
|
+
| `getAll()` | Get all songs |
|
|
120
|
+
| `get(id)` | Fetch a song |
|
|
121
|
+
| `create(songObj)` | Create a song |
|
|
122
|
+
| `search(field, query)` | Search songs |
|
|
123
|
+
| `browse(field)` | Browse unique values for a field |
|
|
124
|
+
| `getRecent(limit)` | Fetch `n` most recent songs |
|
|
125
|
+
| `getMyPlaylists()` | Retrieve user playlists |
|
|
126
|
+
| `getPlaylist(id)` | Get a specific playlist |
|
|
127
|
+
| `savePlaylist(obj)` | Save playlist |
|
|
128
|
+
| `rate(id, rating)` | Rate a song |
|
|
129
|
+
| `getRating(id)` | Fetch a rating |
|
|
130
|
+
| `count()` | Count total songs |
|
|
131
|
+
| `group(field)` | Group by field |
|
|
132
|
+
| `getRandomPlaylist(genre, limit)` | Randomized playlist |
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
### 🎬 `api.movie`
|
|
137
|
+
|
|
138
|
+
Standard CRUD + metadata operations.
|
|
139
|
+
|
|
140
|
+
### 📺 `api.episode`
|
|
141
|
+
|
|
142
|
+
Episode-level metadata for TV/anime content.
|
|
143
|
+
|
|
144
|
+
### 🎭 `api.anime`
|
|
145
|
+
|
|
146
|
+
Anime metadata (similar to episode model).
|
|
147
|
+
|
|
148
|
+
### 📄 `api.doc`
|
|
149
|
+
|
|
150
|
+
Document/Ebook metadata.
|
|
151
|
+
|
|
152
|
+
### 📥 `api.holdingbay`
|
|
153
|
+
|
|
154
|
+
Read-only staging area for media not yet classified:
|
|
155
|
+
|
|
156
|
+
| Method | Description |
|
|
157
|
+
| ------------- | ----------------------------------- |
|
|
158
|
+
| `getMovies()` | Items possibly classified as movies |
|
|
159
|
+
| `getSongs()` | Music-type items |
|
|
160
|
+
| `getTv()` | TV/episode candidates |
|
|
161
|
+
| `getComics()` | Comic content |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 🧱 Project Structure
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
src/
|
|
169
|
+
├── core.js
|
|
170
|
+
├── singleton.js
|
|
171
|
+
├── models/
|
|
172
|
+
│ ├── anime.js
|
|
173
|
+
│ ├── doc.js
|
|
174
|
+
│ ├── episode.js
|
|
175
|
+
│ ├── movie.js
|
|
176
|
+
│ ├── song.js
|
|
177
|
+
│ └── holdingbay.js
|
|
178
|
+
└── formData.js
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## ⚙️ Creating a Direct Client (Non-Singleton)
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
import { createApiClient } from '@outlawdesigns/loe-rest-client/core.js';
|
|
187
|
+
|
|
188
|
+
const api = createApiClient(apiUrl, oauthScope);
|
|
189
|
+
await api.auth.init(authUrl, clientId, clientSecret);
|
|
190
|
+
await api.auth.clientCredentialFlow(oauthScope, [apiUrl]);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 👤 Author
|
|
196
|
+
|
|
197
|
+
Maintained by **Outlaw Designs**
|
|
198
|
+
[https://github.com/outlawdesigns-io](https://github.com/outlawdesigns-io)
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@outlawdesigns/loe-rest-client",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"author": "",
|
|
10
|
+
"license": "ISC",
|
|
11
|
+
"description": "",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@outlawdesigns/authenticationclient": "^2.1.1",
|
|
14
|
+
"axios": "^1.13.2"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/core.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import authClient from '@outlawdesigns/authenticationclient';
|
|
3
|
+
|
|
4
|
+
import createAnime from './models/anime.js';
|
|
5
|
+
import createDocs from './models/doc.js';
|
|
6
|
+
import createEpisodes from './models/episode.js';
|
|
7
|
+
import createHoldingbay from './models/holdingBay.js';
|
|
8
|
+
import createMovies from './models/movie.js';
|
|
9
|
+
import createSongs from './models/song.js';
|
|
10
|
+
|
|
11
|
+
export function createApiClient(baseURL, requestedScope){
|
|
12
|
+
const oauthScope = requestedScope; //the scope(s) for this app
|
|
13
|
+
const oauthResource = baseURL;
|
|
14
|
+
const oauthRefreshBuffer = 300;
|
|
15
|
+
const axiosInstance = axios.create({baseURL:baseURL});
|
|
16
|
+
authClient.onTokenUpdate((token)=>{
|
|
17
|
+
axiosInstance.defaults.headers.common['Authorization'] = `Bearer ${token}`;
|
|
18
|
+
});
|
|
19
|
+
axiosInstance.interceptors.request.use(async (config)=>{
|
|
20
|
+
const token = authClient.getAccessToken();
|
|
21
|
+
if(!token) throw new Error(`Authenticate before making API calls.`);
|
|
22
|
+
const refreshToken = authClient.getRefreshToken();
|
|
23
|
+
let user;
|
|
24
|
+
if(refreshToken){
|
|
25
|
+
try{
|
|
26
|
+
user = await authClient.verifyAccessToken(token,[oauthResource]);
|
|
27
|
+
}catch(err){
|
|
28
|
+
console.log(err);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const now = Math.floor(Date.now() / 1000);
|
|
32
|
+
const timeDiffSeconds = user.exp - now;
|
|
33
|
+
if(timeDiffSeconds <= oauthRefreshBuffer){
|
|
34
|
+
try{
|
|
35
|
+
await authClient.refreshToken(oauthScope,[oauthResource]);
|
|
36
|
+
}catch(err){
|
|
37
|
+
console.log(err);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
config.headers['Authorization'] = `Bearer ${authClient.getAccessToken()}`;
|
|
43
|
+
return config;
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
auth:authClient,
|
|
47
|
+
movies:createMovies(axiosInstance),
|
|
48
|
+
songs:createSongs(axiosInstance),
|
|
49
|
+
holdingBay:createHoldingbay(axiosInstance),
|
|
50
|
+
episodes:createEpisodes(axiosInstance),
|
|
51
|
+
docs:createDocs(axiosInstance),
|
|
52
|
+
anime:createAnime(axiosInstance)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const resource = '/anime';
|
|
2
|
+
|
|
3
|
+
export default function createAnime(axios){
|
|
4
|
+
return {
|
|
5
|
+
async getAll(){
|
|
6
|
+
const res = await axios.get(`${resource}`);
|
|
7
|
+
return res.data;
|
|
8
|
+
},
|
|
9
|
+
async get(id){
|
|
10
|
+
if(!id){
|
|
11
|
+
throw new Error('Id argument reguired.');
|
|
12
|
+
}
|
|
13
|
+
const res = await axios.get(`${resource}/${id}`);
|
|
14
|
+
return res.data;
|
|
15
|
+
},
|
|
16
|
+
async create(animeObj){
|
|
17
|
+
const res = await axios.post(`${resource}`,animeObj);
|
|
18
|
+
return res.data;
|
|
19
|
+
},
|
|
20
|
+
async search(field, query){
|
|
21
|
+
const res = await axios.get(`${resource}/search/${field}/${query}`);
|
|
22
|
+
return res.data;
|
|
23
|
+
},
|
|
24
|
+
async browse(field){
|
|
25
|
+
const res = await axios.get(`${resource}/browse/${field}`);
|
|
26
|
+
return res.data;
|
|
27
|
+
},
|
|
28
|
+
async getRecent(limit){
|
|
29
|
+
const res = await axios.get(`${resource}/recent/${limit}`);
|
|
30
|
+
return res.data;
|
|
31
|
+
},
|
|
32
|
+
async getMyPlaylists(){
|
|
33
|
+
const res = await axios.get(`${resource}/list/`);
|
|
34
|
+
return res.data;
|
|
35
|
+
},
|
|
36
|
+
async getPlaylist(id){
|
|
37
|
+
const res = await axios.get(`${resource}/list/${id}`);
|
|
38
|
+
return res.data;
|
|
39
|
+
},
|
|
40
|
+
async savePlaylist(playlistObj){
|
|
41
|
+
const res = await axios.post(`${resource}/list`,playlistObj);
|
|
42
|
+
return res.data;
|
|
43
|
+
},
|
|
44
|
+
async rate(animeId, rating){
|
|
45
|
+
const res = await axios.post(`${resource}/rate/${animeId}`,{rating:rating});
|
|
46
|
+
return res.data;
|
|
47
|
+
},
|
|
48
|
+
async getRating(id){
|
|
49
|
+
const res = await axios.get(`${resource}/rate/${id}`);
|
|
50
|
+
return res.data;
|
|
51
|
+
},
|
|
52
|
+
async count(){
|
|
53
|
+
const res = await axios.get(`${resource}/count/`);
|
|
54
|
+
return res.data;
|
|
55
|
+
},
|
|
56
|
+
async group(field){
|
|
57
|
+
const res = await axios.get(`${resource}/group/${field}`);
|
|
58
|
+
return res.data;
|
|
59
|
+
},
|
|
60
|
+
async getRandomPlaylist(genre, limit){
|
|
61
|
+
const res = await axios.get(`${resource}/random/${genre}/${limit}`);
|
|
62
|
+
return res.data;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const resource = '/doc';
|
|
2
|
+
|
|
3
|
+
export default function createDocs(axios){
|
|
4
|
+
return {
|
|
5
|
+
async getAll(){
|
|
6
|
+
const res = await axios.get(`${resource}`);
|
|
7
|
+
return res.data;
|
|
8
|
+
},
|
|
9
|
+
async get(id){
|
|
10
|
+
if(!id){
|
|
11
|
+
throw new Error('Id argument reguired.');
|
|
12
|
+
}
|
|
13
|
+
const res = await axios.get(`${resource}/${id}`);
|
|
14
|
+
return res.data;
|
|
15
|
+
},
|
|
16
|
+
async create(docObj){
|
|
17
|
+
const res = await axios.post(`${resource}`,docObj);
|
|
18
|
+
return res.data;
|
|
19
|
+
},
|
|
20
|
+
async search(field, query){
|
|
21
|
+
const res = await axios.get(`${resource}/search/${field}/${query}`);
|
|
22
|
+
return res.data;
|
|
23
|
+
},
|
|
24
|
+
async browse(field){
|
|
25
|
+
const res = await axios.get(`${resource}/browse/${field}`);
|
|
26
|
+
return res.data;
|
|
27
|
+
},
|
|
28
|
+
async getRecent(limit){
|
|
29
|
+
const res = await axios.get(`${resource}/recent/${limit}`);
|
|
30
|
+
return res.data;
|
|
31
|
+
},
|
|
32
|
+
async getMyPlaylists(){
|
|
33
|
+
const res = await axios.get(`${resource}/list/`);
|
|
34
|
+
return res.data;
|
|
35
|
+
},
|
|
36
|
+
async getPlaylist(id){
|
|
37
|
+
const res = await axios.get(`${resource}/list/${id}`);
|
|
38
|
+
return res.data;
|
|
39
|
+
},
|
|
40
|
+
async savePlaylist(playlistObj){
|
|
41
|
+
const res = await axios.post(`${resource}/list`,playlistObj);
|
|
42
|
+
return res.data;
|
|
43
|
+
},
|
|
44
|
+
async rate(docId, rating){
|
|
45
|
+
const res = await axios.post(`${resource}/rate/${docId}`,{rating:rating});
|
|
46
|
+
return res.data;
|
|
47
|
+
},
|
|
48
|
+
async getRating(id){
|
|
49
|
+
const res = await axios.get(`${resource}/rate/${id}`);
|
|
50
|
+
return res.data;
|
|
51
|
+
},
|
|
52
|
+
async count(){
|
|
53
|
+
const res = await axios.get(`${resource}/count/`);
|
|
54
|
+
return res.data;
|
|
55
|
+
},
|
|
56
|
+
async group(field){
|
|
57
|
+
const res = await axios.get(`${resource}/group/${field}`);
|
|
58
|
+
return res.data;
|
|
59
|
+
},
|
|
60
|
+
async getRandomPlaylist(genre, limit){
|
|
61
|
+
const res = await axios.get(`${resource}/random/${genre}/${limit}`);
|
|
62
|
+
return res.data;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const resource = '/episode';
|
|
2
|
+
|
|
3
|
+
export default function createEpisodes(axios){
|
|
4
|
+
return {
|
|
5
|
+
async getAll(){
|
|
6
|
+
const res = await axios.get(`${resource}`);
|
|
7
|
+
return res.data;
|
|
8
|
+
},
|
|
9
|
+
async get(id){
|
|
10
|
+
if(!id){
|
|
11
|
+
throw new Error('Id argument reguired.');
|
|
12
|
+
}
|
|
13
|
+
const res = await axios.get(`${resource}/${id}`);
|
|
14
|
+
return res.data;
|
|
15
|
+
},
|
|
16
|
+
async create(episodeObj){
|
|
17
|
+
const res = await axios.post(`${resource}`,episodeObj);
|
|
18
|
+
return res.data;
|
|
19
|
+
},
|
|
20
|
+
async search(field, query){
|
|
21
|
+
const res = await axios.get(`${resource}/search/${field}/${query}`);
|
|
22
|
+
return res.data;
|
|
23
|
+
},
|
|
24
|
+
async browse(field){
|
|
25
|
+
const res = await axios.get(`${resource}/browse/${field}`);
|
|
26
|
+
return res.data;
|
|
27
|
+
},
|
|
28
|
+
async getRecent(limit){
|
|
29
|
+
const res = await axios.get(`${resource}/recent/${limit}`);
|
|
30
|
+
return res.data;
|
|
31
|
+
},
|
|
32
|
+
async getMyPlaylists(){
|
|
33
|
+
const res = await axios.get(`${resource}/list/`);
|
|
34
|
+
return res.data;
|
|
35
|
+
},
|
|
36
|
+
async getPlaylist(id){
|
|
37
|
+
const res = await axios.get(`${resource}/list/${id}`);
|
|
38
|
+
return res.data;
|
|
39
|
+
},
|
|
40
|
+
async savePlaylist(playlistObj){
|
|
41
|
+
const res = await axios.post(`${resource}/list`,playlistObj);
|
|
42
|
+
return res.data;
|
|
43
|
+
},
|
|
44
|
+
async rate(episodeId, rating){
|
|
45
|
+
const res = await axios.post(`${resource}/rate/${episodeId}`,{rating:rating});
|
|
46
|
+
return res.data;
|
|
47
|
+
},
|
|
48
|
+
async getRating(id){
|
|
49
|
+
const res = await axios.get(`${resource}/rate/${id}`);
|
|
50
|
+
return res.data;
|
|
51
|
+
},
|
|
52
|
+
async count(){
|
|
53
|
+
const res = await axios.get(`${resource}/count/`);
|
|
54
|
+
return res.data;
|
|
55
|
+
},
|
|
56
|
+
async group(field){
|
|
57
|
+
const res = await axios.get(`${resource}/group/${field}`);
|
|
58
|
+
return res.data;
|
|
59
|
+
},
|
|
60
|
+
async getRandomPlaylist(genre, limit){
|
|
61
|
+
const res = await axios.get(`${resource}/random/${genre}/${limit}`);
|
|
62
|
+
return res.data;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const resource = '/holdingbay';
|
|
2
|
+
|
|
3
|
+
export default function createHoldingbay(axios){
|
|
4
|
+
return {
|
|
5
|
+
async getMovies(){
|
|
6
|
+
const res = await axios.get(`${resource}/movies`);
|
|
7
|
+
return res.data;
|
|
8
|
+
},
|
|
9
|
+
async getSongs(){
|
|
10
|
+
const res = await axios.get(`${resource}/music`);
|
|
11
|
+
return res.data;
|
|
12
|
+
},
|
|
13
|
+
async getTv(){
|
|
14
|
+
const res = await axios.get(`${resource}/tv`);
|
|
15
|
+
return res.data;
|
|
16
|
+
},
|
|
17
|
+
async getComics(){
|
|
18
|
+
const res = await axios.get(`${resource}/comic`);
|
|
19
|
+
return res.data;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const resource = '/movie';
|
|
2
|
+
|
|
3
|
+
export default function createMovies(axios){
|
|
4
|
+
return {
|
|
5
|
+
async getAll(){
|
|
6
|
+
const res = await axios.get(`${resource}`);
|
|
7
|
+
return res.data;
|
|
8
|
+
},
|
|
9
|
+
async get(id){
|
|
10
|
+
if(!id){
|
|
11
|
+
throw new Error('Id argument reguired.');
|
|
12
|
+
}
|
|
13
|
+
const res = await axios.get(`${resource}/${id}`);
|
|
14
|
+
return res.data;
|
|
15
|
+
},
|
|
16
|
+
async create(movieObj){
|
|
17
|
+
const res = await axios.post(`${resource}`,movieObj);
|
|
18
|
+
return res.data;
|
|
19
|
+
},
|
|
20
|
+
async search(field, query){
|
|
21
|
+
const res = await axios.get(`${resource}/search/${field}/${query}`);
|
|
22
|
+
return res.data;
|
|
23
|
+
},
|
|
24
|
+
async browse(field){
|
|
25
|
+
const res = await axios.get(`${resource}/browse/${field}`);
|
|
26
|
+
return res.data;
|
|
27
|
+
},
|
|
28
|
+
async getRecent(limit){
|
|
29
|
+
const res = await axios.get(`${resource}/recent/${limit}`);
|
|
30
|
+
return res.data;
|
|
31
|
+
},
|
|
32
|
+
async getMyPlaylists(){
|
|
33
|
+
const res = await axios.get(`${resource}/list/`);
|
|
34
|
+
return res.data;
|
|
35
|
+
},
|
|
36
|
+
async getPlaylist(id){
|
|
37
|
+
const res = await axios.get(`${resource}/list/${id}`);
|
|
38
|
+
return res.data;
|
|
39
|
+
},
|
|
40
|
+
async savePlaylist(playlistObj){
|
|
41
|
+
const res = await axios.post(`${resource}/list`,playlistObj);
|
|
42
|
+
return res.data;
|
|
43
|
+
},
|
|
44
|
+
async rate(movieId, rating){
|
|
45
|
+
const res = await axios.post(`${resource}/rate/${movieId}`,{rating:rating});
|
|
46
|
+
return res.data;
|
|
47
|
+
},
|
|
48
|
+
async getRating(id){
|
|
49
|
+
const res = await axios.get(`${resource}/rate/${id}`);
|
|
50
|
+
return res.data;
|
|
51
|
+
},
|
|
52
|
+
async count(){
|
|
53
|
+
const res = await axios.get(`${resource}/count/`);
|
|
54
|
+
return res.data;
|
|
55
|
+
},
|
|
56
|
+
async group(field){
|
|
57
|
+
const res = await axios.get(`${resource}/group/${field}`);
|
|
58
|
+
return res.data;
|
|
59
|
+
},
|
|
60
|
+
async getRandomPlaylist(genre, limit){
|
|
61
|
+
const res = await axios.get(`${resource}/random/${genre}/${limit}`);
|
|
62
|
+
return res.data;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const resource = '/song';
|
|
2
|
+
|
|
3
|
+
export default function createSongs(axios){
|
|
4
|
+
return {
|
|
5
|
+
async getAll(){
|
|
6
|
+
const res = await axios.get(`${resource}`);
|
|
7
|
+
return res.data;
|
|
8
|
+
},
|
|
9
|
+
async get(id){
|
|
10
|
+
if(!id){
|
|
11
|
+
throw new Error('Id argument reguired.');
|
|
12
|
+
}
|
|
13
|
+
const res = await axios.get(`${resource}/${id}`);
|
|
14
|
+
return res.data;
|
|
15
|
+
},
|
|
16
|
+
async create(songObj){
|
|
17
|
+
const res = await axios.post(`${resource}`,songObj);
|
|
18
|
+
return res.data;
|
|
19
|
+
},
|
|
20
|
+
async search(field, query){
|
|
21
|
+
const res = await axios.get(`${resource}/search/${field}/${query}`);
|
|
22
|
+
return res.data;
|
|
23
|
+
},
|
|
24
|
+
async browse(field){
|
|
25
|
+
const res = await axios.get(`${resource}/browse/${field}`);
|
|
26
|
+
return res.data;
|
|
27
|
+
},
|
|
28
|
+
async getRecent(limit){
|
|
29
|
+
const res = await axios.get(`${resource}/recent/${limit}`);
|
|
30
|
+
return res.data;
|
|
31
|
+
},
|
|
32
|
+
async getMyPlaylists(){
|
|
33
|
+
const res = await axios.get(`${resource}/list/`);
|
|
34
|
+
return res.data;
|
|
35
|
+
},
|
|
36
|
+
async getPlaylist(id){
|
|
37
|
+
const res = await axios.get(`${resource}/list/${id}`);
|
|
38
|
+
return res.data;
|
|
39
|
+
},
|
|
40
|
+
async savePlaylist(playlistObj){
|
|
41
|
+
const res = await axios.post(`${resource}/list`,playlistObj);
|
|
42
|
+
return res.data;
|
|
43
|
+
},
|
|
44
|
+
async rate(songId, rating){
|
|
45
|
+
const res = await axios.post(`${resource}/rate/${songId}`,{rating:rating});
|
|
46
|
+
return res.data;
|
|
47
|
+
},
|
|
48
|
+
async getRating(id){
|
|
49
|
+
const res = await axios.get(`${resource}/rate/${id}`);
|
|
50
|
+
return res.data;
|
|
51
|
+
},
|
|
52
|
+
async count(){
|
|
53
|
+
const res = await axios.get(`${resource}/count/`);
|
|
54
|
+
return res.data;
|
|
55
|
+
},
|
|
56
|
+
async group(field){
|
|
57
|
+
const res = await axios.get(`${resource}/group/${field}`);
|
|
58
|
+
return res.data;
|
|
59
|
+
},
|
|
60
|
+
async getRandomPlaylist(genre, limit){
|
|
61
|
+
const res = await axios.get(`${resource}/random/${genre}/${limit}`);
|
|
62
|
+
return res.data;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/singleton.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createApiClient } from './core.js';
|
|
2
|
+
|
|
3
|
+
let instance = null;
|
|
4
|
+
|
|
5
|
+
function init(baseURL){
|
|
6
|
+
if(instance){
|
|
7
|
+
if(instance.axios.defaults.baseURL !== baseURL){
|
|
8
|
+
throw new Error(`API client already initialized with ${instance.axios.defaults.baseURL}`);
|
|
9
|
+
}
|
|
10
|
+
return instance;
|
|
11
|
+
}
|
|
12
|
+
instance = createApiClient(baseURL);
|
|
13
|
+
return instance;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getInstance(){
|
|
17
|
+
if(!instance){
|
|
18
|
+
throw new Error('API client not initialized. Call init(baseURL) first.');
|
|
19
|
+
}
|
|
20
|
+
return instance;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default{
|
|
24
|
+
init,
|
|
25
|
+
get: getInstance
|
|
26
|
+
};
|