@insignia-education/api-sdk-js 0.9.34 → 0.12.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.
Files changed (49) hide show
  1. package/.env.test +1 -1
  2. package/AGENTS.md +15 -0
  3. package/coverage/clover.xml +229 -135
  4. package/coverage/coverage-final.json +7 -7
  5. package/coverage/lcov-report/index.html +27 -27
  6. package/coverage/lcov-report/src/Client.js.html +163 -67
  7. package/coverage/lcov-report/src/api/index.html +1 -1
  8. package/coverage/lcov-report/src/api/index.js.html +1 -1
  9. package/coverage/lcov-report/src/api/v1/Accounts.js.html +1 -1
  10. package/coverage/lcov-report/src/api/v1/Auth.js.html +27 -6
  11. package/coverage/lcov-report/src/api/v1/Categories.js.html +1 -1
  12. package/coverage/lcov-report/src/api/v1/Changelogs.js.html +1 -1
  13. package/coverage/lcov-report/src/api/v1/Configs.js.html +1 -1
  14. package/coverage/lcov-report/src/api/v1/ContactForms.js.html +1 -1
  15. package/coverage/lcov-report/src/api/v1/ConversationalTopics.js.html +1 -1
  16. package/coverage/lcov-report/src/api/v1/Countries.js.html +1 -1
  17. package/coverage/lcov-report/src/api/v1/Coupons.js.html +1 -1
  18. package/coverage/lcov-report/src/api/v1/Courses.js.html +23 -8
  19. package/coverage/lcov-report/src/api/v1/Currencies.js.html +1 -1
  20. package/coverage/lcov-report/src/api/v1/Files.js.html +55 -10
  21. package/coverage/lcov-report/src/api/v1/Forums.js.html +1 -1
  22. package/coverage/lcov-report/src/api/v1/Hashes.js.html +1 -1
  23. package/coverage/lcov-report/src/api/v1/Insignias.js.html +1 -1
  24. package/coverage/lcov-report/src/api/v1/Languages.js.html +1 -1
  25. package/coverage/lcov-report/src/api/v1/MailBlacklist.js.html +1 -1
  26. package/coverage/lcov-report/src/api/v1/Organizations.js.html +1 -1
  27. package/coverage/lcov-report/src/api/v1/PaymentMethods.js.html +1 -1
  28. package/coverage/lcov-report/src/api/v1/Quizzes.js.html +31 -31
  29. package/coverage/lcov-report/src/api/v1/ShortLinks.js.html +1 -1
  30. package/coverage/lcov-report/src/api/v1/Surveys.js.html +1 -1
  31. package/coverage/lcov-report/src/api/v1/Taxes.js.html +1 -1
  32. package/coverage/lcov-report/src/api/v1/Teacher.js.html +1 -1
  33. package/coverage/lcov-report/src/api/v1/UserTypes.js.html +1 -1
  34. package/coverage/lcov-report/src/api/v1/Users.js.html +116 -11
  35. package/coverage/lcov-report/src/api/v1/Zoom.js.html +1 -1
  36. package/coverage/lcov-report/src/api/v1/index.html +45 -45
  37. package/coverage/lcov-report/src/api/v1/index.js.html +1 -1
  38. package/coverage/lcov-report/src/index.html +17 -17
  39. package/coverage/lcov-report/src/index.js.html +1 -1
  40. package/coverage/lcov-report/tests/helpers.js.html +1 -1
  41. package/coverage/lcov-report/tests/index.html +1 -1
  42. package/coverage/lcov.info +376 -221
  43. package/coverage/test-report.html +1 -704
  44. package/package.json +2 -3
  45. package/src/Client.js +11 -3
  46. package/src/api/v1/Courses.js +3 -0
  47. package/src/api/v1/Files.js +2 -2
  48. package/src/api/v1/Users.js +4 -0
  49. package/tests/client.test.js +12 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@insignia-education/api-sdk-js",
3
- "version": "0.9.34",
3
+ "version": "0.12.0",
4
4
  "description": "JavaScript SDK for the Insignia Education API",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -26,8 +26,7 @@
26
26
  "url": "https://github.com/insignia-education/api-sdk-js/issues"
27
27
  },
28
28
  "homepage": "https://github.com/insignia-education/api-sdk-js#readme",
29
- "dependencies": {
30
- },
29
+ "dependencies": {},
31
30
  "devDependencies": {
32
31
  "@babel/preset-env": "^7.26.0",
33
32
  "babel-jest": "^30.3.0",
package/src/Client.js CHANGED
@@ -79,7 +79,7 @@ export default class InsigniaClient {
79
79
  if (!response.ok) {
80
80
  const err = new Error(`HTTP ${response.status}`);
81
81
  err.status = response.status;
82
- try { err.data = await this.#parseResponse(response); } catch (_) {}
82
+ try { err.data = await this.#parseResponse(response); } catch { /* body wasn't parseable — leave err.data unset */ }
83
83
  throw err;
84
84
  }
85
85
  const data = await this.#parseResponse(response);
@@ -97,14 +97,22 @@ export default class InsigniaClient {
97
97
  if (!response.ok) {
98
98
  const err = new Error(`HTTP ${response.status}`);
99
99
  err.status = response.status;
100
- try { err.data = await this.#parseResponse(response); } catch (_) {}
100
+ try { err.data = await this.#parseResponse(response); } catch { /* body wasn't parseable — leave err.data unset */ }
101
101
  throw err;
102
102
  }
103
103
  const data = await this.#parseResponse(response);
104
104
  return data?.success ? data.response : data;
105
105
  }
106
106
 
107
- get(path) { return this.#request('GET', path); }
107
+ get(path, params = null) {
108
+ if (params && typeof params === 'object') {
109
+ const qs = new URLSearchParams(
110
+ Object.entries(params).filter(([, v]) => v !== undefined && v !== null)
111
+ ).toString();
112
+ if (qs) path += (path.includes('?') ? '&' : '?') + qs;
113
+ }
114
+ return this.#request('GET', path);
115
+ }
108
116
  post(path, body = null) { return this.#request('POST', path, body); }
109
117
  put(path, body = null) { return this.#request('PUT', path, body); }
110
118
  patch(path, body = null){ return this.#request('PATCH', path, body); }
@@ -24,6 +24,9 @@ export default class Courses {
24
24
  edit(id, data) { return this.#client.patch(`/courses/${id}`, data); }
25
25
  delete(id) { return this.#client.del(`/courses/${id}`); }
26
26
 
27
+ /** Users enrolled on this course as a teacher (user_courses.teacher = 1). */
28
+ teachers(courseId) { return this.#client.get(`/courses/${courseId}/teachers`); }
29
+
27
30
  dates(courseId) {
28
31
  const base = `/courses/${courseId}/dates`;
29
32
  return {
@@ -14,8 +14,8 @@ export default class Files {
14
14
  /** Upload a file (FormData). Supports: file, folder, organization_id, filename, generate_webp, … */
15
15
  upload(formData) { return this.#client.upload('/files', formData); }
16
16
 
17
- /** Create a logical folder. body: { organization_id, path } */
18
- createFolder(body) { return this.#client.upload('/files/folder', body); }
17
+ /** Create a logical folder (JSON PUT, no file involved). body: { path } */
18
+ createFolder(body) { return this.#client.put('/files/folder', body); }
19
19
 
20
20
  /** Update file description. body: { description } */
21
21
  update(id, body) { return this.#client.patch(`/files/${id}`, body); }
@@ -9,6 +9,10 @@ export default class Users {
9
9
  getByUsername(username) { return this.#client.get(`/users/username/${username}`); }
10
10
  findByUsername(username) { return this.#client.get(`/users/find/${username}`); }
11
11
  cashReceivers() { return this.#client.get('/users/cash-receivers'); }
12
+ /** Employee-only: users assignable as a course owner (super-admins, education centers, organizations). */
13
+ assignable() { return this.#client.get('/users/assignable'); }
14
+ /** Employee-only: users assignable as a course teacher (super-admins, admins, employees, teachers). */
15
+ assignableTeachers() { return this.#client.get('/users/assignable-teachers'); }
12
16
  edit(id, data) { return this.#client.patch(`/users/${id}`, data); }
13
17
 
14
18
  #nested(userId, path) {
@@ -3,7 +3,7 @@ import InsigniaClient from '../src/Client.js';
3
3
  const BASE = 'http://localhost:8000';
4
4
 
5
5
  function mockFetch(json = {}) {
6
- return jest.fn().mockResolvedValue({ json: () => Promise.resolve(json) });
6
+ return jest.fn().mockResolvedValue({ ok: true, status: 200, json: () => Promise.resolve(json) });
7
7
  }
8
8
 
9
9
  beforeEach(() => {
@@ -100,7 +100,7 @@ describe('HTTP methods', () => {
100
100
  let client;
101
101
 
102
102
  beforeEach(() => {
103
- global.fetch = mockFetch({ ok: true });
103
+ global.fetch = mockFetch();
104
104
  client = new InsigniaClient(BASE);
105
105
  });
106
106
 
@@ -181,12 +181,16 @@ describe('HTTP methods', () => {
181
181
  test('stores response cookies and sends them on later requests in Node', async () => {
182
182
  global.fetch = jest.fn()
183
183
  .mockResolvedValueOnce({
184
+ ok: true,
185
+ status: 200,
184
186
  headers: {
185
187
  getSetCookie: () => ['token=abc123; Path=/; HttpOnly; Secure; SameSite=Lax'],
186
188
  },
187
189
  json: () => Promise.resolve({ success: 'ok' }),
188
190
  })
189
191
  .mockResolvedValueOnce({
192
+ ok: true,
193
+ status: 200,
190
194
  json: () => Promise.resolve({ id: 1, title: 'test' }),
191
195
  });
192
196
  client = new InsigniaClient(BASE);
@@ -200,6 +204,8 @@ describe('HTTP methods', () => {
200
204
 
201
205
  test('returns parsed JSON from response', async () => {
202
206
  global.fetch = jest.fn().mockResolvedValue({
207
+ ok: true,
208
+ status: 200,
203
209
  json: () => Promise.resolve({ id: 1, name: 'test' }),
204
210
  });
205
211
  client = new InsigniaClient(BASE);
@@ -209,6 +215,7 @@ describe('HTTP methods', () => {
209
215
 
210
216
  test('returns null for empty response text', async () => {
211
217
  global.fetch = jest.fn().mockResolvedValue({
218
+ ok: true,
212
219
  status: 200,
213
220
  text: () => Promise.resolve(''),
214
221
  });
@@ -219,6 +226,7 @@ describe('HTTP methods', () => {
219
226
 
220
227
  test('returns null for no-content responses', async () => {
221
228
  global.fetch = jest.fn().mockResolvedValue({
229
+ ok: true,
222
230
  status: 204,
223
231
  json: jest.fn(),
224
232
  });
@@ -229,6 +237,8 @@ describe('HTTP methods', () => {
229
237
 
230
238
  test('returns null when json parsing fails because the response is empty', async () => {
231
239
  global.fetch = jest.fn().mockResolvedValue({
240
+ ok: true,
241
+ status: 200,
232
242
  json: () => Promise.reject(new SyntaxError('Unexpected end of JSON input')),
233
243
  });
234
244
  client = new InsigniaClient(BASE);