@ichinese/open-sdk 1.0.0-rc.2 → 1.0.0-rc.3
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 +65 -1
- package/dist/client.d.ts +75 -2
- package/dist/errors.d.ts +5 -0
- package/dist/gen/meta.d.ts +14 -4
- package/dist/gen/operations.d.ts +73 -3
- package/dist/gen/schema.d.ts +2030 -191
- package/dist/index.cjs +405 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +395 -9
- package/dist/types.d.ts +11 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,6 +28,13 @@ var OpenApiError = class extends Error {
|
|
|
28
28
|
get neverAsk() {
|
|
29
29
|
return this.details.never_ask === true;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Where the person agrees to show their name on posts (409 `consent_required`): send them there, an
|
|
33
|
+
* app cannot agree for them.
|
|
34
|
+
*/
|
|
35
|
+
get consentUrl() {
|
|
36
|
+
return typeof this.details.consent_url === "string" ? this.details.consent_url : null;
|
|
37
|
+
}
|
|
31
38
|
/** A refused, not yet asked or undeclared scope (403): degrade gracefully. */
|
|
32
39
|
get isScopeError() {
|
|
33
40
|
return this.status === 403 && ["scope_denied", "scope_pending", "scope_not_declared", "gold_only"].includes(String(this.reason));
|
|
@@ -36,7 +43,7 @@ var OpenApiError = class extends Error {
|
|
|
36
43
|
var isOpenApiError = (e) => e instanceof OpenApiError;
|
|
37
44
|
|
|
38
45
|
// src/gen/meta.ts
|
|
39
|
-
var VERSION = "1.0.0-rc.
|
|
46
|
+
var VERSION = "1.0.0-rc.3";
|
|
40
47
|
var DEFAULT_BASE_URL = "https://open.ichinese.cn";
|
|
41
48
|
var API_PREFIX = "/open-apis/v1";
|
|
42
49
|
var SCOPES = [
|
|
@@ -63,11 +70,21 @@ var SCOPES = [
|
|
|
63
70
|
"job:candidate:readonly",
|
|
64
71
|
"trial",
|
|
65
72
|
"trade:order:readonly",
|
|
73
|
+
"trade:booking:readonly",
|
|
74
|
+
"venue:readonly",
|
|
75
|
+
"venue",
|
|
76
|
+
"opening:readonly",
|
|
66
77
|
"review",
|
|
67
78
|
"profile:interest:readonly",
|
|
68
79
|
"profile:interest",
|
|
69
80
|
"profile:learning:readonly",
|
|
70
|
-
"report:create"
|
|
81
|
+
"report:create",
|
|
82
|
+
"community:post",
|
|
83
|
+
"me:schedule:readonly",
|
|
84
|
+
"me:learning:readonly",
|
|
85
|
+
"me:words:readonly",
|
|
86
|
+
"me:words",
|
|
87
|
+
"insights:readonly"
|
|
71
88
|
];
|
|
72
89
|
var EVENT_TYPES = [
|
|
73
90
|
"app.installed",
|
|
@@ -81,7 +98,18 @@ var EVENT_TYPES = [
|
|
|
81
98
|
"trade.order_paid",
|
|
82
99
|
"review.created",
|
|
83
100
|
"trial.booked",
|
|
84
|
-
"im.message_received"
|
|
101
|
+
"im.message_received",
|
|
102
|
+
"class.member_changed",
|
|
103
|
+
"course.completed",
|
|
104
|
+
"exam.results_released",
|
|
105
|
+
"event.checked_in",
|
|
106
|
+
"live.recording_ready",
|
|
107
|
+
"trade.order_refunded",
|
|
108
|
+
"trial.confirmed",
|
|
109
|
+
"trial.cancelled",
|
|
110
|
+
"community.post_reviewed",
|
|
111
|
+
"community.video_ready",
|
|
112
|
+
"booking.changed"
|
|
85
113
|
];
|
|
86
114
|
var ERROR_REASONS = [
|
|
87
115
|
"invalid_credentials",
|
|
@@ -102,7 +130,30 @@ var ERROR_REASONS = [
|
|
|
102
130
|
"code_invalid",
|
|
103
131
|
"redirect_uri_mismatch",
|
|
104
132
|
"origin_not_trusted",
|
|
105
|
-
"ip_not_allowed"
|
|
133
|
+
"ip_not_allowed",
|
|
134
|
+
"org_not_installed",
|
|
135
|
+
"minor_not_allowed",
|
|
136
|
+
"birth_year_required",
|
|
137
|
+
"feature_disabled",
|
|
138
|
+
"app_post_stopped",
|
|
139
|
+
"consent_required",
|
|
140
|
+
"video_not_ready",
|
|
141
|
+
"video_failed",
|
|
142
|
+
"post_rate_limited",
|
|
143
|
+
"app_user_daily_limit",
|
|
144
|
+
"post_in_progress",
|
|
145
|
+
"no_org_account",
|
|
146
|
+
"teacher_busy",
|
|
147
|
+
"online_only",
|
|
148
|
+
"venue_unavailable",
|
|
149
|
+
"venue_not_found",
|
|
150
|
+
"slot_overlap",
|
|
151
|
+
"slot_taken",
|
|
152
|
+
"not_draft",
|
|
153
|
+
"not_editable",
|
|
154
|
+
"org_not_verified",
|
|
155
|
+
"opening_ended",
|
|
156
|
+
"opening_incomplete"
|
|
106
157
|
];
|
|
107
158
|
var OPERATION_TOKENS = {
|
|
108
159
|
getUserInfo: ["user"],
|
|
@@ -115,6 +166,8 @@ var OPERATION_TOKENS = {
|
|
|
115
166
|
addClassMember: ["tenant", "user"],
|
|
116
167
|
removeClassMember: ["tenant", "user"],
|
|
117
168
|
listClassSessions: ["tenant", "user"],
|
|
169
|
+
createClassSessions: ["tenant", "user"],
|
|
170
|
+
setClassSessionPlace: ["tenant", "user"],
|
|
118
171
|
cancelClassSession: ["tenant", "user"],
|
|
119
172
|
rescheduleClassSession: ["tenant", "user"],
|
|
120
173
|
getSessionAttendance: ["tenant", "user"],
|
|
@@ -142,16 +195,40 @@ var OPERATION_TOKENS = {
|
|
|
142
195
|
listTrialSlots: ["tenant"],
|
|
143
196
|
createTrialSlots: ["tenant"],
|
|
144
197
|
cancelTrialSlot: ["tenant"],
|
|
198
|
+
setTrialTeacher: ["tenant"],
|
|
145
199
|
listTrialBookings: ["tenant"],
|
|
146
200
|
confirmTrialBooking: ["tenant"],
|
|
201
|
+
listVenues: ["tenant"],
|
|
202
|
+
createVenue: ["tenant"],
|
|
203
|
+
getVenue: ["tenant"],
|
|
204
|
+
listOpenings: ["tenant", "user"],
|
|
205
|
+
getOpening: ["tenant", "user"],
|
|
206
|
+
getInsights: ["tenant"],
|
|
147
207
|
listTradeOrders: ["tenant"],
|
|
208
|
+
listBookings: ["tenant"],
|
|
209
|
+
getBooking: ["tenant"],
|
|
148
210
|
listTenantReviews: ["tenant"],
|
|
149
211
|
replyReview: ["tenant"],
|
|
150
212
|
listPublicContent: ["app", "tenant", "user"],
|
|
151
213
|
getUserInterests: ["user"],
|
|
152
214
|
addInterestSignal: ["user"],
|
|
153
215
|
getUserLearningProfile: ["user"],
|
|
154
|
-
reportOnBehalf: ["user"]
|
|
216
|
+
reportOnBehalf: ["user"],
|
|
217
|
+
createCommunityImageUpload: ["user", "tenant"],
|
|
218
|
+
createCommunityVideoUpload: ["user", "tenant"],
|
|
219
|
+
completeCommunityVideoUpload: ["user", "tenant"],
|
|
220
|
+
getCommunityVideo: ["user", "tenant"],
|
|
221
|
+
listCommunityPosts: ["user", "tenant"],
|
|
222
|
+
createCommunityPost: ["user", "tenant"],
|
|
223
|
+
getCommunityPost: ["user", "tenant"],
|
|
224
|
+
deleteCommunityPost: ["user", "tenant"],
|
|
225
|
+
listCommunityCategories: ["user", "tenant"],
|
|
226
|
+
getMySchedule: ["user"],
|
|
227
|
+
listMyCourses: ["user"],
|
|
228
|
+
listMyWords: ["user"],
|
|
229
|
+
addMyWord: ["user"],
|
|
230
|
+
listClassExams: ["tenant"],
|
|
231
|
+
listClassExamResults: ["tenant"]
|
|
155
232
|
};
|
|
156
233
|
var SCOPE_OPENID = "openid";
|
|
157
234
|
var SCOPE_PROFILE = "profile";
|
|
@@ -176,11 +253,21 @@ var SCOPE_JOB = "job";
|
|
|
176
253
|
var SCOPE_JOB_CANDIDATE_READONLY = "job:candidate:readonly";
|
|
177
254
|
var SCOPE_TRIAL = "trial";
|
|
178
255
|
var SCOPE_TRADE_ORDER_READONLY = "trade:order:readonly";
|
|
256
|
+
var SCOPE_TRADE_BOOKING_READONLY = "trade:booking:readonly";
|
|
257
|
+
var SCOPE_VENUE_READONLY = "venue:readonly";
|
|
258
|
+
var SCOPE_VENUE = "venue";
|
|
259
|
+
var SCOPE_OPENING_READONLY = "opening:readonly";
|
|
179
260
|
var SCOPE_REVIEW = "review";
|
|
180
261
|
var SCOPE_PROFILE_INTEREST_READONLY = "profile:interest:readonly";
|
|
181
262
|
var SCOPE_PROFILE_INTEREST = "profile:interest";
|
|
182
263
|
var SCOPE_PROFILE_LEARNING_READONLY = "profile:learning:readonly";
|
|
183
264
|
var SCOPE_REPORT_CREATE = "report:create";
|
|
265
|
+
var SCOPE_COMMUNITY_POST = "community:post";
|
|
266
|
+
var SCOPE_ME_SCHEDULE_READONLY = "me:schedule:readonly";
|
|
267
|
+
var SCOPE_ME_LEARNING_READONLY = "me:learning:readonly";
|
|
268
|
+
var SCOPE_ME_WORDS_READONLY = "me:words:readonly";
|
|
269
|
+
var SCOPE_ME_WORDS = "me:words";
|
|
270
|
+
var SCOPE_INSIGHTS_READONLY = "insights:readonly";
|
|
184
271
|
|
|
185
272
|
// src/gen/operations.ts
|
|
186
273
|
var Operations = class {
|
|
@@ -240,6 +327,14 @@ var Operations = class {
|
|
|
240
327
|
listClassSessionsAll(classId, query, options) {
|
|
241
328
|
return this.iterate({ id: "listClassSessions", method: "GET", path: "/classes/{class_id}/sessions", pathParams: { class_id: classId }, query, tokenTypes: ["tenant", "user"] }, options);
|
|
242
329
|
}
|
|
330
|
+
/** Schedule a class's sessions (once, or repeating weekly), online or at one of the institution's teaching sites (`POST /classes/{class_id}/sessions`; tenant / user token; scopes: schedule) */
|
|
331
|
+
createClassSessions(classId, body, options) {
|
|
332
|
+
return this.call({ id: "createClassSessions", method: "POST", path: "/classes/{class_id}/sessions", pathParams: { class_id: classId }, body, tokenTypes: ["tenant", "user"] }, options);
|
|
333
|
+
}
|
|
334
|
+
/** Change where one session (or this one and the later ones of its series) is held (`PUT /classes/{class_id}/sessions/{session_id}/place`; tenant / user token; scopes: schedule) */
|
|
335
|
+
setClassSessionPlace(classId, sessionId, body, options) {
|
|
336
|
+
return this.call({ id: "setClassSessionPlace", method: "PUT", path: "/classes/{class_id}/sessions/{session_id}/place", pathParams: { class_id: classId, session_id: sessionId }, body, tokenTypes: ["tenant", "user"] }, options);
|
|
337
|
+
}
|
|
243
338
|
/** Cancel a class session (members are notified, the same as cancelling in the timetable) (`POST /classes/{class_id}/sessions/{session_id}/cancel`; tenant / user token; scopes: schedule) */
|
|
244
339
|
cancelClassSession(classId, sessionId, options) {
|
|
245
340
|
return this.call({ id: "cancelClassSession", method: "POST", path: "/classes/{class_id}/sessions/{session_id}/cancel", pathParams: { class_id: classId, session_id: sessionId }, tokenTypes: ["tenant", "user"] }, options);
|
|
@@ -384,7 +479,7 @@ var Operations = class {
|
|
|
384
479
|
listTrialVenuesAll(query, options) {
|
|
385
480
|
return this.iterate({ id: "listTrialVenues", method: "GET", path: "/trial/venues", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
386
481
|
}
|
|
387
|
-
/** Trial lesson
|
|
482
|
+
/** Trial times (a trial lesson's own times at teaching sites; by default only those not over yet, by time) (`GET /trial/slots`; tenant token; scopes: trial) */
|
|
388
483
|
listTrialSlots(query, options) {
|
|
389
484
|
return this.call({ id: "listTrialSlots", method: "GET", path: "/trial/slots", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
390
485
|
}
|
|
@@ -392,14 +487,18 @@ var Operations = class {
|
|
|
392
487
|
listTrialSlotsAll(query, options) {
|
|
393
488
|
return this.iterate({ id: "listTrialSlots", method: "GET", path: "/trial/slots", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
394
489
|
}
|
|
395
|
-
/**
|
|
490
|
+
/** Open trial times (they can repeat weekly): add them to an existing trial lesson, or open a new trial draft (`POST /trial/slots`; tenant token; scopes: trial) */
|
|
396
491
|
createTrialSlots(body, options) {
|
|
397
492
|
return this.call({ id: "createTrialSlots", method: "POST", path: "/trial/slots", pathParams: {}, body, tokenTypes: ["tenant"] }, options);
|
|
398
493
|
}
|
|
399
|
-
/**
|
|
494
|
+
/** Close a trial time (no more bookings; bookings already made stay as they are, and nobody is notified) (`POST /trial/slots/{slot_id}/cancel`; tenant token; scopes: trial) */
|
|
400
495
|
cancelTrialSlot(slotId, body, options) {
|
|
401
496
|
return this.call({ id: "cancelTrialSlot", method: "POST", path: "/trial/slots/{slot_id}/cancel", pathParams: { slot_id: slotId }, body, tokenTypes: ["tenant"] }, options);
|
|
402
497
|
}
|
|
498
|
+
/** Fill in (or change) the teacher of a trial draft (`PUT /trial/openings/{opening_id}/teacher`; tenant token; scopes: trial) */
|
|
499
|
+
setTrialTeacher(openingId, body, options) {
|
|
500
|
+
return this.call({ id: "setTrialTeacher", method: "PUT", path: "/trial/openings/{opening_id}/teacher", pathParams: { opening_id: openingId }, body, tokenTypes: ["tenant"] }, options);
|
|
501
|
+
}
|
|
403
502
|
/** Trial lesson bookings at the institution's teaching sites (`GET /trial/bookings`; tenant token; scopes: trial) */
|
|
404
503
|
listTrialBookings(query, options) {
|
|
405
504
|
return this.call({ id: "listTrialBookings", method: "GET", path: "/trial/bookings", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
@@ -412,6 +511,38 @@ var Operations = class {
|
|
|
412
511
|
confirmTrialBooking(bookingId, options) {
|
|
413
512
|
return this.call({ id: "confirmTrialBooking", method: "POST", path: "/trial/bookings/{booking_id}/confirm", pathParams: { booking_id: bookingId }, tokenTypes: ["tenant"] }, options);
|
|
414
513
|
}
|
|
514
|
+
/** The institution's teaching sites (campuses), newest first (`GET /venues`; tenant token; scopes: venue:readonly, venue, trial) */
|
|
515
|
+
listVenues(query, options) {
|
|
516
|
+
return this.call({ id: "listVenues", method: "GET", path: "/venues", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
517
|
+
}
|
|
518
|
+
/** Every item of `listVenues`, page after page. */
|
|
519
|
+
listVenuesAll(query, options) {
|
|
520
|
+
return this.iterate({ id: "listVenues", method: "GET", path: "/venues", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
521
|
+
}
|
|
522
|
+
/** Create a draft teaching site (campus); it is published in the platform and reviewed as usual (`POST /venues`; tenant token; scopes: venue) */
|
|
523
|
+
createVenue(body, options) {
|
|
524
|
+
return this.call({ id: "createVenue", method: "POST", path: "/venues", pathParams: {}, body, tokenTypes: ["tenant"] }, options);
|
|
525
|
+
}
|
|
526
|
+
/** One teaching site (of this institution only) (`GET /venues/{venue_id}`; tenant token; scopes: venue:readonly, venue, trial) */
|
|
527
|
+
getVenue(venueId, options) {
|
|
528
|
+
return this.call({ id: "getVenue", method: "GET", path: "/venues/{venue_id}", pathParams: { venue_id: venueId }, tokenTypes: ["tenant"] }, options);
|
|
529
|
+
}
|
|
530
|
+
/** Openings: every class and course opened (group classes, one-to-ones, open classes, trial lessons, video courses), newest first (`GET /openings`; tenant / user token; scopes: opening:readonly) */
|
|
531
|
+
listOpenings(query, options) {
|
|
532
|
+
return this.call({ id: "listOpenings", method: "GET", path: "/openings", pathParams: {}, query, tokenTypes: ["tenant", "user"] }, options);
|
|
533
|
+
}
|
|
534
|
+
/** Every item of `listOpenings`, page after page. */
|
|
535
|
+
listOpeningsAll(query, options) {
|
|
536
|
+
return this.iterate({ id: "listOpenings", method: "GET", path: "/openings", pathParams: {}, query, tokenTypes: ["tenant", "user"] }, options);
|
|
537
|
+
}
|
|
538
|
+
/** One opening (`GET /openings/{opening_id}`; tenant / user token; scopes: opening:readonly) */
|
|
539
|
+
getOpening(openingId, options) {
|
|
540
|
+
return this.call({ id: "getOpening", method: "GET", path: "/openings/{opening_id}", pathParams: { opening_id: openingId }, tokenTypes: ["tenant", "user"] }, options);
|
|
541
|
+
}
|
|
542
|
+
/** One page of the institution's dashboard (read only): enrolment, teaching, teaching sites, income, community and events (`GET /insights`; tenant token; scopes: insights:readonly) */
|
|
543
|
+
getInsights(query, options) {
|
|
544
|
+
return this.call({ id: "getInsights", method: "GET", path: "/insights", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
545
|
+
}
|
|
415
546
|
/** The institution's orders (read-only, for reconciliation). Write operations involving money are not open (`GET /trade/orders`; tenant token; scopes: trade:order:readonly) */
|
|
416
547
|
listTradeOrders(query, options) {
|
|
417
548
|
return this.call({ id: "listTradeOrders", method: "GET", path: "/trade/orders", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
@@ -420,6 +551,18 @@ var Operations = class {
|
|
|
420
551
|
listTradeOrdersAll(query, options) {
|
|
421
552
|
return this.iterate({ id: "listTradeOrders", method: "GET", path: "/trade/orders", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
422
553
|
}
|
|
554
|
+
/** The institution's bookings (one-to-ones and trial lessons, read only for reconciliation), latest lesson first (`GET /bookings`; tenant token; scopes: trade:booking:readonly) */
|
|
555
|
+
listBookings(query, options) {
|
|
556
|
+
return this.call({ id: "listBookings", method: "GET", path: "/bookings", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
557
|
+
}
|
|
558
|
+
/** Every item of `listBookings`, page after page. */
|
|
559
|
+
listBookingsAll(query, options) {
|
|
560
|
+
return this.iterate({ id: "listBookings", method: "GET", path: "/bookings", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
561
|
+
}
|
|
562
|
+
/** One booked lesson (`GET /bookings/{booking_id}`; tenant token; scopes: trade:booking:readonly) */
|
|
563
|
+
getBooking(bookingId, options) {
|
|
564
|
+
return this.call({ id: "getBooking", method: "GET", path: "/bookings/{booking_id}", pathParams: { booking_id: bookingId }, tokenTypes: ["tenant"] }, options);
|
|
565
|
+
}
|
|
423
566
|
/** Reviews of this institution (and its courses and teaching sites) (`GET /reviews`; tenant token; scopes: review) */
|
|
424
567
|
listTenantReviews(query, options) {
|
|
425
568
|
return this.call({ id: "listTenantReviews", method: "GET", path: "/reviews", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
|
|
@@ -456,6 +599,90 @@ var Operations = class {
|
|
|
456
599
|
reportOnBehalf(body, options) {
|
|
457
600
|
return this.call({ id: "reportOnBehalf", method: "POST", path: "/reports", pathParams: {}, body, tokenTypes: ["user"] }, options);
|
|
458
601
|
}
|
|
602
|
+
/** Get an upload URL for an image (it is re-encoded and its EXIF removed when the post is made) (`POST /community/images`; user / tenant token; scopes: community:post) */
|
|
603
|
+
createCommunityImageUpload(body, options) {
|
|
604
|
+
return this.call({ id: "createCommunityImageUpload", method: "POST", path: "/community/images", pathParams: {}, body, tokenTypes: ["user", "tenant"] }, options);
|
|
605
|
+
}
|
|
606
|
+
/** Get an upload URL for a video (up to 4 GB, 1 hour) (`POST /community/videos`; user / tenant token; scopes: community:post) */
|
|
607
|
+
createCommunityVideoUpload(body, options) {
|
|
608
|
+
return this.call({ id: "createCommunityVideoUpload", method: "POST", path: "/community/videos", pathParams: {}, body, tokenTypes: ["user", "tenant"] }, options);
|
|
609
|
+
}
|
|
610
|
+
/** The upload is done; start transcoding (calling it again has no side effects) (`POST /community/videos/{asset_id}/complete`; user / tenant token; scopes: community:post) */
|
|
611
|
+
completeCommunityVideoUpload(assetId, options) {
|
|
612
|
+
return this.call({ id: "completeCommunityVideoUpload", method: "POST", path: "/community/videos/{asset_id}/complete", pathParams: { asset_id: assetId }, tokenTypes: ["user", "tenant"] }, options);
|
|
613
|
+
}
|
|
614
|
+
/** Transcoding status (only for videos this app uploaded for this person) (`GET /community/videos/{asset_id}`; user / tenant token; scopes: community:post) */
|
|
615
|
+
getCommunityVideo(assetId, options) {
|
|
616
|
+
return this.call({ id: "getCommunityVideo", method: "GET", path: "/community/videos/{asset_id}", pathParams: { asset_id: assetId }, tokenTypes: ["user", "tenant"] }, options);
|
|
617
|
+
}
|
|
618
|
+
/** Posts this app made in this person's name (the person's own posts and other apps' posts are not visible) (`GET /community/posts`; user / tenant token; scopes: community:post) */
|
|
619
|
+
listCommunityPosts(query, options) {
|
|
620
|
+
return this.call({ id: "listCommunityPosts", method: "GET", path: "/community/posts", pathParams: {}, query, tokenTypes: ["user", "tenant"] }, options);
|
|
621
|
+
}
|
|
622
|
+
/** Every item of `listCommunityPosts`, page after page. */
|
|
623
|
+
listCommunityPostsAll(query, options) {
|
|
624
|
+
return this.iterate({ id: "listCommunityPosts", method: "GET", path: "/community/posts", pathParams: {}, query, tokenTypes: ["user", "tenant"] }, options);
|
|
625
|
+
}
|
|
626
|
+
/** Publish a public post in this person's name (text, up to 9 images or 1 video, up to 10 topics) (`POST /community/posts`; user / tenant token; scopes: community:post) */
|
|
627
|
+
createCommunityPost(body, options) {
|
|
628
|
+
return this.call({ id: "createCommunityPost", method: "POST", path: "/community/posts", pathParams: {}, body, tokenTypes: ["user", "tenant"] }, options);
|
|
629
|
+
}
|
|
630
|
+
/** The status of one post (only posts this app made; anything else is 404) (`GET /community/posts/{post_id}`; user / tenant token; scopes: community:post) */
|
|
631
|
+
getCommunityPost(postId, options) {
|
|
632
|
+
return this.call({ id: "getCommunityPost", method: "GET", path: "/community/posts/{post_id}", pathParams: { post_id: postId }, tokenTypes: ["user", "tenant"] }, options);
|
|
633
|
+
}
|
|
634
|
+
/** Delete a post (only posts this app made; anything else is 404) (`DELETE /community/posts/{post_id}`; user / tenant token; scopes: community:post) */
|
|
635
|
+
deleteCommunityPost(postId, options) {
|
|
636
|
+
return this.call({ id: "deleteCommunityPost", method: "DELETE", path: "/community/posts/{post_id}", pathParams: { post_id: postId }, tokenTypes: ["user", "tenant"] }, options);
|
|
637
|
+
}
|
|
638
|
+
/** Categories a post can be filed under (those enabled in this region) (`GET /community/categories`; user / tenant token; scopes: community:post) */
|
|
639
|
+
listCommunityCategories(options) {
|
|
640
|
+
return this.call({ id: "listCommunityCategories", method: "GET", path: "/community/categories", pathParams: {}, tokenTypes: ["user", "tenant"] }, options);
|
|
641
|
+
}
|
|
642
|
+
/** This person's timetable: their sessions, the events they signed up for, confirmed trial lessons and exams (for calendar apps; read-only) (`GET /me/schedule`; user token; scopes: me:schedule:readonly) */
|
|
643
|
+
getMySchedule(query, options) {
|
|
644
|
+
return this.call({ id: "getMySchedule", method: "GET", path: "/me/schedule", pathParams: {}, query, tokenTypes: ["user"] }, options);
|
|
645
|
+
}
|
|
646
|
+
/** Every item of `getMySchedule`, page after page. */
|
|
647
|
+
getMyScheduleAll(query, options) {
|
|
648
|
+
return this.iterate({ id: "getMySchedule", method: "GET", path: "/me/schedule", pathParams: {}, query, tokenTypes: ["user"] }, options);
|
|
649
|
+
}
|
|
650
|
+
/** The courses this person is studying and their progress (lessons completed, last studied; level and goals belong to the gold-only profile and are not here) (`GET /me/courses`; user token; scopes: me:learning:readonly) */
|
|
651
|
+
listMyCourses(query, options) {
|
|
652
|
+
return this.call({ id: "listMyCourses", method: "GET", path: "/me/courses", pathParams: {}, query, tokenTypes: ["user"] }, options);
|
|
653
|
+
}
|
|
654
|
+
/** Every item of `listMyCourses`, page after page. */
|
|
655
|
+
listMyCoursesAll(query, options) {
|
|
656
|
+
return this.iterate({ id: "listMyCourses", method: "GET", path: "/me/courses", pathParams: {}, query, tokenTypes: ["user"] }, options);
|
|
657
|
+
}
|
|
658
|
+
/** This person's word bank: words, pinyin, review state (no meanings, and review records cannot be changed) (`GET /me/words`; user token; scopes: me:words:readonly, me:words) */
|
|
659
|
+
listMyWords(query, options) {
|
|
660
|
+
return this.call({ id: "listMyWords", method: "GET", path: "/me/words", pathParams: {}, query, tokenTypes: ["user"] }, options);
|
|
661
|
+
}
|
|
662
|
+
/** Every item of `listMyWords`, page after page. */
|
|
663
|
+
listMyWordsAll(query, options) {
|
|
664
|
+
return this.iterate({ id: "listMyWords", method: "GET", path: "/me/words", pathParams: {}, query, tokenTypes: ["user"] }, options);
|
|
665
|
+
}
|
|
666
|
+
/** Add a word to this person's word bank (unchanged if it is already there; its source is recorded as this app) (`POST /me/words`; user token; scopes: me:words) */
|
|
667
|
+
addMyWord(body, options) {
|
|
668
|
+
return this.call({ id: "addMyWord", method: "POST", path: "/me/words", pathParams: {}, body, tokenTypes: ["user"] }, options);
|
|
669
|
+
}
|
|
670
|
+
/** A class's exams (tenant tokens only, own institution) (`GET /classes/{class_id}/exams`; tenant token; scopes: grade:readonly) */
|
|
671
|
+
listClassExams(classId, query, options) {
|
|
672
|
+
return this.call({ id: "listClassExams", method: "GET", path: "/classes/{class_id}/exams", pathParams: { class_id: classId }, query, tokenTypes: ["tenant"] }, options);
|
|
673
|
+
}
|
|
674
|
+
/** Every item of `listClassExams`, page after page. */
|
|
675
|
+
listClassExamsAll(classId, query, options) {
|
|
676
|
+
return this.iterate({ id: "listClassExams", method: "GET", path: "/classes/{class_id}/exams", pathParams: { class_id: classId }, query, tokenTypes: ["tenant"] }, options);
|
|
677
|
+
}
|
|
678
|
+
/** Each learner's result in one exam (scores only once released; tenant tokens only, own institution) (`GET /classes/{class_id}/exams/{exam_id}/results`; tenant token; scopes: grade:readonly) */
|
|
679
|
+
listClassExamResults(classId, examId, query, options) {
|
|
680
|
+
return this.call({ id: "listClassExamResults", method: "GET", path: "/classes/{class_id}/exams/{exam_id}/results", pathParams: { class_id: classId, exam_id: examId }, query, tokenTypes: ["tenant"] }, options);
|
|
681
|
+
}
|
|
682
|
+
/** Every item of `listClassExamResults`, page after page. */
|
|
683
|
+
listClassExamResultsAll(classId, examId, query, options) {
|
|
684
|
+
return this.iterate({ id: "listClassExamResults", method: "GET", path: "/classes/{class_id}/exams/{exam_id}/results", pathParams: { class_id: classId, exam_id: examId }, query, tokenTypes: ["tenant"] }, options);
|
|
685
|
+
}
|
|
459
686
|
};
|
|
460
687
|
|
|
461
688
|
// src/client.ts
|
|
@@ -537,6 +764,129 @@ var Client = class extends Operations {
|
|
|
537
764
|
clearTokenCache() {
|
|
538
765
|
this.tokens.clear();
|
|
539
766
|
}
|
|
767
|
+
// ── posting in a person's name (D105), and as the institution (M6.21 K2) ─────
|
|
768
|
+
/**
|
|
769
|
+
* Uploads an image for a post: asks for an upload slot, PUTs the bytes there and returns the `key` to
|
|
770
|
+
* put in `images[].key`. Up to 10 MB; jpeg, png, webp or gif.
|
|
771
|
+
*/
|
|
772
|
+
uploadImage(userAccessToken, data, contentType, options = {}) {
|
|
773
|
+
return this.uploadImageWith({ userAccessToken, signal: options.signal }, data, contentType);
|
|
774
|
+
}
|
|
775
|
+
/** `uploadImage` for a post of the institution (its tenant token, `community:post` granted by it). */
|
|
776
|
+
uploadImageAsOrg(tenantKey, data, contentType, options = {}) {
|
|
777
|
+
return this.uploadImageWith({ tenantKey, signal: options.signal }, data, contentType);
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Uploads a video for a post (up to 4 GB, 1 hour): asks for an upload slot, PUTs the file there and
|
|
781
|
+
* starts transcoding. A large file goes as a Blob that reads from disk (`await fs.openAsBlob(path)`).
|
|
782
|
+
* Returns the video's status (`asset_id`); wait for `ready` with `waitVideoReady` before posting.
|
|
783
|
+
*/
|
|
784
|
+
uploadVideo(userAccessToken, data, file, options = {}) {
|
|
785
|
+
return this.uploadVideoWith({ userAccessToken, signal: options.signal }, data, file);
|
|
786
|
+
}
|
|
787
|
+
/** `uploadVideo` for a post of the institution. */
|
|
788
|
+
uploadVideoAsOrg(tenantKey, data, file, options = {}) {
|
|
789
|
+
return this.uploadVideoWith({ tenantKey, signal: options.signal }, data, file);
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Waits until a video has been transcoded and returns it (`status: ready`). A failed transcode throws
|
|
793
|
+
* an OpenApiError with reason `video_failed` (upload another file); running out of time throws one
|
|
794
|
+
* with code TIMEOUT (reason `video_not_ready`). You also get the `community.video_ready` event.
|
|
795
|
+
*/
|
|
796
|
+
waitVideoReady(userAccessToken, assetId, options = {}) {
|
|
797
|
+
return this.waitVideoWith({ userAccessToken }, assetId, options);
|
|
798
|
+
}
|
|
799
|
+
/** `waitVideoReady` for a video uploaded for the institution. */
|
|
800
|
+
waitVideoReadyAsOrg(tenantKey, assetId, options = {}) {
|
|
801
|
+
return this.waitVideoWith({ tenantKey }, assetId, options);
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Posts in the person's name. Without a `uuid` the SDK makes one, so its own retries never post twice;
|
|
805
|
+
* give your own `uuid` to make your retries safe too (one post per `uuid` within an hour). Errors keep the server's reason: `consent_required`
|
|
806
|
+
* (send the person to `error.consentUrl`), `video_not_ready`, `video_failed`, `post_rate_limited`,
|
|
807
|
+
* `app_user_daily_limit`, `daily_quota_exceeded`, `minor_not_allowed`, `app_post_stopped`, …
|
|
808
|
+
*/
|
|
809
|
+
createPost(userAccessToken, post, options = {}) {
|
|
810
|
+
return this.createPostWith({ userAccessToken, signal: options.signal }, post);
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Posts as the institution, with its community account (its name and logo; the post says it came from
|
|
814
|
+
* this app). The institution must have installed the app and granted `community:post`; before its
|
|
815
|
+
* homepage was first published there is no account yet (409 `no_org_account`). The same limits,
|
|
816
|
+
* review and `uuid` handling as `createPost`.
|
|
817
|
+
*/
|
|
818
|
+
createPostAsOrg(tenantKey, post, options = {}) {
|
|
819
|
+
return this.createPostWith({ tenantKey, signal: options.signal }, post);
|
|
820
|
+
}
|
|
821
|
+
// ── the institution's dashboard (M6.21 K2) ────────────────────────────────
|
|
822
|
+
/**
|
|
823
|
+
* Every page of the institution's dashboard the API offers (`GET /insights`, `insights:readonly`): the
|
|
824
|
+
* default page first, then each other tab it lists, with the same query (range, dates, filters). Returns
|
|
825
|
+
* tab → page. Fewer than five people come back as `null` with `withheld: true`; money is per currency.
|
|
826
|
+
*/
|
|
827
|
+
async getAllInsights(tenantKey, query = {}, options = {}) {
|
|
828
|
+
const who = { tenantKey, signal: options.signal };
|
|
829
|
+
const first = await this.getInsights(query, who);
|
|
830
|
+
const out = { [first.tab]: first };
|
|
831
|
+
for (const tab of first.tabs) {
|
|
832
|
+
if (out[tab]) continue;
|
|
833
|
+
out[tab] = await this.getInsights({ ...query, tab }, who);
|
|
834
|
+
}
|
|
835
|
+
return out;
|
|
836
|
+
}
|
|
837
|
+
async uploadImageWith(who, data, contentType) {
|
|
838
|
+
const slot = await this.createCommunityImageUpload({ content_type: contentType, size_bytes: sizeOf(data) }, who);
|
|
839
|
+
await this.putUpload(slot.upload_url, slot.headers, data, who.signal);
|
|
840
|
+
return slot.key;
|
|
841
|
+
}
|
|
842
|
+
async uploadVideoWith(who, data, file) {
|
|
843
|
+
const slot = await this.createCommunityVideoUpload(
|
|
844
|
+
{ filename: file.filename, content_type: file.contentType, size_bytes: sizeOf(data) },
|
|
845
|
+
who
|
|
846
|
+
);
|
|
847
|
+
await this.putUpload(slot.upload.url, slot.upload.headers, data, who.signal);
|
|
848
|
+
return this.send(
|
|
849
|
+
"POST",
|
|
850
|
+
`/community/videos/${encodeURIComponent(slot.asset_id)}/complete`,
|
|
851
|
+
{ auth: this.postingAuth("completeCommunityVideoUpload", who), retryPost: true }
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
async waitVideoWith(who, assetId, options) {
|
|
855
|
+
const deadline = Date.now() + (options.timeoutMs ?? 30 * 6e4);
|
|
856
|
+
let interval = options.intervalMs ?? 2e3;
|
|
857
|
+
const maxInterval = options.maxIntervalMs ?? 15e3;
|
|
858
|
+
for (; ; ) {
|
|
859
|
+
const video = await this.getCommunityVideo(assetId, { ...who, signal: options.signal });
|
|
860
|
+
if (video.status === "ready") return video;
|
|
861
|
+
if (video.status === "failed") {
|
|
862
|
+
throw new OpenApiError({
|
|
863
|
+
status: 409,
|
|
864
|
+
code: "CONFLICT",
|
|
865
|
+
message: `video ${assetId} could not be transcoded${video.error ? ` (${video.error})` : ""}`,
|
|
866
|
+
details: { reason: "video_failed", asset_id: assetId, error: video.error ?? null }
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
const left = deadline - Date.now();
|
|
870
|
+
if (left <= 0) {
|
|
871
|
+
throw new OpenApiError({
|
|
872
|
+
status: 0,
|
|
873
|
+
code: "TIMEOUT",
|
|
874
|
+
message: `video ${assetId} is not ready yet (${video.status})`,
|
|
875
|
+
details: { reason: "video_not_ready", asset_id: assetId, status: video.status }
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
await this.sleep(Math.min(interval, left), options.signal);
|
|
879
|
+
interval = Math.min(interval * 2, maxInterval);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
createPostWith(who, post) {
|
|
883
|
+
const body = post.uuid ? post : { ...post, uuid: crypto.randomUUID() };
|
|
884
|
+
return this.send("POST", "/community/posts", {
|
|
885
|
+
body,
|
|
886
|
+
auth: this.postingAuth("createCommunityPost", who),
|
|
887
|
+
retryPost: true
|
|
888
|
+
});
|
|
889
|
+
}
|
|
540
890
|
// ── calls ─────────────────────────────────────────────────────────────────
|
|
541
891
|
/** Any open API call by method and path (`/tenant/members`), for what the generated methods do not cover. */
|
|
542
892
|
request(method, path, init = {}, options) {
|
|
@@ -578,6 +928,31 @@ var Client = class extends Operations {
|
|
|
578
928
|
credentials() {
|
|
579
929
|
return { app_id: this.appId, app_secret: this.appSecret };
|
|
580
930
|
}
|
|
931
|
+
postingAuth(id, who) {
|
|
932
|
+
const spec = { id, method: "POST", path: "", pathParams: {}, tokenTypes: ["user", "tenant"] };
|
|
933
|
+
return { spec, options: who };
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* PUTs an upload to the slot the platform gave (object storage, not the open API: no token, its own
|
|
937
|
+
* headers). Not limited by `timeoutMs`, a large video takes as long as it takes; cancel with the signal.
|
|
938
|
+
*/
|
|
939
|
+
async putUpload(url, headers, data, signal) {
|
|
940
|
+
let res;
|
|
941
|
+
try {
|
|
942
|
+
res = await this.fetchFn(url, { method: "PUT", headers, body: data, signal });
|
|
943
|
+
} catch (err) {
|
|
944
|
+
if (signal?.aborted) throw err;
|
|
945
|
+
throw new OpenApiError({ status: 0, code: "NETWORK", message: `PUT upload: ${err.message}` });
|
|
946
|
+
}
|
|
947
|
+
if (!res.ok) {
|
|
948
|
+
throw new OpenApiError({
|
|
949
|
+
status: res.status,
|
|
950
|
+
code: "UPLOAD_FAILED",
|
|
951
|
+
message: `PUT upload: ${res.status} ${res.statusText}`,
|
|
952
|
+
details: { reason: "upload_failed", body: (await res.text().catch(() => "")).slice(0, 500) }
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
}
|
|
581
956
|
async cached(key, fetchToken) {
|
|
582
957
|
const hit = this.tokens.get(key);
|
|
583
958
|
if (hit && hit.expiresAt - this.refreshBefore * 1e3 > Date.now()) return hit;
|
|
@@ -648,7 +1023,7 @@ var Client = class extends Operations {
|
|
|
648
1023
|
const timeout = AbortSignal.timeout(this.timeoutMs);
|
|
649
1024
|
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
650
1025
|
const canRetry = attempt < this.maxRetries;
|
|
651
|
-
const idempotent = IDEMPOTENT.has(method) || method === "POST" && init.retryPost !== false && !init.auth;
|
|
1026
|
+
const idempotent = IDEMPOTENT.has(method) || method === "POST" && (init.retryPost === true || init.retryPost !== false && !init.auth);
|
|
652
1027
|
let res;
|
|
653
1028
|
try {
|
|
654
1029
|
res = await this.fetchFn(this.url(path, init.query), {
|
|
@@ -698,6 +1073,7 @@ var Client = class extends Operations {
|
|
|
698
1073
|
}
|
|
699
1074
|
}
|
|
700
1075
|
};
|
|
1076
|
+
var sizeOf = (data) => data instanceof Uint8Array ? data.byteLength : data.size;
|
|
701
1077
|
async function toError(res) {
|
|
702
1078
|
let body = {};
|
|
703
1079
|
try {
|
|
@@ -783,6 +1159,7 @@ export {
|
|
|
783
1159
|
SCOPE_ATTENDANCE_READONLY,
|
|
784
1160
|
SCOPE_CLASS_MEMBER,
|
|
785
1161
|
SCOPE_CLASS_READONLY,
|
|
1162
|
+
SCOPE_COMMUNITY_POST,
|
|
786
1163
|
SCOPE_COURSE,
|
|
787
1164
|
SCOPE_COURSEWORK,
|
|
788
1165
|
SCOPE_EVENT,
|
|
@@ -790,12 +1167,18 @@ export {
|
|
|
790
1167
|
SCOPE_GRADE_READONLY,
|
|
791
1168
|
SCOPE_IM_CHAT_READONLY,
|
|
792
1169
|
SCOPE_IM_MESSAGE_SEND_AS_BOT,
|
|
1170
|
+
SCOPE_INSIGHTS_READONLY,
|
|
793
1171
|
SCOPE_JOB,
|
|
794
1172
|
SCOPE_JOB_CANDIDATE_READONLY,
|
|
795
1173
|
SCOPE_LEARNING_PROGRESS_READONLY,
|
|
796
1174
|
SCOPE_LIVE_RECORDING_READONLY,
|
|
1175
|
+
SCOPE_ME_LEARNING_READONLY,
|
|
1176
|
+
SCOPE_ME_SCHEDULE_READONLY,
|
|
1177
|
+
SCOPE_ME_WORDS,
|
|
1178
|
+
SCOPE_ME_WORDS_READONLY,
|
|
797
1179
|
SCOPE_NOTIFY_SEND,
|
|
798
1180
|
SCOPE_OPENID,
|
|
1181
|
+
SCOPE_OPENING_READONLY,
|
|
799
1182
|
SCOPE_ORG_MEMBER,
|
|
800
1183
|
SCOPE_ORG_MEMBER_READONLY,
|
|
801
1184
|
SCOPE_PROFILE,
|
|
@@ -806,8 +1189,11 @@ export {
|
|
|
806
1189
|
SCOPE_REVIEW,
|
|
807
1190
|
SCOPE_SCHEDULE,
|
|
808
1191
|
SCOPE_SCHEDULE_READONLY,
|
|
1192
|
+
SCOPE_TRADE_BOOKING_READONLY,
|
|
809
1193
|
SCOPE_TRADE_ORDER_READONLY,
|
|
810
1194
|
SCOPE_TRIAL,
|
|
1195
|
+
SCOPE_VENUE,
|
|
1196
|
+
SCOPE_VENUE_READONLY,
|
|
811
1197
|
VERSION,
|
|
812
1198
|
challengeResponse,
|
|
813
1199
|
decrypt,
|
package/dist/types.d.ts
CHANGED
|
@@ -42,6 +42,17 @@ export interface ClientOptions {
|
|
|
42
42
|
/** Appended to the User-Agent header. */
|
|
43
43
|
userAgent?: string;
|
|
44
44
|
}
|
|
45
|
+
/** How `waitVideoReady` polls a video's status. */
|
|
46
|
+
export interface WaitVideoOptions {
|
|
47
|
+
/** Give up after this many milliseconds (an OpenApiError, code TIMEOUT). Default 30 minutes. */
|
|
48
|
+
timeoutMs?: number;
|
|
49
|
+
/** First wait between two looks, in milliseconds; it doubles up to `maxIntervalMs`. Default 2 000. */
|
|
50
|
+
intervalMs?: number;
|
|
51
|
+
/** Longest wait between two looks. Default 15 000. */
|
|
52
|
+
maxIntervalMs?: number;
|
|
53
|
+
/** Stops waiting. */
|
|
54
|
+
signal?: AbortSignal;
|
|
55
|
+
}
|
|
45
56
|
export interface UserAccessToken {
|
|
46
57
|
access_token: string;
|
|
47
58
|
token_type: 'Bearer';
|
package/package.json
CHANGED